Improve biometric lock, new comment UI, create issue/pr from a note (#1356) Some checks failed ci/woodpecker/push/check Pipeline failed ci/woodpecker/push/locale Pipeline was successful ci/woodpecker/push/build Pipeline was successful ci/woodpecker/push/finish unknown status ci/woodpecker/cron/locale Pipeline was successful ci/woodpecker/cron/build Pipeline was successful ci/woodpecker/cron/check Pipeline was successful
Some checks failed
ci/woodpecker/push/check Pipeline failed
ci/woodpecker/push/locale Pipeline was successful
ci/woodpecker/push/build Pipeline was successful
ci/woodpecker/push/finish unknown status
ci/woodpecker/cron/locale Pipeline was successful
ci/woodpecker/cron/build Pipeline was successful
ci/woodpecker/cron/check Pipeline was successful
This PR will also remove Draft feature from the app. Closes #1270 Closes #1346 Closes #750 Reviewed-on: #1356 Co-authored-by: M M Arif <mmarif@swatian.com> Co-committed-by: M M Arif <mmarif@swatian.com>
This commit is contained in:
parent 36b45df849
commit 4ccaeba2aa
26 changed files with 748 additions and 155 deletions
| @ -36,7 +36,7 @@ Option 2 - Open the terminal (Linux) and navigate to the project directory. Then | |||
- Pull requests | ||||
- Files diff for PRs | ||||
- Notifications | ||||
- Drafts | ||||
- Notes | ||||
- Repositories / issues list | ||||
- [& more...](https://codeberg.org/gitnex/GitNex/wiki/Features) | ||||
| ||||
| |
| @ -82,7 +82,11 @@ | |||
<activity | ||||
android:name=".activities.IssueDetailActivity" | ||||
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation" | ||||
android:windowSoftInputMode="adjustNothing"/> | ||||
android:windowSoftInputMode="adjustResize"/> | ||||
<activity | ||||
android:name=".activities.BiometricUnlock" | ||||
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation" | ||||
android:theme="@style/Theme.AppCompat.DayNight.NoActionBar"/> | ||||
<activity | ||||
android:name=".activities.RepoDetailActivity" | ||||
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation" | ||||
| |
| @ -1,13 +1,10 @@ | |||
package org.mian.gitnex.activities; | ||||
| ||||
import android.content.Context; | ||||
import android.content.Intent; | ||||
import android.os.Bundle; | ||||
import androidx.annotation.NonNull; | ||||
import androidx.appcompat.app.AppCompatActivity; | ||||
import androidx.biometric.BiometricPrompt; | ||||
import androidx.core.content.ContextCompat; | ||||
import java.util.Locale; | ||||
import java.util.concurrent.Executor; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.core.MainApplication; | ||||
import org.mian.gitnex.helpers.AppDatabaseSettings; | ||||
| @ -128,50 +125,8 @@ public abstract class BaseActivity extends AppCompatActivity { | |||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_BIOMETRIC_LIFE_CYCLE_KEY))) { | ||||
| ||||
Executor executor = ContextCompat.getMainExecutor(this); | ||||
| ||||
BiometricPrompt biometricPrompt = | ||||
new BiometricPrompt( | ||||
this, | ||||
executor, | ||||
new BiometricPrompt.AuthenticationCallback() { | ||||
| ||||
@Override | ||||
public void onAuthenticationError( | ||||
int errorCode, @NonNull CharSequence errString) { | ||||
| ||||
super.onAuthenticationError(errorCode, errString); | ||||
| ||||
// Authentication error, close the app | ||||
finish(); | ||||
} | ||||
| ||||
// Authentication succeeded, continue to app | ||||
@Override | ||||
public void onAuthenticationSucceeded( | ||||
@NonNull BiometricPrompt.AuthenticationResult result) { | ||||
super.onAuthenticationSucceeded(result); | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
getApplicationContext(), | ||||
"true", | ||||
AppDatabaseSettings.APP_BIOMETRIC_LIFE_CYCLE_KEY); | ||||
} | ||||
| ||||
// Authentication failed, close the app | ||||
@Override | ||||
public void onAuthenticationFailed() { | ||||
super.onAuthenticationFailed(); | ||||
} | ||||
}); | ||||
| ||||
BiometricPrompt.PromptInfo biometricPromptBuilder = | ||||
new BiometricPrompt.PromptInfo.Builder() | ||||
.setTitle(getString(R.string.biometricAuthTitle)) | ||||
.setSubtitle(getString(R.string.biometricAuthSubTitle)) | ||||
.setNegativeButtonText(getString(R.string.cancelButton)) | ||||
.build(); | ||||
| ||||
biometricPrompt.authenticate(biometricPromptBuilder); | ||||
Intent unlockIntent = new Intent(ctx, BiometricUnlock.class); | ||||
ctx.startActivity(unlockIntent); | ||||
} | ||||
} | ||||
| ||||
| |
| @ -0,0 +1,80 @@ | |||
package org.mian.gitnex.activities; | ||||
| ||||
import android.content.Context; | ||||
import android.os.Bundle; | ||||
import androidx.annotation.NonNull; | ||||
import androidx.appcompat.app.AppCompatActivity; | ||||
import androidx.biometric.BiometricPrompt; | ||||
import androidx.core.content.ContextCompat; | ||||
import java.util.concurrent.Executor; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.databinding.ActivityUnlockBinding; | ||||
import org.mian.gitnex.helpers.AppDatabaseSettings; | ||||
| ||||
/** | ||||
* @author M M Arif | ||||
*/ | ||||
public class BiometricUnlock extends AppCompatActivity { | ||||
| ||||
protected Context ctx = this; | ||||
| ||||
@Override | ||||
public void onCreate(Bundle savedInstanceState) { | ||||
| ||||
super.onCreate(savedInstanceState); | ||||
| ||||
ActivityUnlockBinding activityUnlockBinding = | ||||
ActivityUnlockBinding.inflate(getLayoutInflater()); | ||||
setContentView(activityUnlockBinding.getRoot()); | ||||
} | ||||
| ||||
public void onResume() { | ||||
super.onResume(); | ||||
| ||||
Executor executor = ContextCompat.getMainExecutor(this); | ||||
| ||||
BiometricPrompt biometricPrompt = | ||||
new BiometricPrompt( | ||||
this, | ||||
executor, | ||||
new BiometricPrompt.AuthenticationCallback() { | ||||
| ||||
@Override | ||||
public void onAuthenticationError( | ||||
int errorCode, @NonNull CharSequence errString) { | ||||
| ||||
super.onAuthenticationError(errorCode, errString); | ||||
| ||||
// Authentication error, close the app | ||||
finish(); | ||||
} | ||||
| ||||
// Authentication succeeded, continue to app | ||||
@Override | ||||
public void onAuthenticationSucceeded( | ||||
@NonNull BiometricPrompt.AuthenticationResult result) { | ||||
super.onAuthenticationSucceeded(result); | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
getApplicationContext(), | ||||
"true", | ||||
AppDatabaseSettings.APP_BIOMETRIC_LIFE_CYCLE_KEY); | ||||
finish(); | ||||
} | ||||
| ||||
// Authentication failed, close the app | ||||
@Override | ||||
public void onAuthenticationFailed() { | ||||
super.onAuthenticationFailed(); | ||||
} | ||||
}); | ||||
| ||||
BiometricPrompt.PromptInfo biometricPromptBuilder = | ||||
new BiometricPrompt.PromptInfo.Builder() | ||||
.setTitle(getString(R.string.biometricAuthTitle)) | ||||
.setSubtitle(getString(R.string.biometricAuthSubTitle)) | ||||
.setNegativeButtonText(getString(R.string.cancelButton)) | ||||
.build(); | ||||
| ||||
biometricPrompt.authenticate(biometricPromptBuilder); | ||||
} | ||||
} |
| @ -6,6 +6,7 @@ import android.content.Intent; | |||
import android.net.Uri; | ||||
import android.os.Bundle; | ||||
import android.os.Handler; | ||||
import android.os.Looper; | ||||
import android.view.LayoutInflater; | ||||
import android.view.MotionEvent; | ||||
import android.view.View; | ||||
| @ -14,6 +15,7 @@ import android.widget.TextView; | |||
import androidx.activity.result.ActivityResultLauncher; | ||||
import androidx.activity.result.contract.ActivityResultContracts; | ||||
import androidx.annotation.NonNull; | ||||
import androidx.appcompat.app.AlertDialog; | ||||
import androidx.recyclerview.widget.LinearLayoutManager; | ||||
import com.google.android.material.bottomsheet.BottomSheetDialog; | ||||
import com.google.android.material.datepicker.MaterialDatePicker; | ||||
| @ -43,10 +45,15 @@ import org.mian.gitnex.actions.LabelsActions; | |||
import org.mian.gitnex.adapters.AssigneesListAdapter; | ||||
import org.mian.gitnex.adapters.AttachmentsAdapter; | ||||
import org.mian.gitnex.adapters.LabelsListAdapter; | ||||
import org.mian.gitnex.adapters.NotesAdapter; | ||||
import org.mian.gitnex.clients.RetrofitClient; | ||||
import org.mian.gitnex.database.api.BaseApi; | ||||
import org.mian.gitnex.database.api.NotesApi; | ||||
import org.mian.gitnex.database.models.Notes; | ||||
import org.mian.gitnex.databinding.ActivityCreateIssueBinding; | ||||
import org.mian.gitnex.databinding.BottomSheetAttachmentsBinding; | ||||
import org.mian.gitnex.databinding.CustomAssigneesSelectionDialogBinding; | ||||
import org.mian.gitnex.databinding.CustomInsertNoteBinding; | ||||
import org.mian.gitnex.databinding.CustomLabelsSelectionDialogBinding; | ||||
import org.mian.gitnex.fragments.IssuesFragment; | ||||
import org.mian.gitnex.helpers.AlertDialogs; | ||||
| @ -54,6 +61,7 @@ import org.mian.gitnex.helpers.AppDatabaseSettings; | |||
import org.mian.gitnex.helpers.Constants; | ||||
import org.mian.gitnex.helpers.Markdown; | ||||
import org.mian.gitnex.helpers.SnackBar; | ||||
import org.mian.gitnex.helpers.Toasty; | ||||
import org.mian.gitnex.helpers.attachments.AttachmentUtils; | ||||
import org.mian.gitnex.helpers.attachments.AttachmentsModel; | ||||
import org.mian.gitnex.helpers.contexts.RepositoryContext; | ||||
| @ -77,6 +85,7 @@ public class CreateIssueActivity extends BaseActivity | |||
private LabelsListAdapter labelsAdapter; | ||||
private AssigneesListAdapter assigneesAdapter; | ||||
private MaterialAlertDialogBuilder materialAlertDialogBuilder; | ||||
private MaterialAlertDialogBuilder materialAlertDialogBuilderNotes; | ||||
private List<Integer> labelsIds = new ArrayList<>(); | ||||
private List<String> assigneesListData = new ArrayList<>(); | ||||
private boolean renderMd = false; | ||||
| @ -84,6 +93,11 @@ public class CreateIssueActivity extends BaseActivity | |||
private static List<AttachmentsModel> attachmentsList; | ||||
private AttachmentsAdapter attachmentsAdapter; | ||||
private static final List<Uri> contentUri = new ArrayList<>(); | ||||
private CustomInsertNoteBinding customInsertNoteBinding; | ||||
private NotesAdapter adapter; | ||||
private NotesApi notesApi; | ||||
private List<Notes> notesList; | ||||
public AlertDialog dialogNotes; | ||||
| ||||
@SuppressLint("ClickableViewAccessibility") | ||||
@Override | ||||
| @ -98,6 +112,8 @@ public class CreateIssueActivity extends BaseActivity | |||
| ||||
materialAlertDialogBuilder = | ||||
new MaterialAlertDialogBuilder(ctx, R.style.ThemeOverlay_Material3_Dialog_Alert); | ||||
materialAlertDialogBuilderNotes = | ||||
new MaterialAlertDialogBuilder(ctx, R.style.ThemeOverlay_Material3_Dialog_Alert); | ||||
| ||||
repository = RepositoryContext.fromIntent(getIntent()); | ||||
| ||||
| @ -174,6 +190,8 @@ public class CreateIssueActivity extends BaseActivity | |||
} | ||||
}); | ||||
| ||||
viewBinding.insertNote.setOnClickListener(insertNote -> showAllNotes()); | ||||
| ||||
getMilestones(repository.getOwner(), repository.getName(), resultLimit); | ||||
| ||||
viewBinding.newIssueLabels.setOnClickListener(newIssueLabels -> showLabels()); | ||||
| @ -189,6 +207,62 @@ public class CreateIssueActivity extends BaseActivity | |||
} | ||||
} | ||||
| ||||
private void showAllNotes() { | ||||
| ||||
notesList = new ArrayList<>(); | ||||
notesApi = BaseApi.getInstance(ctx, NotesApi.class); | ||||
| ||||
customInsertNoteBinding = CustomInsertNoteBinding.inflate(LayoutInflater.from(ctx)); | ||||
| ||||
View view = customInsertNoteBinding.getRoot(); | ||||
materialAlertDialogBuilderNotes.setView(view); | ||||
| ||||
customInsertNoteBinding.recyclerView.setHasFixedSize(true); | ||||
customInsertNoteBinding.recyclerView.setLayoutManager(new LinearLayoutManager(ctx)); | ||||
| ||||
adapter = new NotesAdapter(ctx, notesList, "insert", "issue"); | ||||
| ||||
customInsertNoteBinding.pullToRefresh.setOnRefreshListener( | ||||
() -> | ||||
new Handler(Looper.getMainLooper()) | ||||
.postDelayed( | ||||
() -> { | ||||
notesList.clear(); | ||||
customInsertNoteBinding.pullToRefresh.setRefreshing( | ||||
false); | ||||
customInsertNoteBinding.progressBar.setVisibility( | ||||
View.VISIBLE); | ||||
fetchNotes(); | ||||
}, | ||||
250)); | ||||
| ||||
if (notesApi.getCount() > 0) { | ||||
fetchNotes(); | ||||
dialogNotes = materialAlertDialogBuilderNotes.show(); | ||||
} else { | ||||
Toasty.warning(ctx, getResources().getString(R.string.noNotes)); | ||||
} | ||||
} | ||||
| ||||
private void fetchNotes() { | ||||
| ||||
notesApi.fetchAllNotes() | ||||
.observe( | ||||
this, | ||||
allNotes -> { | ||||
assert allNotes != null; | ||||
if (!allNotes.isEmpty()) { | ||||
| ||||
notesList.clear(); | ||||
| ||||
notesList.addAll(allNotes); | ||||
adapter.notifyDataChanged(); | ||||
customInsertNoteBinding.recyclerView.setAdapter(adapter); | ||||
} | ||||
customInsertNoteBinding.progressBar.setVisibility(View.GONE); | ||||
}); | ||||
} | ||||
| ||||
ActivityResultLauncher<Intent> startActivityForResult = | ||||
registerForActivityResult( | ||||
new ActivityResultContracts.StartActivityForResult(), | ||||
| |
| @ -6,6 +6,7 @@ import android.content.Intent; | |||
import android.net.Uri; | ||||
import android.os.Bundle; | ||||
import android.os.Handler; | ||||
import android.os.Looper; | ||||
import android.view.LayoutInflater; | ||||
import android.view.MotionEvent; | ||||
import android.view.View; | ||||
| @ -14,6 +15,7 @@ import android.widget.TextView; | |||
import androidx.activity.result.ActivityResultLauncher; | ||||
import androidx.activity.result.contract.ActivityResultContracts; | ||||
import androidx.annotation.NonNull; | ||||
import androidx.appcompat.app.AlertDialog; | ||||
import androidx.recyclerview.widget.LinearLayoutManager; | ||||
import com.google.android.material.bottomsheet.BottomSheetDialog; | ||||
import com.google.android.material.datepicker.MaterialDatePicker; | ||||
| @ -41,9 +43,14 @@ import org.mian.gitnex.R; | |||
import org.mian.gitnex.actions.LabelsActions; | ||||
import org.mian.gitnex.adapters.AttachmentsAdapter; | ||||
import org.mian.gitnex.adapters.LabelsListAdapter; | ||||
import org.mian.gitnex.adapters.NotesAdapter; | ||||
import org.mian.gitnex.clients.RetrofitClient; | ||||
import org.mian.gitnex.database.api.BaseApi; | ||||
import org.mian.gitnex.database.api.NotesApi; | ||||
import org.mian.gitnex.database.models.Notes; | ||||
import org.mian.gitnex.databinding.ActivityCreatePrBinding; | ||||
import org.mian.gitnex.databinding.BottomSheetAttachmentsBinding; | ||||
import org.mian.gitnex.databinding.CustomInsertNoteBinding; | ||||
import org.mian.gitnex.databinding.CustomLabelsSelectionDialogBinding; | ||||
import org.mian.gitnex.fragments.PullRequestsFragment; | ||||
import org.mian.gitnex.helpers.AlertDialogs; | ||||
| @ -51,6 +58,7 @@ import org.mian.gitnex.helpers.AppDatabaseSettings; | |||
import org.mian.gitnex.helpers.Constants; | ||||
import org.mian.gitnex.helpers.Markdown; | ||||
import org.mian.gitnex.helpers.SnackBar; | ||||
import org.mian.gitnex.helpers.Toasty; | ||||
import org.mian.gitnex.helpers.attachments.AttachmentUtils; | ||||
import org.mian.gitnex.helpers.attachments.AttachmentsModel; | ||||
import org.mian.gitnex.helpers.contexts.RepositoryContext; | ||||
| @ -74,11 +82,17 @@ public class CreatePullRequestActivity extends BaseActivity | |||
private RepositoryContext repository; | ||||
private LabelsListAdapter labelsAdapter; | ||||
private MaterialAlertDialogBuilder materialAlertDialogBuilder; | ||||
private MaterialAlertDialogBuilder materialAlertDialogBuilderNotes; | ||||
private boolean renderMd = false; | ||||
private RepositoryContext repositoryContext; | ||||
private static List<AttachmentsModel> attachmentsList; | ||||
private AttachmentsAdapter attachmentsAdapter; | ||||
private static final List<Uri> contentUri = new ArrayList<>(); | ||||
private CustomInsertNoteBinding customInsertNoteBinding; | ||||
private NotesAdapter adapter; | ||||
private NotesApi notesApi; | ||||
private List<Notes> notesList; | ||||
public AlertDialog dialogNotes; | ||||
| ||||
@SuppressLint("ClickableViewAccessibility") | ||||
@Override | ||||
| @ -93,6 +107,8 @@ public class CreatePullRequestActivity extends BaseActivity | |||
| ||||
materialAlertDialogBuilder = | ||||
new MaterialAlertDialogBuilder(ctx, R.style.ThemeOverlay_Material3_Dialog_Alert); | ||||
materialAlertDialogBuilderNotes = | ||||
new MaterialAlertDialogBuilder(ctx, R.style.ThemeOverlay_Material3_Dialog_Alert); | ||||
| ||||
repository = RepositoryContext.fromIntent(getIntent()); | ||||
| ||||
| @ -162,6 +178,8 @@ public class CreatePullRequestActivity extends BaseActivity | |||
} | ||||
}); | ||||
| ||||
viewBinding.insertNote.setOnClickListener(insertNote -> showAllNotes()); | ||||
| ||||
getMilestones(repository.getOwner(), repository.getName(), resultLimit); | ||||
getBranches(repository.getOwner(), repository.getName()); | ||||
| ||||
| @ -190,6 +208,62 @@ public class CreatePullRequestActivity extends BaseActivity | |||
} | ||||
}); | ||||
| ||||
private void showAllNotes() { | ||||
| ||||
notesList = new ArrayList<>(); | ||||
notesApi = BaseApi.getInstance(ctx, NotesApi.class); | ||||
| ||||
customInsertNoteBinding = CustomInsertNoteBinding.inflate(LayoutInflater.from(ctx)); | ||||
| ||||
View view = customInsertNoteBinding.getRoot(); | ||||
materialAlertDialogBuilderNotes.setView(view); | ||||
| ||||
customInsertNoteBinding.recyclerView.setHasFixedSize(true); | ||||
customInsertNoteBinding.recyclerView.setLayoutManager(new LinearLayoutManager(ctx)); | ||||
| ||||
adapter = new NotesAdapter(ctx, notesList, "insert", "pr"); | ||||
| ||||
customInsertNoteBinding.pullToRefresh.setOnRefreshListener( | ||||
() -> | ||||
new Handler(Looper.getMainLooper()) | ||||
.postDelayed( | ||||
() -> { | ||||
notesList.clear(); | ||||
customInsertNoteBinding.pullToRefresh.setRefreshing( | ||||
false); | ||||
customInsertNoteBinding.progressBar.setVisibility( | ||||
View.VISIBLE); | ||||
fetchNotes(); | ||||
}, | ||||
250)); | ||||
| ||||
if (notesApi.getCount() > 0) { | ||||
fetchNotes(); | ||||
dialogNotes = materialAlertDialogBuilderNotes.show(); | ||||
} else { | ||||
Toasty.warning(ctx, getResources().getString(R.string.noNotes)); | ||||
} | ||||
} | ||||
| ||||
private void fetchNotes() { | ||||
| ||||
notesApi.fetchAllNotes() | ||||
.observe( | ||||
this, | ||||
allNotes -> { | ||||
assert allNotes != null; | ||||
if (!allNotes.isEmpty()) { | ||||
| ||||
notesList.clear(); | ||||
| ||||
notesList.addAll(allNotes); | ||||
adapter.notifyDataChanged(); | ||||
customInsertNoteBinding.recyclerView.setAdapter(adapter); | ||||
} | ||||
customInsertNoteBinding.progressBar.setVisibility(View.GONE); | ||||
}); | ||||
} | ||||
| ||||
public void onDestroy() { | ||||
AttachmentsAdapter.setAttachmentsReceiveListener(null); | ||||
super.onDestroy(); | ||||
| @ -202,7 +276,7 @@ public class CreatePullRequestActivity extends BaseActivity | |||
| ||||
private void checkForAttachments() { | ||||
| ||||
if (contentUri.size() > 0) { | ||||
if (!contentUri.isEmpty()) { | ||||
| ||||
BottomSheetAttachmentsBinding bottomSheetAttachmentsBinding = | ||||
BottomSheetAttachmentsBinding.inflate(getLayoutInflater()); | ||||
| @ -241,7 +315,10 @@ public class CreatePullRequestActivity extends BaseActivity | |||
| ||||
RequestBody requestFile = | ||||
RequestBody.create( | ||||
file, MediaType.parse(getContentResolver().getType(contentUri.get(i)))); | ||||
file, | ||||
MediaType.parse( | ||||
Objects.requireNonNull( | ||||
getContentResolver().getType(contentUri.get(i))))); | ||||
| ||||
uploadAttachments(requestFile, issueIndex, file.getName()); | ||||
} | ||||
| @ -301,7 +378,7 @@ public class CreatePullRequestActivity extends BaseActivity | |||
| ||||
assignees.add(""); | ||||
| ||||
if (labelsIds.size() == 0) { | ||||
if (labelsIds.isEmpty()) { | ||||
| ||||
labelsIds.add(0); | ||||
} | ||||
| @ -383,7 +460,7 @@ public class CreatePullRequestActivity extends BaseActivity | |||
PullRequestsFragment.resumePullRequests = true; | ||||
MainActivity.reloadRepos = true; | ||||
| ||||
if (contentUri.size() > 0) { | ||||
if (!contentUri.isEmpty()) { | ||||
assert response.body() != null; | ||||
processAttachments(response.body().getNumber()); | ||||
contentUri.clear(); | ||||
| @ -555,7 +632,7 @@ public class CreatePullRequestActivity extends BaseActivity | |||
.title(getString(R.string.issueCreatedNoMilestone))); | ||||
assert milestonesList_ != null; | ||||
| ||||
if (milestonesList_.size() > 0) { | ||||
if (!milestonesList_.isEmpty()) { | ||||
| ||||
for (Milestone milestone : milestonesList_) { | ||||
| ||||
| |
| @ -3,10 +3,15 @@ package org.mian.gitnex.activities; | |||
import android.annotation.SuppressLint; | ||||
import android.os.Bundle; | ||||
import android.os.Handler; | ||||
import android.os.Looper; | ||||
import android.view.LayoutInflater; | ||||
import android.view.MotionEvent; | ||||
import android.view.View; | ||||
import android.widget.ArrayAdapter; | ||||
import androidx.annotation.NonNull; | ||||
import androidx.appcompat.app.AlertDialog; | ||||
import androidx.recyclerview.widget.LinearLayoutManager; | ||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder; | ||||
import com.vdurmont.emoji.EmojiParser; | ||||
import java.util.ArrayList; | ||||
import java.util.List; | ||||
| @ -17,11 +22,17 @@ import org.gitnex.tea4j.v2.models.CreateTagOption; | |||
import org.gitnex.tea4j.v2.models.Release; | ||||
import org.gitnex.tea4j.v2.models.Tag; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.adapters.NotesAdapter; | ||||
import org.mian.gitnex.clients.RetrofitClient; | ||||
import org.mian.gitnex.database.api.BaseApi; | ||||
import org.mian.gitnex.database.api.NotesApi; | ||||
import org.mian.gitnex.database.models.Notes; | ||||
import org.mian.gitnex.databinding.ActivityCreateReleaseBinding; | ||||
import org.mian.gitnex.databinding.CustomInsertNoteBinding; | ||||
import org.mian.gitnex.helpers.AlertDialogs; | ||||
import org.mian.gitnex.helpers.Markdown; | ||||
import org.mian.gitnex.helpers.SnackBar; | ||||
import org.mian.gitnex.helpers.Toasty; | ||||
import org.mian.gitnex.helpers.contexts.RepositoryContext; | ||||
import retrofit2.Call; | ||||
import retrofit2.Callback; | ||||
| @ -36,6 +47,12 @@ public class CreateReleaseActivity extends BaseActivity { | |||
private String selectedBranch; | ||||
private RepositoryContext repository; | ||||
private boolean renderMd = false; | ||||
private MaterialAlertDialogBuilder materialAlertDialogBuilder; | ||||
private CustomInsertNoteBinding customInsertNoteBinding; | ||||
private NotesAdapter adapter; | ||||
private NotesApi notesApi; | ||||
private List<Notes> notesList; | ||||
public AlertDialog dialogNotes; | ||||
| ||||
@SuppressLint("ClickableViewAccessibility") | ||||
@Override | ||||
| @ -48,6 +65,9 @@ public class CreateReleaseActivity extends BaseActivity { | |||
| ||||
repository = RepositoryContext.fromIntent(getIntent()); | ||||
| ||||
materialAlertDialogBuilder = | ||||
new MaterialAlertDialogBuilder(ctx, R.style.ThemeOverlay_Material3_Dialog_Alert); | ||||
| ||||
binding.releaseContent.setOnTouchListener( | ||||
(touchView, motionEvent) -> { | ||||
touchView.getParent().requestDisallowInterceptTouchEvent(true); | ||||
| @ -60,10 +80,7 @@ public class CreateReleaseActivity extends BaseActivity { | |||
return false; | ||||
}); | ||||
| ||||
binding.topAppBar.setNavigationOnClickListener( | ||||
v -> { | ||||
finish(); | ||||
}); | ||||
binding.topAppBar.setNavigationOnClickListener(v -> finish()); | ||||
| ||||
binding.topAppBar.setOnMenuItemClickListener( | ||||
menuItem -> { | ||||
| @ -103,9 +120,67 @@ public class CreateReleaseActivity extends BaseActivity { | |||
} | ||||
}); | ||||
| ||||
binding.insertNote.setOnClickListener(insertNote -> showAllNotes()); | ||||
| ||||
getBranches(repository.getOwner(), repository.getName()); | ||||
} | ||||
| ||||
private void showAllNotes() { | ||||
| ||||
notesList = new ArrayList<>(); | ||||
notesApi = BaseApi.getInstance(ctx, NotesApi.class); | ||||
| ||||
customInsertNoteBinding = CustomInsertNoteBinding.inflate(LayoutInflater.from(ctx)); | ||||
| ||||
View view = customInsertNoteBinding.getRoot(); | ||||
materialAlertDialogBuilder.setView(view); | ||||
| ||||
customInsertNoteBinding.recyclerView.setHasFixedSize(true); | ||||
customInsertNoteBinding.recyclerView.setLayoutManager(new LinearLayoutManager(ctx)); | ||||
| ||||
adapter = new NotesAdapter(ctx, notesList, "insert", "release"); | ||||
| ||||
customInsertNoteBinding.pullToRefresh.setOnRefreshListener( | ||||
() -> | ||||
new Handler(Looper.getMainLooper()) | ||||
.postDelayed( | ||||
() -> { | ||||
notesList.clear(); | ||||
customInsertNoteBinding.pullToRefresh.setRefreshing( | ||||
false); | ||||
customInsertNoteBinding.progressBar.setVisibility( | ||||
View.VISIBLE); | ||||
fetchNotes(); | ||||
}, | ||||
250)); | ||||
| ||||
if (notesApi.getCount() > 0) { | ||||
fetchNotes(); | ||||
dialogNotes = materialAlertDialogBuilder.show(); | ||||
} else { | ||||
Toasty.warning(ctx, getResources().getString(R.string.noNotes)); | ||||
} | ||||
} | ||||
| ||||
private void fetchNotes() { | ||||
| ||||
notesApi.fetchAllNotes() | ||||
.observe( | ||||
this, | ||||
allNotes -> { | ||||
assert allNotes != null; | ||||
if (!allNotes.isEmpty()) { | ||||
| ||||
notesList.clear(); | ||||
| ||||
notesList.addAll(allNotes); | ||||
adapter.notifyDataChanged(); | ||||
customInsertNoteBinding.recyclerView.setAdapter(adapter); | ||||
} | ||||
customInsertNoteBinding.progressBar.setVisibility(View.GONE); | ||||
}); | ||||
} | ||||
| ||||
private void createNewTag() { | ||||
| ||||
String tagName = Objects.requireNonNull(binding.releaseTagName.getText()).toString(); | ||||
| @ -114,7 +189,7 @@ public class CreateReleaseActivity extends BaseActivity { | |||
+ "\n\n" | ||||
+ Objects.requireNonNull(binding.releaseContent.getText()); | ||||
| ||||
if (tagName.equals("")) { | ||||
if (tagName.isEmpty()) { | ||||
SnackBar.error( | ||||
ctx, findViewById(android.R.id.content), getString(R.string.tagNameErrorEmpty)); | ||||
return; | ||||
| @ -187,13 +262,13 @@ public class CreateReleaseActivity extends BaseActivity { | |||
boolean newReleaseType = binding.releaseType.isChecked(); | ||||
boolean newReleaseDraft = binding.releaseDraft.isChecked(); | ||||
| ||||
if (newReleaseTitle.equals("")) { | ||||
if (newReleaseTitle.isEmpty()) { | ||||
SnackBar.error( | ||||
ctx, findViewById(android.R.id.content), getString(R.string.titleErrorEmpty)); | ||||
return; | ||||
} | ||||
| ||||
if (newReleaseTagName.equals("")) { | ||||
if (newReleaseTagName.isEmpty()) { | ||||
SnackBar.error( | ||||
ctx, findViewById(android.R.id.content), getString(R.string.tagNameErrorEmpty)); | ||||
return; | ||||
| |
| @ -11,6 +11,8 @@ import android.os.Build; | |||
import android.os.Bundle; | ||||
import android.os.Handler; | ||||
import android.os.Looper; | ||||
import android.text.Editable; | ||||
import android.text.TextWatcher; | ||||
import android.util.Log; | ||||
import android.view.Gravity; | ||||
import android.view.LayoutInflater; | ||||
| @ -18,6 +20,8 @@ import android.view.Menu; | |||
import android.view.MenuInflater; | ||||
import android.view.MenuItem; | ||||
import android.view.View; | ||||
import android.view.WindowManager; | ||||
import android.view.inputmethod.InputMethodManager; | ||||
import android.widget.ImageView; | ||||
import android.widget.LinearLayout; | ||||
import android.widget.ScrollView; | ||||
| @ -57,7 +61,9 @@ import org.gitnex.tea4j.v2.models.Repository; | |||
import org.gitnex.tea4j.v2.models.User; | ||||
import org.gitnex.tea4j.v2.models.WatchInfo; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.actions.ActionResult; | ||||
import org.mian.gitnex.actions.AssigneesActions; | ||||
import org.mian.gitnex.actions.IssueActions; | ||||
import org.mian.gitnex.actions.LabelsActions; | ||||
import org.mian.gitnex.adapters.AssigneesListAdapter; | ||||
import org.mian.gitnex.adapters.IssueCommentsAdapter; | ||||
| @ -82,6 +88,7 @@ import org.mian.gitnex.helpers.LabelWidthCalculator; | |||
import org.mian.gitnex.helpers.Markdown; | ||||
import org.mian.gitnex.helpers.RoundedTransformation; | ||||
import org.mian.gitnex.helpers.TimeHelper; | ||||
import org.mian.gitnex.helpers.TinyDB; | ||||
import org.mian.gitnex.helpers.Toasty; | ||||
import org.mian.gitnex.helpers.contexts.IssueContext; | ||||
import org.mian.gitnex.notifications.Notifications; | ||||
| @ -105,7 +112,7 @@ public class IssueDetailActivity extends BaseActivity | |||
public static boolean commentPosted = false; | ||||
private final List<Label> labelsList = new ArrayList<>(); | ||||
private final List<User> assigneesList = new ArrayList<>(); | ||||
public boolean commentEdited = false; | ||||
public static boolean commentEdited = false; | ||||
private IssueCommentsAdapter adapter; | ||||
private String repoOwner; | ||||
private String repoName; | ||||
| @ -131,6 +138,13 @@ public class IssueDetailActivity extends BaseActivity | |||
private String instanceUrlOnly; | ||||
private String token; | ||||
private int page = 1; | ||||
private TinyDB tinyDB; | ||||
private Mode mode = Mode.SEND; | ||||
| ||||
private enum Mode { | ||||
EDIT, | ||||
SEND | ||||
} | ||||
| ||||
public ActivityResultLauncher<Intent> editIssueLauncher = | ||||
registerForActivityResult( | ||||
| @ -275,10 +289,15 @@ public class IssueDetailActivity extends BaseActivity | |||
repoName = issue.getRepository().getName(); | ||||
issueIndex = issue.getIssueIndex(); | ||||
| ||||
tinyDB = TinyDB.getInstance(ctx); | ||||
| ||||
setSupportActionBar(viewBinding.toolbar); | ||||
Objects.requireNonNull(getSupportActionBar()).setTitle(repoName); | ||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true); | ||||
| ||||
InputMethodManager imm = | ||||
(InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE); | ||||
| ||||
String instanceUrl = ((BaseActivity) ctx).getAccount().getAccount().getInstanceUrl(); | ||||
instanceUrlOnly = instanceUrl.substring(0, instanceUrl.lastIndexOf("api/v1/")); | ||||
| ||||
| @ -293,31 +312,11 @@ public class IssueDetailActivity extends BaseActivity | |||
viewBinding.recyclerView.setNestedScrollingEnabled(false); | ||||
viewBinding.recyclerView.setLayoutManager(new LinearLayoutManager(ctx)); | ||||
| ||||
new Handler() | ||||
.postDelayed( | ||||
() -> { | ||||
if (issue.getIssue() != null) { | ||||
if (issue.getIssue().isIsLocked() != null) { | ||||
if (issue.getIssue().isIsLocked()) { | ||||
if (issue.getRepository().getPermissions() != null | ||||
&& issue.getRepository().getPermissions().isAdmin() | ||||
!= null) { | ||||
if (issue.getRepository().getPermissions().isAdmin()) { | ||||
viewBinding.addNewComment.setVisibility( | ||||
View.VISIBLE); | ||||
} else { | ||||
viewBinding.addNewComment.setVisibility(View.GONE); | ||||
} | ||||
} else { | ||||
viewBinding.addNewComment.setVisibility(View.GONE); | ||||
} | ||||
} else { | ||||
viewBinding.addNewComment.setVisibility(View.VISIBLE); | ||||
} | ||||
} | ||||
} | ||||
}, | ||||
50); | ||||
float buttonAlphaStatDisabled = .5F; | ||||
float buttonAlphaStatEnabled = 1F; | ||||
| ||||
viewBinding.send.setAlpha(buttonAlphaStatDisabled); | ||||
viewBinding.send.setEnabled(false); | ||||
| ||||
viewBinding.addNewComment.setOnClickListener( | ||||
v -> { | ||||
| @ -360,6 +359,166 @@ public class IssueDetailActivity extends BaseActivity | |||
&& Objects.equals(getIntent().getStringExtra("openPrDiff"), "true")) { | ||||
startActivity(issue.getIntent(ctx, DiffActivity.class)); | ||||
} | ||||
| ||||
viewBinding.commentReply.addTextChangedListener( | ||||
new TextWatcher() { | ||||
@Override | ||||
public void beforeTextChanged(CharSequence chr, int i, int i1, int i2) {} | ||||
| ||||
@Override | ||||
public void onTextChanged(CharSequence chr, int i, int i1, int i2) { | ||||
if (chr.length() > 0) { | ||||
viewBinding.commentReply.requestFocus(); | ||||
getWindow() | ||||
.setSoftInputMode( | ||||
WindowManager.LayoutParams | ||||
.SOFT_INPUT_STATE_ALWAYS_VISIBLE); | ||||
viewBinding.commentReply.setSelection( | ||||
viewBinding.commentReply.length()); | ||||
viewBinding.send.setAlpha(buttonAlphaStatEnabled); | ||||
viewBinding.send.setEnabled(true); | ||||
} else { | ||||
viewBinding.send.setAlpha(buttonAlphaStatDisabled); | ||||
viewBinding.send.setEnabled(false); | ||||
} | ||||
} | ||||
| ||||
@Override | ||||
public void afterTextChanged(Editable editable) { | ||||
| ||||
if (editable.length() > 0) { | ||||
new Handler() | ||||
.postDelayed( | ||||
() -> { | ||||
if (issue.getIssue() != null) { | ||||
if (issue.getIssue().isIsLocked() != null) { | ||||
if (issue.getIssue().isIsLocked()) { | ||||
if (issue.getRepository() | ||||
.getPermissions() | ||||
!= null | ||||
&& issue.getRepository() | ||||
.getPermissions() | ||||
.isAdmin() | ||||
!= null) { | ||||
if (issue.getRepository() | ||||
.getPermissions() | ||||
.isAdmin()) { | ||||
viewBinding.send.setEnabled( | ||||
true); | ||||
viewBinding.send.setAlpha( | ||||
buttonAlphaStatEnabled); | ||||
} else { | ||||
viewBinding.send.setAlpha( | ||||
buttonAlphaStatDisabled); | ||||
viewBinding.send.setEnabled( | ||||
false); | ||||
} | ||||
} else { | ||||
viewBinding.send.setAlpha( | ||||
buttonAlphaStatDisabled); | ||||
viewBinding.send.setEnabled(false); | ||||
} | ||||
} else { | ||||
viewBinding.send.setEnabled(true); | ||||
viewBinding.send.setAlpha( | ||||
buttonAlphaStatEnabled); | ||||
} | ||||
} | ||||
} | ||||
}, | ||||
50); | ||||
} | ||||
} | ||||
}); | ||||
| ||||
viewBinding.send.setOnClickListener( | ||||
v -> { | ||||
if (Objects.requireNonNull(tinyDB.getString("commentAction")) | ||||
.equalsIgnoreCase("edit")) { | ||||
mode = Mode.EDIT; | ||||
} else { | ||||
mode = Mode.SEND; | ||||
} | ||||
| ||||
if (mode == Mode.SEND) { | ||||
| ||||
IssueActions.reply( | ||||
ctx, viewBinding.commentReply.getText().toString(), issue) | ||||
.accept( | ||||
(status, result) -> { | ||||
if (status == ActionResult.Status.SUCCESS) { | ||||
| ||||
viewBinding.scrollViewComments.post( | ||||
() -> | ||||
issueCommentsModel | ||||
.loadIssueComments( | ||||
repoOwner, | ||||
repoName, | ||||
issueIndex, | ||||
ctx, | ||||
() -> | ||||
viewBinding | ||||
.scrollViewComments | ||||
.fullScroll( | ||||
ScrollView | ||||
.FOCUS_DOWN))); | ||||
| ||||
Toasty.success( | ||||
ctx, getString(R.string.commentSuccess)); | ||||
| ||||
viewBinding.send.setAlpha(buttonAlphaStatDisabled); | ||||
viewBinding.send.setEnabled(false); | ||||
viewBinding.commentReply.setText(null); | ||||
viewBinding.commentReply.clearFocus(); | ||||
imm.toggleSoftInput( | ||||
InputMethodManager.HIDE_IMPLICIT_ONLY, 0); | ||||
} else { | ||||
| ||||
Toasty.error(ctx, getString(R.string.genericError)); | ||||
} | ||||
}); | ||||
} else { | ||||
| ||||
IssueActions.edit( | ||||
ctx, | ||||
viewBinding.commentReply.getText().toString(), | ||||
tinyDB.getInt("commentId"), | ||||
issue) | ||||
.accept( | ||||
(status, result) -> { | ||||
if (status == ActionResult.Status.SUCCESS) { | ||||
| ||||
tinyDB.remove("commentId"); | ||||
tinyDB.remove("commentAction"); | ||||
| ||||
viewBinding.scrollViewComments.post( | ||||
() -> | ||||
issueCommentsModel | ||||
.loadIssueComments( | ||||
repoOwner, | ||||
repoName, | ||||
issueIndex, | ||||
ctx, | ||||
null)); | ||||
| ||||
Toasty.success( | ||||
ctx, | ||||
getString(R.string.editCommentUpdatedText)); | ||||
| ||||
mode = Mode.SEND; | ||||
viewBinding.send.setAlpha(buttonAlphaStatDisabled); | ||||
viewBinding.send.setEnabled(false); | ||||
viewBinding.commentReply.setText(null); | ||||
viewBinding.commentReply.clearFocus(); | ||||
imm.toggleSoftInput( | ||||
InputMethodManager.HIDE_IMPLICIT_ONLY, 0); | ||||
} else { | ||||
| ||||
Toasty.error(ctx, getString(R.string.genericError)); | ||||
} | ||||
}); | ||||
} | ||||
}); | ||||
} | ||||
| ||||
@Override | ||||
| @ -708,6 +867,10 @@ public class IssueDetailActivity extends BaseActivity | |||
}, | ||||
500); | ||||
} | ||||
| ||||
tinyDB.remove("commentId"); | ||||
tinyDB.remove("commentAction"); | ||||
mode = Mode.SEND; | ||||
} | ||||
| ||||
private void fetchDataAsync(String owner, String repo, int index) { | ||||
| @ -724,12 +887,7 @@ public class IssueDetailActivity extends BaseActivity | |||
| ||||
adapter = | ||||
new IssueCommentsAdapter( | ||||
ctx, | ||||
bundle, | ||||
issueCommentsMain, | ||||
getSupportFragmentManager(), | ||||
this::onResume, | ||||
issue); | ||||
ctx, bundle, issueCommentsMain, this::onResume, issue); | ||||
adapter.setLoadMoreListener( | ||||
new IssueCommentsAdapter.OnLoadMoreListener() { | ||||
| ||||
| |
| @ -18,6 +18,7 @@ import android.view.Gravity; | |||
import android.view.LayoutInflater; | ||||
import android.view.View; | ||||
import android.view.ViewGroup; | ||||
import android.widget.EditText; | ||||
import android.widget.FrameLayout; | ||||
import android.widget.ImageView; | ||||
import android.widget.LinearLayout; | ||||
| @ -27,7 +28,6 @@ import androidx.annotation.NonNull; | |||
import androidx.core.content.ContextCompat; | ||||
import androidx.core.content.res.ResourcesCompat; | ||||
import androidx.core.text.HtmlCompat; | ||||
import androidx.fragment.app.FragmentManager; | ||||
import androidx.recyclerview.widget.RecyclerView; | ||||
import com.amulyakhare.textdrawable.TextDrawable; | ||||
import com.google.android.material.bottomsheet.BottomSheetDialog; | ||||
| @ -52,7 +52,6 @@ import org.mian.gitnex.activities.ProfileActivity; | |||
import org.mian.gitnex.clients.PicassoService; | ||||
import org.mian.gitnex.clients.RetrofitClient; | ||||
import org.mian.gitnex.databinding.CustomImageViewDialogBinding; | ||||
import org.mian.gitnex.fragments.BottomSheetReplyFragment; | ||||
import org.mian.gitnex.fragments.IssuesFragment; | ||||
import org.mian.gitnex.helpers.AlertDialogs; | ||||
import org.mian.gitnex.helpers.AppDatabaseSettings; | ||||
| @ -79,7 +78,6 @@ public class IssueCommentsAdapter extends RecyclerView.Adapter<RecyclerView.View | |||
private final Context context; | ||||
private final TinyDB tinyDB; | ||||
private final Bundle bundle; | ||||
private final FragmentManager fragmentManager; | ||||
private final Runnable onInteractedListener; | ||||
private final Locale locale; | ||||
private final IssueContext issue; | ||||
| @ -92,14 +90,12 @@ public class IssueCommentsAdapter extends RecyclerView.Adapter<RecyclerView.View | |||
Context ctx, | ||||
Bundle bundle, | ||||
List<TimelineComment> issuesCommentsMain, | ||||
FragmentManager fragmentManager, | ||||
Runnable onInteractedListener, | ||||
IssueContext issue) { | ||||
| ||||
this.context = ctx; | ||||
this.bundle = bundle; | ||||
this.issuesComments = issuesCommentsMain; | ||||
this.fragmentManager = fragmentManager; | ||||
this.onInteractedListener = onInteractedListener; | ||||
tinyDB = TinyDB.getInstance(ctx); | ||||
locale = ctx.getResources().getConfiguration().locale; | ||||
| @ -266,11 +262,7 @@ public class IssueCommentsAdapter extends RecyclerView.Adapter<RecyclerView.View | |||
| ||||
if (issueComment != null) { | ||||
new Handler() | ||||
.postDelayed( | ||||
() -> { | ||||
getAttachments(issueComment.getId(), view, token); | ||||
}, | ||||
250); | ||||
.postDelayed(() -> getAttachments(issueComment.getId(), view, token), 250); | ||||
} | ||||
| ||||
menu.setOnClickListener( | ||||
| @ -343,18 +335,14 @@ public class IssueCommentsAdapter extends RecyclerView.Adapter<RecyclerView.View | |||
| ||||
commentMenuEdit.setOnClickListener( | ||||
v1 -> { | ||||
Bundle bundle = new Bundle(); | ||||
bundle.putInt( | ||||
"commentId", Math.toIntExact(issueComment.getId())); | ||||
bundle.putString("commentAction", "edit"); | ||||
bundle.putString("commentBody", issueComment.getBody()); | ||||
IssueDetailActivity parentActivity = | ||||
(IssueDetailActivity) context; | ||||
EditText text = parentActivity.findViewById(R.id.comment_reply); | ||||
text.append(issueComment.getBody()); | ||||
| ||||
BottomSheetReplyFragment bottomSheetReplyFragment = | ||||
BottomSheetReplyFragment.newInstance(bundle, issue); | ||||
bottomSheetReplyFragment.setOnInteractedListener( | ||||
onInteractedListener); | ||||
bottomSheetReplyFragment.show( | ||||
fragmentManager, "replyBottomSheet"); | ||||
tinyDB.putString("commentAction", "edit"); | ||||
tinyDB.putInt( | ||||
"commentId", Math.toIntExact(issueComment.getId())); | ||||
| ||||
dialog.dismiss(); | ||||
}); | ||||
| @ -402,15 +390,14 @@ public class IssueCommentsAdapter extends RecyclerView.Adapter<RecyclerView.View | |||
stringBuilder.append(">").append(line).append("\n"); | ||||
} | ||||
| ||||
stringBuilder.append("\n"); | ||||
String comment = String.valueOf(stringBuilder.append("\n")); | ||||
| ||||
Bundle bundle = new Bundle(); | ||||
bundle.putString("commentBody", stringBuilder.toString()); | ||||
bundle.putBoolean("cursorToEnd", true); | ||||
IssueDetailActivity parentActivity = | ||||
(IssueDetailActivity) context; | ||||
EditText text = parentActivity.findViewById(R.id.comment_reply); | ||||
text.setText(comment); | ||||
| ||||
dialog.dismiss(); | ||||
BottomSheetReplyFragment.newInstance(bundle, issue) | ||||
.show(fragmentManager, "replyBottomSheet"); | ||||
}); | ||||
| ||||
commentMenuCopy.setOnClickListener( | ||||
| @ -1489,7 +1476,7 @@ public class IssueCommentsAdapter extends RecyclerView.Adapter<RecyclerView.View | |||
attachmentView.setLayoutParams(paramsAttachment); | ||||
materialCardView.addView(attachmentView); | ||||
| ||||
int finalI = i; | ||||
// int finalI = i; | ||||
materialCardView.setOnClickListener( | ||||
v1 -> { | ||||
// filesize = attachment.get(finalI).getSize(); | ||||
| |
| @ -6,6 +6,7 @@ import android.content.Intent; | |||
import android.view.LayoutInflater; | ||||
import android.view.View; | ||||
import android.view.ViewGroup; | ||||
import android.widget.EditText; | ||||
import android.widget.ImageView; | ||||
import android.widget.TextView; | ||||
import androidx.annotation.NonNull; | ||||
| @ -19,7 +20,10 @@ import java.util.Locale; | |||
import java.util.Objects; | ||||
import org.apache.commons.lang3.StringUtils; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.activities.CreateIssueActivity; | ||||
import org.mian.gitnex.activities.CreateNoteActivity; | ||||
import org.mian.gitnex.activities.CreatePullRequestActivity; | ||||
import org.mian.gitnex.activities.CreateReleaseActivity; | ||||
import org.mian.gitnex.database.api.BaseApi; | ||||
import org.mian.gitnex.database.api.NotesApi; | ||||
import org.mian.gitnex.database.models.Notes; | ||||
| @ -35,14 +39,18 @@ public class NotesAdapter extends RecyclerView.Adapter<NotesAdapter.NotesViewHol | |||
private List<Notes> notesList; | ||||
private final Context ctx; | ||||
private final Intent noteIntent; | ||||
private final String insert; | ||||
private final String source; | ||||
| ||||
public NotesAdapter(Context ctx, List<Notes> notesListMain) { | ||||
public NotesAdapter(Context ctx, List<Notes> notesListMain, String insert, String source) { | ||||
this.ctx = ctx; | ||||
this.notesList = notesListMain; | ||||
noteIntent = new Intent(ctx, CreateNoteActivity.class); | ||||
this.insert = insert; | ||||
this.source = source; | ||||
} | ||||
| ||||
class NotesViewHolder extends RecyclerView.ViewHolder { | ||||
public class NotesViewHolder extends RecyclerView.ViewHolder { | ||||
| ||||
private Notes notes; | ||||
| ||||
| @ -82,6 +90,49 @@ public class NotesAdapter extends RecyclerView.Adapter<NotesAdapter.NotesViewHol | |||
.setNeutralButton(R.string.cancelButton, null) | ||||
.show(); | ||||
}); | ||||
| ||||
if (insert.equalsIgnoreCase("insert") && source.equalsIgnoreCase("issue")) { | ||||
| ||||
deleteNote.setVisibility(View.GONE); | ||||
| ||||
itemView.setOnClickListener( | ||||
view -> { | ||||
CreateIssueActivity parentActivity = (CreateIssueActivity) ctx; | ||||
EditText text = parentActivity.findViewById(R.id.newIssueDescription); | ||||
text.append(notes.getContent()); | ||||
| ||||
parentActivity.dialogNotes.dismiss(); | ||||
}); | ||||
} | ||||
| ||||
if (insert.equalsIgnoreCase("insert") && source.equalsIgnoreCase("release")) { | ||||
| ||||
deleteNote.setVisibility(View.GONE); | ||||
| ||||
itemView.setOnClickListener( | ||||
view -> { | ||||
CreateReleaseActivity parentActivity = (CreateReleaseActivity) ctx; | ||||
EditText text = parentActivity.findViewById(R.id.releaseContent); | ||||
text.append(notes.getContent()); | ||||
| ||||
parentActivity.dialogNotes.dismiss(); | ||||
}); | ||||
} | ||||
| ||||
if (insert.equalsIgnoreCase("insert") && source.equalsIgnoreCase("pr")) { | ||||
| ||||
deleteNote.setVisibility(View.GONE); | ||||
| ||||
itemView.setOnClickListener( | ||||
view -> { | ||||
CreatePullRequestActivity parentActivity = | ||||
(CreatePullRequestActivity) ctx; | ||||
EditText text = parentActivity.findViewById(R.id.prBody); | ||||
text.append(notes.getContent()); | ||||
| ||||
parentActivity.dialogNotes.dismiss(); | ||||
}); | ||||
} | ||||
} | ||||
} | ||||
| ||||
| |
| @ -35,6 +35,10 @@ public class NotesApi extends BaseApi { | |||
return notesDao.fetchAllNotes(); | ||||
} | ||||
| ||||
public Integer getCount() { | ||||
return notesDao.getCount(); | ||||
} | ||||
| ||||
public Notes fetchNoteById(int noteId) { | ||||
return notesDao.fetchNoteById(noteId); | ||||
} | ||||
| |
| @ -33,4 +33,7 @@ public interface NotesDao { | |||
| ||||
@Query("DELETE FROM Notes WHERE noteId = :noteId") | ||||
void deleteNote(int noteId); | ||||
| ||||
@Query("SELECT COUNT(noteId) FROM Notes") | ||||
Integer getCount(); | ||||
} | ||||
| |
| @ -33,7 +33,6 @@ import org.mian.gitnex.database.api.DraftsApi; | |||
import org.mian.gitnex.databinding.BottomSheetReplyLayoutBinding; | ||||
import org.mian.gitnex.helpers.AppDatabaseSettings; | ||||
import org.mian.gitnex.helpers.Constants; | ||||
import org.mian.gitnex.helpers.TinyDB; | ||||
import org.mian.gitnex.helpers.Toasty; | ||||
import org.mian.gitnex.helpers.contexts.IssueContext; | ||||
| ||||
| @ -43,7 +42,6 @@ import org.mian.gitnex.helpers.contexts.IssueContext; | |||
public class BottomSheetReplyFragment extends BottomSheetDialogFragment { | ||||
| ||||
private Mode mode = Mode.SEND; | ||||
private TinyDB tinyDB; | ||||
private DraftsApi draftsApi; | ||||
private int currentActiveAccountId; | ||||
private IssueContext issue; | ||||
| @ -65,7 +63,6 @@ public class BottomSheetReplyFragment extends BottomSheetDialogFragment { | |||
| ||||
super.onAttach(context); | ||||
| ||||
tinyDB = TinyDB.getInstance(context); | ||||
draftsApi = BaseApi.getInstance(context, DraftsApi.class); | ||||
| ||||
currentActiveAccountId = | ||||
| @ -103,7 +100,7 @@ public class BottomSheetReplyFragment extends BottomSheetDialogFragment { | |||
| ||||
if (arguments.getString("draftId") != null) { | ||||
| ||||
draftId = Long.parseLong(arguments.getString("draftId")); | ||||
draftId = Long.parseLong(Objects.requireNonNull(arguments.getString("draftId"))); | ||||
} | ||||
| ||||
if (issue.getIssue() != null && !issue.getIssue().getTitle().isEmpty()) { | ||||
| @ -235,8 +232,7 @@ public class BottomSheetReplyFragment extends BottomSheetDialogFragment { | |||
(status, result) -> { | ||||
FragmentActivity activity = requireActivity(); | ||||
if (activity instanceof IssueDetailActivity) { | ||||
((IssueDetailActivity) activity).commentEdited = | ||||
true; | ||||
IssueDetailActivity.commentEdited = true; | ||||
} | ||||
| ||||
if (status == ActionResult.Status.SUCCESS) { | ||||
| @ -271,6 +267,7 @@ public class BottomSheetReplyFragment extends BottomSheetDialogFragment { | |||
| ||||
private void saveDraft(String text, boolean remove) { | ||||
| ||||
@SuppressLint("Recycle") | ||||
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 1f); | ||||
valueAnimator.setDuration(500); | ||||
valueAnimator.addUpdateListener( | ||||
| |
| @ -70,7 +70,7 @@ public class NotesFragment extends Fragment { | |||
binding.recyclerView.setPadding(0, 0, 0, 220); | ||||
binding.recyclerView.setClipToPadding(false); | ||||
| ||||
adapter = new NotesAdapter(ctx, notesList); | ||||
adapter = new NotesAdapter(ctx, notesList, "", ""); | ||||
| ||||
binding.pullToRefresh.setOnRefreshListener( | ||||
() -> | ||||
| @ -103,7 +103,7 @@ public class NotesFragment extends Fragment { | |||
allNotes -> { | ||||
binding.pullToRefresh.setRefreshing(false); | ||||
assert allNotes != null; | ||||
if (allNotes.size() > 0) { | ||||
if (!allNotes.isEmpty()) { | ||||
| ||||
notesList.clear(); | ||||
binding.noData.setVisibility(View.GONE); | ||||
| @ -138,7 +138,7 @@ public class NotesFragment extends Fragment { | |||
| ||||
public void deleteAllNotes() { | ||||
| ||||
if (notesList.size() > 0) { | ||||
if (!notesList.isEmpty()) { | ||||
| ||||
notesApi.deleteAllNotes(); | ||||
notesList.clear(); | ||||
| @ -159,6 +159,7 @@ public class NotesFragment extends Fragment { | |||
| ||||
MenuItem searchItem = menu.findItem(R.id.action_search); | ||||
SearchView searchView = (SearchView) searchItem.getActionView(); | ||||
assert searchView != null; | ||||
searchView.setImeOptions(EditorInfo.IME_ACTION_DONE); | ||||
| ||||
searchView.setOnQueryTextListener( | ||||
| @ -184,7 +185,7 @@ public class NotesFragment extends Fragment { | |||
| ||||
if (item.getItemId() == R.id.reset_menu_item) { | ||||
| ||||
if (notesList.size() == 0) { | ||||
if (notesList.isEmpty()) { | ||||
Toasty.warning(ctx, getResources().getString(R.string.noDataFound)); | ||||
} else { | ||||
new MaterialAlertDialogBuilder(ctx) | ||||
| |
| @ -4,17 +4,17 @@ | |||
android:viewportWidth="24" | ||||
android:viewportHeight="24"> | ||||
<path | ||||
android:fillColor="#00000000" | ||||
android:pathData="M22,2L11,13" | ||||
android:strokeWidth="2" | ||||
android:strokeColor="?attr/iconsColor" | ||||
android:strokeLineCap="round" | ||||
android:strokeLineJoin="round"/> | ||||
android:pathData="M4.698,4.034l16.302,7.966l-16.302,7.966a0.503,0.503 0,0 1,-0.546 -0.124a0.555,0.555 0,0 1,-0.12 -0.568l2.468,-7.274l-2.468,-7.274a0.555,0.555 0,0 1,0.12 -0.568a0.503,0.503 0,0 1,0.546 -0.124z" | ||||
android:strokeLineJoin="round" | ||||
android:strokeWidth="2" | ||||
android:fillColor="#00000000" | ||||
android:strokeColor="?attr/iconsColor" | ||||
android:strokeLineCap="round"/> | ||||
<path | ||||
android:fillColor="#00000000" | ||||
android:pathData="M22,2l-7,20l-4,-9l-9,-4l20,-7z" | ||||
android:strokeWidth="2" | ||||
android:strokeColor="?attr/iconsColor" | ||||
android:strokeLineCap="round" | ||||
android:strokeLineJoin="round"/> | ||||
android:pathData="M6.5,12h14.5" | ||||
android:strokeLineJoin="round" | ||||
android:strokeWidth="2" | ||||
android:fillColor="#00000000" | ||||
android:strokeColor="?attr/iconsColor" | ||||
android:strokeLineCap="round"/> | ||||
</vector> | ||||
| |
| @ -4,11 +4,11 @@ | |||
android:shape="rectangle"> | ||||
| ||||
<solid | ||||
android:color="?attr/inputBackgroundColor"> | ||||
android:color="?attr/materialCardBackgroundColor"> | ||||
</solid> | ||||
| ||||
<corners | ||||
android:radius="3dp"> | ||||
android:radius="32dp"> | ||||
</corners> | ||||
| ||||
<padding | ||||
| |
| @ -84,6 +84,17 @@ | |||
| ||||
</com.google.android.material.textfield.TextInputLayout> | ||||
| ||||
<TextView | ||||
android:id="@+id/insertNote" | ||||
android:layout_width="wrap_content" | ||||
android:layout_height="wrap_content" | ||||
android:layout_gravity="end" | ||||
android:layout_marginTop="@dimen/dimen8dp" | ||||
android:layout_marginBottom="@dimen/dimen0dp" | ||||
android:text="@string/insertNote" | ||||
android:textColor="?attr/primaryTextColor" | ||||
android:textSize="@dimen/dimen14sp"/> | ||||
| ||||
<com.google.android.material.textfield.TextInputLayout | ||||
android:id="@+id/newIssueDescriptionLayout" | ||||
android:layout_width="match_parent" | ||||
| |
| @ -84,6 +84,17 @@ | |||
| ||||
</com.google.android.material.textfield.TextInputLayout> | ||||
| ||||
<TextView | ||||
android:id="@+id/insertNote" | ||||
android:layout_width="wrap_content" | ||||
android:layout_height="wrap_content" | ||||
android:layout_gravity="end" | ||||
android:layout_marginTop="@dimen/dimen8dp" | ||||
android:layout_marginBottom="@dimen/dimen0dp" | ||||
android:text="@string/insertNote" | ||||
android:textColor="?attr/primaryTextColor" | ||||
android:textSize="@dimen/dimen14sp"/> | ||||
| ||||
<com.google.android.material.textfield.TextInputLayout | ||||
android:id="@+id/prBodyLayout" | ||||
android:layout_width="match_parent" | ||||
| |
| @ -99,6 +99,17 @@ | |||
| ||||
</com.google.android.material.textfield.TextInputLayout> | ||||
| ||||
<TextView | ||||
android:id="@+id/insertNote" | ||||
android:layout_width="wrap_content" | ||||
android:layout_height="wrap_content" | ||||
android:layout_gravity="end" | ||||
android:layout_marginTop="@dimen/dimen8dp" | ||||
android:layout_marginBottom="@dimen/dimen0dp" | ||||
android:text="@string/insertNote" | ||||
android:textColor="?attr/primaryTextColor" | ||||
android:textSize="@dimen/dimen14sp"/> | ||||
| ||||
<com.google.android.material.textfield.TextInputLayout | ||||
android:id="@+id/releaseContentLayout" | ||||
android:layout_width="match_parent" | ||||
| |
| @ -63,6 +63,7 @@ | |||
android:text="@string/commentButtonText" | ||||
android:textColor="?attr/materialCardBackgroundColor" | ||||
app:iconTint="?attr/materialCardBackgroundColor" | ||||
android:visibility="gone" | ||||
app:icon="@drawable/ic_reply" /> | ||||
| ||||
<RelativeLayout | ||||
| @ -89,7 +90,7 @@ | |||
android:paddingStart="@dimen/dimen8dp" | ||||
android:paddingTop="@dimen/dimen2dp" | ||||
android:paddingEnd="@dimen/dimen8dp" | ||||
android:paddingBottom="@dimen/dimen8dp"> | ||||
android:paddingBottom="@dimen/dimen104dp"> | ||||
| ||||
<com.google.android.material.card.MaterialCardView | ||||
android:id="@+id/titleCard" | ||||
| @ -153,7 +154,8 @@ | |||
android:layout_width="@dimen/dimen24dp" | ||||
android:layout_height="@dimen/dimen24dp" | ||||
app:cardCornerRadius="@dimen/dimen12dp" | ||||
app:cardElevation="@dimen/dimen0dp"> | ||||
app:cardElevation="@dimen/dimen0dp" | ||||
tools:ignore="TooDeepLayout"> | ||||
| ||||
<ImageView | ||||
android:id="@+id/assigneeAvatar" | ||||
| @ -371,9 +373,7 @@ | |||
<androidx.recyclerview.widget.RecyclerView | ||||
android:id="@+id/recyclerView" | ||||
android:layout_width="match_parent" | ||||
android:layout_height="wrap_content" | ||||
android:clipToPadding="false" | ||||
android:paddingBottom="@dimen/dimen72dp" /> | ||||
android:layout_height="wrap_content" /> | ||||
| ||||
</FrameLayout> | ||||
| ||||
| @ -383,6 +383,56 @@ | |||
| ||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout> | ||||
| ||||
<LinearLayout | ||||
android:layout_width="match_parent" | ||||
android:layout_height="wrap_content" | ||||
android:orientation="horizontal" | ||||
android:layout_alignParentBottom="true" | ||||
android:background="?attr/primaryBackgroundColor" | ||||
android:padding="@dimen/dimen8dp"> | ||||
| ||||
<EditText | ||||
android:id="@+id/comment_reply" | ||||
android:layout_width="0dp" | ||||
android:layout_height="wrap_content" | ||||
android:autofillHints="@string/commentButtonText" | ||||
android:background="@drawable/shape_inputs" | ||||
android:inputType="textMultiLine|textImeMultiLine|textCapSentences" | ||||
android:labelFor="@+id/comment_reply" | ||||
android:layout_weight="1" | ||||
android:maxLines="4" | ||||
android:paddingStart="@dimen/dimen16dp" | ||||
android:paddingEnd="@dimen/dimen16dp" | ||||
android:paddingTop="@dimen/dimen8dp" | ||||
android:paddingBottom="@dimen/dimen8dp" | ||||
android:textColor="?attr/inputTextColor" | ||||
android:layout_marginEnd="@dimen/dimen12dp" | ||||
android:textSize="@dimen/dimen14sp" /> | ||||
| ||||
<com.google.android.material.card.MaterialCardView | ||||
style="?attr/materialCardViewFilledStyle" | ||||
android:id="@+id/send_button" | ||||
android:layout_width="@dimen/dimen36dp" | ||||
android:layout_height="@dimen/dimen36dp" | ||||
android:layout_gravity="center_vertical" | ||||
android:backgroundTint="?attr/fabColor" | ||||
app:cardCornerRadius="@dimen/dimen36dp"> | ||||
| ||||
<ImageView | ||||
android:id="@+id/send" | ||||
android:layout_width="@dimen/dimen24dp" | ||||
android:layout_height="@dimen/dimen24dp" | ||||
android:layout_gravity="center_vertical|center_horizontal" | ||||
android:contentDescription="@string/generalImgContentText" | ||||
app:tint="?attr/materialCardBackgroundColor" | ||||
android:clickable="true" | ||||
android:src="@drawable/ic_send" | ||||
android:focusable="true" /> | ||||
| ||||
</com.google.android.material.card.MaterialCardView> | ||||
| ||||
</LinearLayout> | ||||
| ||||
</RelativeLayout> | ||||
| ||||
</androidx.coordinatorlayout.widget.CoordinatorLayout> | ||||
| |
| @ -138,6 +138,7 @@ | |||
android:layout_width="match_parent" | ||||
android:layout_height="wrap_content" | ||||
android:layout_marginTop="@dimen/dimen32dp" | ||||
android:visibility="gone" | ||||
android:orientation="vertical"> | ||||
| ||||
<LinearLayout | ||||
| |
7 app/src/main/res/layout/activity_unlock.xml Normal file
7
app/src/main/res/layout/activity_unlock.xml Normal file | @ -0,0 +1,7 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | ||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
android:layout_width="match_parent" | ||||
android:layout_height="match_parent" | ||||
android:orientation="vertical"> | ||||
| ||||
</androidx.coordinatorlayout.widget.CoordinatorLayout> |
| @ -29,7 +29,6 @@ | |||
android:layout_marginBottom="8dp" | ||||
android:hint="@string/title" | ||||
android:textColorHint="?attr/hintColor" | ||||
app:boxBackgroundColor="?attr/inputBackgroundColor" | ||||
app:boxStrokeErrorColor="@color/darkRed" | ||||
app:hintTextColor="?attr/hintColor"> | ||||
| ||||
| @ -51,7 +50,6 @@ | |||
android:layout_marginBottom="8dp" | ||||
android:hint="@string/sshKey" | ||||
android:textColorHint="?attr/hintColor" | ||||
app:boxBackgroundColor="?attr/inputBackgroundColor" | ||||
app:boxStrokeErrorColor="@color/darkRed" | ||||
app:hintTextColor="?attr/hintColor"> | ||||
| ||||
| |
35 app/src/main/res/layout/custom_insert_note.xml Normal file
35
app/src/main/res/layout/custom_insert_note.xml Normal file | @ -0,0 +1,35 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | ||||
<androidx.coordinatorlayout.widget.CoordinatorLayout | ||||
xmlns:android="http://schemas.android.com/apk/res/android" | ||||
android:layout_width="match_parent" | ||||
android:layout_height="match_parent" | ||||
xmlns:app="http://schemas.android.com/apk/res-auto"> | ||||
| ||||
<FrameLayout | ||||
android:layout_width="match_parent" | ||||
android:layout_height="match_parent" | ||||
android:padding="@dimen/dimen16dp"> | ||||
| ||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout | ||||
android:id="@+id/pullToRefresh" | ||||
android:layout_width="match_parent" | ||||
android:layout_height="wrap_content"> | ||||
| ||||
<androidx.recyclerview.widget.RecyclerView | ||||
android:id="@+id/recyclerView" | ||||
android:layout_width="match_parent" | ||||
android:layout_height="match_parent"/> | ||||
| ||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout> | ||||
| ||||
</FrameLayout> | ||||
| ||||
<com.google.android.material.progressindicator.LinearProgressIndicator | ||||
android:id="@+id/progressBar" | ||||
android:layout_width="match_parent" | ||||
android:layout_height="wrap_content" | ||||
android:indeterminate="true" | ||||
style="@style/Widget.Material3.LinearProgressIndicator" | ||||
app:indicatorColor="?attr/progressIndicatorColor" /> | ||||
| ||||
</androidx.coordinatorlayout.widget.CoordinatorLayout> |
| @ -58,6 +58,7 @@ | |||
| ||||
<item android:id="@+id/nav_comments_draft" | ||||
android:icon="@drawable/ic_drafts" | ||||
android:visible="false" | ||||
android:title="@string/titleDrafts"/> | ||||
| ||||
<item android:id="@+id/nav_profile" | ||||
| |
| @ -853,6 +853,8 @@ | |||
<item quantity="other">Notes deleted successfully</item> | ||||
</plurals> | ||||
<string name="notesAllDeletionMessage">This will delete all of your notes. This action cannot be undone.</string> | ||||
<string name="insertNote">Insert a Note</string> | ||||
<string name="noNotes">No notes found</string> | ||||
| ||||
<!-- timeline --> | ||||
<string name="commitsText">commit</string> | ||||
| |
Loading…
Add table
Add a link
Reference in a new issue