mirror of
https://codeberg.org/gitnex/GitNex.git
synced 2024-12-26 16:04:07 +08:00
Notifications (#554)
Cleanup Extending and improving notifications Using new icons instead Lowering polling delay to one minute and other improvements Fixing minor issues Simplifying progress layout Fixing bugs and other improvements Adding translations Notifications Co-authored-by: opyale <opyale@noreply.gitea.io> Co-authored-by: 6543 <6543@noreply.codeberg.org> Reviewed-on: https://codeberg.org/gitnex/GitNex/pulls/554 Reviewed-by: 6543 <6543@noreply.codeberg.org> Reviewed-by: M M Arif <mmarif@noreply.codeberg.org>
This commit is contained in:
parent
cd55f946f0
commit
39ac49b258
@ -9,7 +9,6 @@ max_line_length = 150
|
||||
|
||||
[*.java]
|
||||
indent_style = tab
|
||||
max_line_length = 220
|
||||
line_comment = //
|
||||
block_comment_start = /*
|
||||
block_comment = *
|
||||
|
7
.idea/codeStyles/Project.xml
generated
7
.idea/codeStyles/Project.xml
generated
@ -29,6 +29,7 @@
|
||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
||||
<option name="CATCH_ON_NEW_LINE" value="true" />
|
||||
<option name="FINALLY_ON_NEW_LINE" value="true" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
|
||||
<option name="SPACE_BEFORE_IF_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_WHILE_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_FOR_PARENTHESES" value="false" />
|
||||
@ -36,9 +37,13 @@
|
||||
<option name="SPACE_BEFORE_CATCH_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_SWITCH_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_SYNCHRONIZED_PARENTHESES" value="false" />
|
||||
<option name="CALL_PARAMETERS_WRAP" value="1" />
|
||||
<option name="METHOD_PARAMETERS_WRAP" value="1" />
|
||||
<option name="THROWS_LIST_WRAP" value="1" />
|
||||
<option name="METHOD_CALL_CHAIN_WRAP" value="1" />
|
||||
<option name="TERNARY_OPERATION_WRAP" value="1" />
|
||||
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
|
||||
<option name="IF_BRACE_FORCE" value="3" />
|
||||
<option name="WRAP_ON_TYPING" value="1" />
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
|
@ -37,7 +37,8 @@ configurations {
|
||||
|
||||
dependencies {
|
||||
def lifecycle_version = "2.3.0-alpha05"
|
||||
def markwon_version = '4.4.0'
|
||||
def markwon_version = "4.4.0"
|
||||
def work_version = "2.3.4"
|
||||
def acra = "5.5.0"
|
||||
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
@ -80,6 +81,7 @@ dependencies {
|
||||
implementation "com.hendraanggrian.appcompat:socialview-commons:0.2"
|
||||
implementation "com.github.HamidrezaAmz:BreadcrumbsView:0.2.9"
|
||||
implementation "commons-io:commons-io:20030203.000550"
|
||||
implementation "org.apache.commons:commons-lang3:3.10"
|
||||
implementation "com.github.chrisbanes:PhotoView:2.3.0"
|
||||
implementation "com.github.barteksc:android-pdf-viewer:3.2.0-beta.1"
|
||||
implementation "ch.acra:acra-mail:$acra"
|
||||
@ -87,6 +89,8 @@ dependencies {
|
||||
implementation "ch.acra:acra-notification:$acra"
|
||||
implementation "androidx.room:room-runtime:2.2.5"
|
||||
annotationProcessor "androidx.room:room-compiler:2.2.5"
|
||||
implementation "androidx.work:work-runtime:$work_version"
|
||||
implementation "com.eightbitlab:blurview:1.6.3"
|
||||
implementation "io.mikael:urlbuilder:2.0.9"
|
||||
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
package="org.mian.gitnex">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
<application
|
||||
@ -14,6 +15,7 @@
|
||||
android:roundIcon="@mipmap/app_logo_round"
|
||||
android:supportsRtl="true"
|
||||
tools:targetApi="n">
|
||||
|
||||
<activity android:name=".activities.MergePullRequestActivity" />
|
||||
<activity
|
||||
android:name=".activities.FileViewActivity"
|
||||
@ -81,6 +83,7 @@
|
||||
<activity android:name=".activities.SettingsReportsActivity" />
|
||||
<activity android:name=".activities.AddNewTeamMemberActivity" />
|
||||
<activity android:name=".activities.SettingsDraftsActivity" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
@ -0,0 +1,59 @@
|
||||
package org.mian.gitnex.actions;
|
||||
|
||||
import android.content.Context;
|
||||
import org.mian.gitnex.clients.RetrofitClient;
|
||||
import org.mian.gitnex.helpers.AppUtil;
|
||||
import org.mian.gitnex.helpers.TinyDB;
|
||||
import org.mian.gitnex.models.NotificationThread;
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import okhttp3.ResponseBody;
|
||||
import retrofit2.Call;
|
||||
|
||||
/**
|
||||
* Author opyale
|
||||
*/
|
||||
|
||||
public class NotificationsActions {
|
||||
|
||||
public enum NotificationStatus {READ, UNREAD, PINNED}
|
||||
|
||||
private TinyDB tinyDB;
|
||||
private Context context;
|
||||
private String instanceUrl;
|
||||
private String instanceToken;
|
||||
|
||||
public NotificationsActions(Context context) {
|
||||
|
||||
this.context = context;
|
||||
this.tinyDB = new TinyDB(context);
|
||||
|
||||
String loginUid = tinyDB.getString("loginUid");
|
||||
|
||||
instanceUrl = tinyDB.getString("instanceUrl");
|
||||
instanceToken = "token " + tinyDB.getString(loginUid + "-token");
|
||||
|
||||
}
|
||||
|
||||
public void setNotificationStatus(NotificationThread notificationThread, NotificationStatus notificationStatus) throws IOException {
|
||||
|
||||
Call<ResponseBody> call = RetrofitClient.getInstance(instanceUrl, context).getApiInterface()
|
||||
.markNotificationThreadAsRead(instanceToken, notificationThread.getId(), notificationStatus.name());
|
||||
|
||||
if(!call.execute().isSuccessful()) {
|
||||
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean setAllNotificationsRead(Date date) throws IOException {
|
||||
|
||||
Call<ResponseBody> call = RetrofitClient.getInstance(instanceUrl, context).getApiInterface()
|
||||
.markNotificationThreadsAsRead(instanceToken, AppUtil.getTimestampFromDate(context, date), true,
|
||||
new String[]{"unread", "pinned"}, "read");
|
||||
|
||||
return call.execute().isSuccessful();
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package org.mian.gitnex.activities;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import org.acra.ACRA;
|
||||
@ -14,6 +15,7 @@ import org.mian.gitnex.helpers.AppUtil;
|
||||
import org.mian.gitnex.helpers.FontsOverride;
|
||||
import org.mian.gitnex.helpers.TimeHelper;
|
||||
import org.mian.gitnex.helpers.TinyDB;
|
||||
import org.mian.gitnex.notifications.NotificationsMaster;
|
||||
|
||||
/**
|
||||
* Author M M Arif
|
||||
@ -26,10 +28,13 @@ import org.mian.gitnex.helpers.TinyDB;
|
||||
|
||||
public abstract class BaseActivity extends AppCompatActivity {
|
||||
|
||||
private Context appCtx;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
||||
final TinyDB tinyDb = new TinyDB(getApplicationContext());
|
||||
appCtx = getApplicationContext();
|
||||
final TinyDB tinyDb = new TinyDB(appCtx);
|
||||
|
||||
switch(tinyDb.getInt("themeId")) {
|
||||
|
||||
@ -83,11 +88,17 @@ public abstract class BaseActivity extends AppCompatActivity {
|
||||
|
||||
}
|
||||
|
||||
// enabling counter badges by default
|
||||
if(tinyDb.getString("enableCounterBadgesInit").isEmpty()) {
|
||||
tinyDb.putBoolean("enableCounterBadges", true);
|
||||
tinyDb.putString("enableCounterBadgesInit", "yes");
|
||||
}
|
||||
if(tinyDb.getInt("pollingDelayMinutes") == 0) {
|
||||
tinyDb.putInt("pollingDelayMinutes", 15);
|
||||
}
|
||||
|
||||
NotificationsMaster.hireWorker(appCtx);
|
||||
|
||||
// enabling counter badges by default
|
||||
if(tinyDb.getString("enableCounterBadgesInit").isEmpty()) {
|
||||
tinyDb.putBoolean("enableCounterBadges", true);
|
||||
tinyDb.putString("enableCounterBadgesInit", "yes");
|
||||
}
|
||||
|
||||
// enable crash reports by default
|
||||
if(tinyDb.getString("crashReportingEnabledInit").isEmpty()) {
|
||||
|
@ -121,7 +121,7 @@ public class EditIssueActivity extends BaseActivity implements View.OnClickListe
|
||||
|
||||
if(!tinyDb.getString("issueNumber").isEmpty()) {
|
||||
|
||||
if(tinyDb.getString("issueType").equals("pr")) {
|
||||
if(tinyDb.getString("issueType").equalsIgnoreCase("Pull")) {
|
||||
toolbar_title.setText(getString(R.string.editPrNavHeader, String.valueOf(issueIndex)));
|
||||
}
|
||||
else {
|
||||
@ -266,7 +266,7 @@ public class EditIssueActivity extends BaseActivity implements View.OnClickListe
|
||||
|
||||
if(response.code() == 201) {
|
||||
|
||||
if(tinyDb.getString("issueType").equals("pr")) {
|
||||
if(tinyDb.getString("issueType").equalsIgnoreCase("Pull")) {
|
||||
Toasty.info(ctx, getString(R.string.editPrSuccessMessage));
|
||||
}
|
||||
else {
|
||||
|
@ -25,9 +25,7 @@ import android.widget.RelativeLayout;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
@ -41,6 +39,7 @@ import org.mian.gitnex.clients.PicassoService;
|
||||
import org.mian.gitnex.clients.RetrofitClient;
|
||||
import org.mian.gitnex.fragments.BottomSheetSingleIssueFragment;
|
||||
import org.mian.gitnex.helpers.AlertDialogs;
|
||||
import org.mian.gitnex.helpers.AppUtil;
|
||||
import org.mian.gitnex.helpers.Authorization;
|
||||
import org.mian.gitnex.helpers.ClickListener;
|
||||
import org.mian.gitnex.helpers.ColorInverter;
|
||||
@ -50,7 +49,6 @@ import org.mian.gitnex.helpers.TimeHelper;
|
||||
import org.mian.gitnex.helpers.TinyDB;
|
||||
import org.mian.gitnex.helpers.UserMentions;
|
||||
import org.mian.gitnex.helpers.Version;
|
||||
import org.mian.gitnex.models.IssueComments;
|
||||
import org.mian.gitnex.models.Issues;
|
||||
import org.mian.gitnex.models.WatchInfo;
|
||||
import org.mian.gitnex.viewmodels.IssueCommentsViewModel;
|
||||
@ -58,7 +56,6 @@ import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import io.noties.markwon.AbstractMarkwonPlugin;
|
||||
@ -191,7 +188,9 @@ public class IssueDetailActivity extends BaseActivity {
|
||||
swipeRefresh.setOnRefreshListener(() -> new Handler().postDelayed(() -> {
|
||||
|
||||
swipeRefresh.setRefreshing(false);
|
||||
IssueCommentsViewModel.loadIssueComments(instanceUrl, Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, issueIndex, ctx);
|
||||
IssueCommentsViewModel
|
||||
.loadIssueComments(instanceUrl, Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, issueIndex,
|
||||
ctx);
|
||||
|
||||
}, 500));
|
||||
|
||||
@ -265,7 +264,9 @@ public class IssueDetailActivity extends BaseActivity {
|
||||
if(tinyDb.getBoolean("commentPosted")) {
|
||||
scrollViewComments.post(() -> {
|
||||
|
||||
IssueCommentsViewModel.loadIssueComments(instanceUrl, Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, issueIndex, ctx);
|
||||
IssueCommentsViewModel
|
||||
.loadIssueComments(instanceUrl, Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, issueIndex,
|
||||
ctx);
|
||||
|
||||
new Handler().postDelayed(() -> scrollViewComments.fullScroll(ScrollView.FOCUS_DOWN), 1000);
|
||||
|
||||
@ -277,7 +278,9 @@ public class IssueDetailActivity extends BaseActivity {
|
||||
if(tinyDb.getBoolean("commentEdited")) {
|
||||
scrollViewComments.post(() -> {
|
||||
|
||||
IssueCommentsViewModel.loadIssueComments(instanceUrl, Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, issueIndex, ctx);
|
||||
IssueCommentsViewModel
|
||||
.loadIssueComments(instanceUrl, Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, issueIndex,
|
||||
ctx);
|
||||
tinyDb.putBoolean("commentEdited", false);
|
||||
|
||||
});
|
||||
@ -315,12 +318,11 @@ public class IssueDetailActivity extends BaseActivity {
|
||||
|
||||
IssueCommentsViewModel issueCommentsModel = new ViewModelProvider(this).get(IssueCommentsViewModel.class);
|
||||
|
||||
issueCommentsModel.getIssueCommentList(instanceUrl, Authorization.returnAuthentication(ctx, loginUid, instanceToken), owner, repo, index, ctx).observe(this, new Observer<List<IssueComments>>() {
|
||||
|
||||
@Override
|
||||
public void onChanged(@Nullable List<IssueComments> issueCommentsMain) {
|
||||
issueCommentsModel.getIssueCommentList(instanceUrl, Authorization.returnAuthentication(ctx, loginUid, instanceToken), owner, repo, index, ctx)
|
||||
.observe(this, issueCommentsMain -> {
|
||||
|
||||
assert issueCommentsMain != null;
|
||||
|
||||
if(issueCommentsMain.size() > 0) {
|
||||
divider.setVisibility(View.VISIBLE);
|
||||
}
|
||||
@ -328,15 +330,15 @@ public class IssueDetailActivity extends BaseActivity {
|
||||
adapter = new IssueCommentsAdapter(ctx, issueCommentsMain);
|
||||
mRecyclerView.setAdapter(adapter);
|
||||
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void getSingleIssue(String instanceUrl, String instanceToken, String repoOwner, String repoName, int issueIndex, String loginUid) {
|
||||
|
||||
final TinyDB tinyDb = new TinyDB(appCtx);
|
||||
Call<Issues> call = RetrofitClient.getInstance(instanceUrl, ctx).getApiInterface().getIssueByIndex(Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, issueIndex);
|
||||
Call<Issues> call = RetrofitClient.getInstance(instanceUrl, ctx).getApiInterface()
|
||||
.getIssueByIndex(Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, issueIndex);
|
||||
|
||||
call.enqueue(new Callback<Issues>() {
|
||||
|
||||
@ -348,43 +350,47 @@ public class IssueDetailActivity extends BaseActivity {
|
||||
Issues singleIssue = response.body();
|
||||
assert singleIssue != null;
|
||||
|
||||
final Markwon markwon = Markwon.builder(Objects.requireNonNull(ctx)).usePlugin(CorePlugin.create()).usePlugin(ImagesPlugin.create(plugin -> {
|
||||
plugin.addSchemeHandler(new SchemeHandler() {
|
||||
final Markwon markwon = Markwon.builder(Objects.requireNonNull(ctx)).usePlugin(CorePlugin.create())
|
||||
.usePlugin(ImagesPlugin.create(plugin -> {
|
||||
plugin.addSchemeHandler(new SchemeHandler() {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ImageItem handle(@NonNull String raw, @NonNull Uri uri) {
|
||||
|
||||
final int resourceId = ctx.getResources()
|
||||
.getIdentifier(raw.substring("drawable://".length()), "drawable", ctx.getPackageName());
|
||||
|
||||
final Drawable drawable = ctx.getDrawable(resourceId);
|
||||
|
||||
assert drawable != null;
|
||||
return ImageItem.withResult(drawable);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Collection<String> supportedSchemes() {
|
||||
|
||||
return Collections.singleton("drawable");
|
||||
}
|
||||
});
|
||||
plugin.placeholderProvider(drawable -> null);
|
||||
plugin.addMediaDecoder(GifMediaDecoder.create(false));
|
||||
plugin.addMediaDecoder(SvgMediaDecoder.create(ctx.getResources()));
|
||||
plugin.addMediaDecoder(SvgMediaDecoder.create());
|
||||
plugin.defaultMediaDecoder(DefaultMediaDecoder.create(ctx.getResources()));
|
||||
plugin.defaultMediaDecoder(DefaultMediaDecoder.create());
|
||||
|
||||
})).usePlugin(new AbstractMarkwonPlugin() {
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ImageItem handle(@NonNull String raw, @NonNull Uri uri) {
|
||||
public void configureTheme(@NonNull MarkwonTheme.Builder builder) {
|
||||
|
||||
final int resourceId = ctx.getResources().getIdentifier(raw.substring("drawable://".length()), "drawable", ctx.getPackageName());
|
||||
|
||||
final Drawable drawable = ctx.getDrawable(resourceId);
|
||||
|
||||
assert drawable != null;
|
||||
return ImageItem.withResult(drawable);
|
||||
builder.codeTextColor(tinyDb.getInt("codeBlockColor")).codeBackgroundColor(tinyDb.getInt("codeBlockBackground"))
|
||||
.linkColor(getResources().getColor(R.color.lightBlue));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Collection<String> supportedSchemes() {
|
||||
|
||||
return Collections.singleton("drawable");
|
||||
}
|
||||
});
|
||||
plugin.placeholderProvider(drawable -> null);
|
||||
plugin.addMediaDecoder(GifMediaDecoder.create(false));
|
||||
plugin.addMediaDecoder(SvgMediaDecoder.create(ctx.getResources()));
|
||||
plugin.addMediaDecoder(SvgMediaDecoder.create());
|
||||
plugin.defaultMediaDecoder(DefaultMediaDecoder.create(ctx.getResources()));
|
||||
plugin.defaultMediaDecoder(DefaultMediaDecoder.create());
|
||||
|
||||
})).usePlugin(new AbstractMarkwonPlugin() {
|
||||
|
||||
@Override
|
||||
public void configureTheme(@NonNull MarkwonTheme.Builder builder) {
|
||||
|
||||
builder.codeTextColor(tinyDb.getInt("codeBlockColor")).codeBackgroundColor(tinyDb.getInt("codeBlockBackground")).linkColor(getResources().getColor(R.color.lightBlue));
|
||||
}
|
||||
}).usePlugin(TablePlugin.create(ctx)).usePlugin(TaskListPlugin.create(ctx)).usePlugin(HtmlPlugin.create()).usePlugin(StrikethroughPlugin.create()).usePlugin(LinkifyPlugin.create()).build();
|
||||
}).usePlugin(TablePlugin.create(ctx)).usePlugin(TaskListPlugin.create(ctx)).usePlugin(HtmlPlugin.create())
|
||||
.usePlugin(StrikethroughPlugin.create()).usePlugin(LinkifyPlugin.create()).build();
|
||||
|
||||
TinyDB tinyDb = new TinyDB(appCtx);
|
||||
final String locale = tinyDb.getString("locale");
|
||||
@ -392,8 +398,10 @@ public class IssueDetailActivity extends BaseActivity {
|
||||
tinyDb.putString("issueState", singleIssue.getState());
|
||||
tinyDb.putString("issueTitle", singleIssue.getTitle());
|
||||
|
||||
PicassoService.getInstance(ctx).get().load(singleIssue.getUser().getAvatar_url()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(120, 120).centerCrop().into(assigneeAvatar);
|
||||
String issueNumber_ = "<font color='" + appCtx.getResources().getColor(R.color.lightGray) + "'>" + appCtx.getResources().getString(R.string.hash) + singleIssue.getNumber() + "</font>";
|
||||
PicassoService.getInstance(ctx).get().load(singleIssue.getUser().getAvatar_url()).placeholder(R.drawable.loader_animated)
|
||||
.transform(new RoundedTransformation(8, 0)).resize(120, 120).centerCrop().into(assigneeAvatar);
|
||||
String issueNumber_ = "<font color='" + appCtx.getResources().getColor(R.color.lightGray) + "'>" + appCtx.getResources()
|
||||
.getString(R.string.hash) + singleIssue.getNumber() + "</font>";
|
||||
issueTitle.setText(Html.fromHtml(issueNumber_ + " " + singleIssue.getTitle()));
|
||||
String cleanIssueDescription = singleIssue.getBody().trim();
|
||||
Spanned bodyWithMD = markwon.toMarkdown(EmojiParser.parseToUnicode(cleanIssueDescription));
|
||||
@ -410,15 +418,19 @@ public class IssueDetailActivity extends BaseActivity {
|
||||
|
||||
ImageView assigneesView = new ImageView(ctx);
|
||||
|
||||
PicassoService.getInstance(ctx).get().load(singleIssue.getAssignees().get(i).getAvatar_url()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(100, 100).centerCrop().into(assigneesView);
|
||||
PicassoService.getInstance(ctx).get().load(singleIssue.getAssignees().get(i).getAvatar_url())
|
||||
.placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(100, 100).centerCrop()
|
||||
.into(assigneesView);
|
||||
|
||||
assigneesLayout.addView(assigneesView);
|
||||
assigneesView.setLayoutParams(params1);
|
||||
if(!singleIssue.getAssignees().get(i).getFull_name().equals("")) {
|
||||
assigneesView.setOnClickListener(new ClickListener(getString(R.string.assignedTo, singleIssue.getAssignees().get(i).getFull_name()), ctx));
|
||||
assigneesView.setOnClickListener(
|
||||
new ClickListener(getString(R.string.assignedTo, singleIssue.getAssignees().get(i).getFull_name()), ctx));
|
||||
}
|
||||
else {
|
||||
assigneesView.setOnClickListener(new ClickListener(getString(R.string.assignedTo, singleIssue.getAssignees().get(i).getLogin()), ctx));
|
||||
assigneesView.setOnClickListener(
|
||||
new ClickListener(getString(R.string.assignedTo, singleIssue.getAssignees().get(i).getLogin()), ctx));
|
||||
}
|
||||
|
||||
}
|
||||
@ -427,12 +439,13 @@ public class IssueDetailActivity extends BaseActivity {
|
||||
assigneesScrollView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
params.setMargins(0, 0, 15, 0);
|
||||
|
||||
if(singleIssue.getLabels() != null) {
|
||||
labelsScrollView.setVisibility(View.VISIBLE);
|
||||
int width = 25;
|
||||
|
||||
for(int i = 0; i < singleIssue.getLabels().size(); i++) {
|
||||
|
||||
String labelColor = singleIssue.getLabels().get(i).getColor();
|
||||
@ -444,9 +457,15 @@ public class IssueDetailActivity extends BaseActivity {
|
||||
labelsLayout.setGravity(Gravity.START | Gravity.TOP);
|
||||
labelsView.setLayoutParams(params);
|
||||
|
||||
TextDrawable drawable = TextDrawable.builder().beginConfig().useFont(Typeface.DEFAULT).textColor(new ColorInverter().getContrastColor(color)).fontSize(30).width(LabelWidthCalculator.calculateLabelWidth(labelName, Typeface.DEFAULT, 30, 15)).height(50).endConfig().buildRoundRect(labelName, color, 10);
|
||||
labelsView.setImageDrawable(drawable);
|
||||
int height = AppUtil.getPixelsFromDensity(ctx, 25);
|
||||
int textSize = AppUtil.getPixelsFromScaledDensity(ctx, 15);
|
||||
|
||||
TextDrawable drawable = TextDrawable.builder().beginConfig().useFont(Typeface.DEFAULT)
|
||||
.textColor(new ColorInverter().getContrastColor(color)).fontSize(textSize)
|
||||
.width(LabelWidthCalculator.calculateLabelWidth(labelName, Typeface.DEFAULT, textSize, AppUtil.getPixelsFromDensity(ctx, 10)))
|
||||
.height(height).endConfig().buildRoundRect(labelName, color, AppUtil.getPixelsFromDensity(ctx, 5));
|
||||
|
||||
labelsView.setImageDrawable(drawable);
|
||||
labelsLayout.addView(labelsView);
|
||||
|
||||
}
|
||||
@ -461,7 +480,8 @@ public class IssueDetailActivity extends BaseActivity {
|
||||
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", new Locale(locale));
|
||||
String dueDate = formatter.format(singleIssue.getDue_date());
|
||||
issueDueDate.setText(dueDate);
|
||||
issueDueDate.setOnClickListener(new ClickListener(TimeHelper.customDateFormatForToastDateFormat(singleIssue.getDue_date()), ctx));
|
||||
issueDueDate
|
||||
.setOnClickListener(new ClickListener(TimeHelper.customDateFormatForToastDateFormat(singleIssue.getDue_date()), ctx));
|
||||
}
|
||||
else if(timeFormat.equals("normal1")) {
|
||||
DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy", new Locale(locale));
|
||||
@ -481,7 +501,8 @@ public class IssueDetailActivity extends BaseActivity {
|
||||
edited = getString(R.string.colorfulBulletSpan) + getString(R.string.modifiedText);
|
||||
issueModified.setVisibility(View.VISIBLE);
|
||||
issueModified.setText(edited);
|
||||
issueModified.setOnClickListener(new ClickListener(TimeHelper.customDateFormatForToastDateFormat(singleIssue.getUpdated_at()), ctx));
|
||||
issueModified
|
||||
.setOnClickListener(new ClickListener(TimeHelper.customDateFormatForToastDateFormat(singleIssue.getUpdated_at()), ctx));
|
||||
}
|
||||
else {
|
||||
issueModified.setVisibility(View.INVISIBLE);
|
||||
@ -508,7 +529,8 @@ public class IssueDetailActivity extends BaseActivity {
|
||||
issueCreatedTime.setVisibility(View.VISIBLE);
|
||||
|
||||
if(timeFormat.equals("pretty")) {
|
||||
issueCreatedTime.setOnClickListener(new ClickListener(TimeHelper.customDateFormatForToastDateFormat(singleIssue.getCreated_at()), ctx));
|
||||
issueCreatedTime
|
||||
.setOnClickListener(new ClickListener(TimeHelper.customDateFormatForToastDateFormat(singleIssue.getCreated_at()), ctx));
|
||||
}
|
||||
|
||||
if(singleIssue.getMilestone() != null) {
|
||||
@ -519,10 +541,12 @@ public class IssueDetailActivity extends BaseActivity {
|
||||
}
|
||||
|
||||
if(!singleIssue.getUser().getFull_name().equals("")) {
|
||||
assigneeAvatar.setOnClickListener(new ClickListener(ctx.getResources().getString(R.string.issueCreator) + singleIssue.getUser().getFull_name(), ctx));
|
||||
assigneeAvatar.setOnClickListener(
|
||||
new ClickListener(ctx.getResources().getString(R.string.issueCreator) + singleIssue.getUser().getFull_name(), ctx));
|
||||
}
|
||||
else {
|
||||
assigneeAvatar.setOnClickListener(new ClickListener(ctx.getResources().getString(R.string.issueCreator) + singleIssue.getUser().getLogin(), ctx));
|
||||
assigneeAvatar.setOnClickListener(
|
||||
new ClickListener(ctx.getResources().getString(R.string.issueCreator) + singleIssue.getUser().getLogin(), ctx));
|
||||
}
|
||||
|
||||
progressBar.setVisibility(View.GONE);
|
||||
@ -531,7 +555,10 @@ public class IssueDetailActivity extends BaseActivity {
|
||||
|
||||
else if(response.code() == 401) {
|
||||
|
||||
AlertDialogs.authorizationTokenRevokedDialog(ctx, getResources().getString(R.string.alertDialogTokenRevokedTitle), getResources().getString(R.string.alertDialogTokenRevokedMessage), getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton), getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton));
|
||||
AlertDialogs.authorizationTokenRevokedDialog(ctx, getResources().getString(R.string.alertDialogTokenRevokedTitle),
|
||||
getResources().getString(R.string.alertDialogTokenRevokedMessage),
|
||||
getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton),
|
||||
getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton));
|
||||
|
||||
}
|
||||
|
||||
@ -547,7 +574,8 @@ public class IssueDetailActivity extends BaseActivity {
|
||||
|
||||
if(new Version(tinyDb.getString("giteaVersion")).higherOrEqual("1.12.0")) {
|
||||
|
||||
Call<WatchInfo> call2 = RetrofitClient.getInstance(instanceUrl, ctx).getApiInterface().checkIssueWatchStatus(Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, issueIndex);
|
||||
Call<WatchInfo> call2 = RetrofitClient.getInstance(instanceUrl, ctx).getApiInterface()
|
||||
.checkIssueWatchStatus(Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, issueIndex);
|
||||
|
||||
call2.enqueue(new Callback<WatchInfo>() {
|
||||
|
||||
|
@ -291,7 +291,7 @@ public class LoginActivity extends BaseActivity {
|
||||
try {
|
||||
gitea_version = new Version(version.getVersion());
|
||||
}
|
||||
catch(Error e) {
|
||||
catch(Exception e) {
|
||||
|
||||
SnackBar.error(ctx, layoutView, getResources().getString(R.string.versionUnknown));
|
||||
enableProcessButton();
|
||||
|
@ -35,6 +35,7 @@ import org.mian.gitnex.fragments.BottomSheetDraftsFragment;
|
||||
import org.mian.gitnex.fragments.DraftsFragment;
|
||||
import org.mian.gitnex.fragments.ExploreRepositoriesFragment;
|
||||
import org.mian.gitnex.fragments.MyRepositoriesFragment;
|
||||
import org.mian.gitnex.fragments.NotificationsFragment;
|
||||
import org.mian.gitnex.fragments.OrganizationsFragment;
|
||||
import org.mian.gitnex.fragments.ProfileFragment;
|
||||
import org.mian.gitnex.fragments.RepositoriesFragment;
|
||||
@ -49,10 +50,10 @@ import org.mian.gitnex.helpers.ColorInverter;
|
||||
import org.mian.gitnex.helpers.RoundedTransformation;
|
||||
import org.mian.gitnex.helpers.TinyDB;
|
||||
import org.mian.gitnex.helpers.Toasty;
|
||||
import org.mian.gitnex.helpers.Version;
|
||||
import org.mian.gitnex.models.GiteaVersion;
|
||||
import org.mian.gitnex.models.UserInfo;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import eightbitlab.com.blurview.BlurView;
|
||||
import eightbitlab.com.blurview.RenderScriptBlur;
|
||||
import retrofit2.Call;
|
||||
@ -90,7 +91,6 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig
|
||||
|
||||
final TinyDB tinyDb = new TinyDB(appCtx);
|
||||
tinyDb.putBoolean("noConnection", false);
|
||||
//userAvatar = findViewById(R.id.userAvatar);
|
||||
|
||||
Intent mainIntent = getIntent();
|
||||
String launchFragment = mainIntent.getStringExtra("launchFragment");
|
||||
@ -124,12 +124,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig
|
||||
}
|
||||
|
||||
String accountName = loginUid + "@" + instanceUrl;
|
||||
try {
|
||||
getAccountData(accountName);
|
||||
}
|
||||
catch(ExecutionException | InterruptedException e) {
|
||||
Log.e("getAccountData", e.toString());
|
||||
}
|
||||
getAccountData(accountName);
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
toolbarTitle = toolbar.findViewById(R.id.toolbar_title);
|
||||
@ -171,6 +166,9 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig
|
||||
else if(fragmentById instanceof ExploreRepositoriesFragment) {
|
||||
toolbarTitle.setText(getResources().getString(R.string.pageTitleExplore));
|
||||
}
|
||||
else if(fragmentById instanceof NotificationsFragment) {
|
||||
toolbarTitle.setText(R.string.pageTitleNotifications);
|
||||
}
|
||||
else if(fragmentById instanceof ProfileFragment) {
|
||||
toolbarTitle.setText(getResources().getString(R.string.pageTitleProfile));
|
||||
}
|
||||
@ -219,7 +217,10 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig
|
||||
userEmail.setTypeface(myTypeface);
|
||||
userFullName.setTypeface(myTypeface);
|
||||
|
||||
String currentVersion = tinyDb.getString("giteaVersion");
|
||||
|
||||
navigationView.getMenu().findItem(R.id.nav_administration).setVisible(tinyDb.getBoolean("userIsAdmin"));
|
||||
navigationView.getMenu().findItem(R.id.nav_notifications).setVisible(new Version(currentVersion).higherOrEqual("1.12.3"));
|
||||
|
||||
if(!userEmailNav.equals("")) {
|
||||
userEmail.setText(userEmailNav);
|
||||
@ -297,13 +298,22 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig
|
||||
|
||||
if(launchFragment != null) {
|
||||
|
||||
if(launchFragment.equals("drafts")) {
|
||||
mainIntent.removeExtra("launchFragment");
|
||||
|
||||
switch(launchFragment) {
|
||||
|
||||
case "drafts":
|
||||
toolbarTitle.setText(getResources().getString(R.string.titleDrafts));
|
||||
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new DraftsFragment()).commit();
|
||||
navigationView.setCheckedItem(R.id.nav_comments_draft);
|
||||
return;
|
||||
|
||||
case "notifications":
|
||||
toolbarTitle.setText(getResources().getString(R.string.pageTitleNotifications));
|
||||
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new NotificationsFragment()).commit();
|
||||
navigationView.setCheckedItem(R.id.nav_notifications);
|
||||
return;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -354,7 +364,6 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(!connToInternet) {
|
||||
@ -376,17 +385,22 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig
|
||||
|
||||
// Changelog popup
|
||||
int versionCode = 0;
|
||||
|
||||
try {
|
||||
|
||||
PackageInfo packageInfo = appCtx.getPackageManager().getPackageInfo(appCtx.getPackageName(), 0);
|
||||
versionCode = packageInfo.versionCode;
|
||||
}
|
||||
catch(PackageManager.NameNotFoundException e) {
|
||||
|
||||
Log.e("changelogDialog", Objects.requireNonNull(e.getMessage()));
|
||||
}
|
||||
|
||||
if(versionCode > tinyDb.getInt("versionCode")) {
|
||||
|
||||
tinyDb.putInt("versionCode", versionCode);
|
||||
tinyDb.putBoolean("versionFlag", true);
|
||||
|
||||
ChangeLog changelogDialog = new ChangeLog(this);
|
||||
changelogDialog.showDialog();
|
||||
}
|
||||
@ -428,7 +442,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig
|
||||
|
||||
}
|
||||
|
||||
public void getAccountData(String accountName) throws ExecutionException, InterruptedException {
|
||||
public void getAccountData(String accountName) {
|
||||
|
||||
UserAccountsApi accountData = new UserAccountsApi(ctx);
|
||||
UserAccount data = accountData.getAccountData(accountName);
|
||||
@ -440,7 +454,6 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig
|
||||
else {
|
||||
AlertDialogs.forceLogoutDialog(ctx, getResources().getString(R.string.forceLogoutDialogHeader), getResources().getString(R.string.forceLogoutDialogDescription), getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -509,6 +522,11 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig
|
||||
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new ExploreRepositoriesFragment()).commit();
|
||||
break;
|
||||
|
||||
case R.id.nav_notifications:
|
||||
toolbarTitle.setText(R.string.pageTitleNotifications);
|
||||
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new NotificationsFragment()).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();
|
||||
@ -583,14 +601,12 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig
|
||||
tinyDb.putString("giteaVersion", version.getVersion());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<GiteaVersion> callVersion, @NonNull Throwable t) {
|
||||
|
||||
Log.e("onFailure-version", t.toString());
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -7,6 +7,7 @@ import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.NumberPicker;
|
||||
import android.widget.TextView;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
@ -15,7 +16,9 @@ import org.mian.gitnex.helpers.AppUtil;
|
||||
import org.mian.gitnex.helpers.FilesData;
|
||||
import org.mian.gitnex.helpers.TinyDB;
|
||||
import org.mian.gitnex.helpers.Toasty;
|
||||
import org.mian.gitnex.helpers.Version;
|
||||
import org.mian.gitnex.helpers.ssl.MemorizingTrustManager;
|
||||
import org.mian.gitnex.notifications.NotificationsMaster;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
@ -27,6 +30,8 @@ import java.util.HashSet;
|
||||
public class SettingsSecurityActivity extends BaseActivity {
|
||||
|
||||
private Context appCtx;
|
||||
private Context ctx = this;
|
||||
|
||||
private View.OnClickListener onClickListener;
|
||||
|
||||
private static String[] cacheSizeDataList = {"50 MB", "100 MB", "250 MB", "500 MB", "1 GB"};
|
||||
@ -35,6 +40,10 @@ public class SettingsSecurityActivity extends BaseActivity {
|
||||
private static String[] cacheSizeImagesList = {"50 MB", "100 MB", "250 MB", "500 MB", "1 GB"};
|
||||
private static int cacheSizeImagesSelectedChoice = 0;
|
||||
|
||||
private static int MINIMUM_POLLING_DELAY = 1;
|
||||
private static int DEFAULT_POLLING_DELAY = 20;
|
||||
private static int MAXIMUM_POLLING_DELAY = 720;
|
||||
|
||||
@Override
|
||||
protected int getLayoutResourceId() {
|
||||
|
||||
@ -48,6 +57,7 @@ public class SettingsSecurityActivity extends BaseActivity {
|
||||
appCtx = getApplicationContext();
|
||||
|
||||
TinyDB tinyDb = new TinyDB(appCtx);
|
||||
String currentVersion = tinyDb.getString("giteaVersion");
|
||||
|
||||
ImageView closeActivity = findViewById(R.id.close);
|
||||
|
||||
@ -57,8 +67,10 @@ public class SettingsSecurityActivity extends BaseActivity {
|
||||
TextView cacheSizeDataSelected = findViewById(R.id.cacheSizeDataSelected); // setter for data cache size
|
||||
TextView cacheSizeImagesSelected = findViewById(R.id.cacheSizeImagesSelected); // setter for images cache size
|
||||
TextView clearCacheSelected = findViewById(R.id.clearCacheSelected); // setter for clear cache
|
||||
TextView pollingDelaySelected = findViewById(R.id.pollingDelaySelected);
|
||||
|
||||
LinearLayout certsFrame = findViewById(R.id.certsFrame);
|
||||
LinearLayout pollingDelayFrame = findViewById(R.id.pollingDelayFrame);
|
||||
LinearLayout cacheSizeDataFrame = findViewById(R.id.cacheSizeDataSelectionFrame);
|
||||
LinearLayout cacheSizeImagesFrame = findViewById(R.id.cacheSizeImagesSelectionFrame);
|
||||
LinearLayout clearCacheFrame = findViewById(R.id.clearCacheSelectionFrame);
|
||||
@ -79,6 +91,12 @@ public class SettingsSecurityActivity extends BaseActivity {
|
||||
cacheSizeImagesSelectedChoice = tinyDb.getInt("cacheSizeImagesId");
|
||||
}
|
||||
|
||||
if(new Version(currentVersion).less("1.12.3")) {
|
||||
pollingDelayFrame.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
pollingDelaySelected.setText(String.format(getString(R.string.pollingDelaySelectedText), tinyDb.getInt("pollingDelayMinutes", DEFAULT_POLLING_DELAY)));
|
||||
|
||||
// clear cache setter
|
||||
File cacheDir = appCtx.getCacheDir();
|
||||
long size__ = FilesData.getFileSizeRecursively(new HashSet<>(), cacheDir);
|
||||
@ -190,7 +208,6 @@ public class SettingsSecurityActivity extends BaseActivity {
|
||||
tinyDb.putBoolean("loggedInMode", false);
|
||||
tinyDb.remove("basicAuthPassword");
|
||||
tinyDb.putBoolean("basicAuthFlag", false);
|
||||
//tinyDb.clear();
|
||||
|
||||
Intent loginActivityIntent = new Intent().setClass(appCtx, LoginActivity.class);
|
||||
loginActivityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
@ -203,12 +220,42 @@ public class SettingsSecurityActivity extends BaseActivity {
|
||||
|
||||
});
|
||||
|
||||
// polling delay
|
||||
pollingDelayFrame.setOnClickListener(v -> {
|
||||
|
||||
NumberPicker numberPicker = new NumberPicker(ctx);
|
||||
numberPicker.setMinValue(MINIMUM_POLLING_DELAY);
|
||||
numberPicker.setMaxValue(MAXIMUM_POLLING_DELAY);
|
||||
numberPicker.setValue(tinyDb.getInt("pollingDelayMinutes", DEFAULT_POLLING_DELAY));
|
||||
numberPicker.setWrapSelectorWheel(true);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
|
||||
builder.setTitle(getString(R.string.pollingDelayDialogHeaderText));
|
||||
builder.setMessage(getString(R.string.pollingDelayDialogDescriptionText));
|
||||
|
||||
builder.setCancelable(true);
|
||||
builder.setPositiveButton(getString(R.string.okButton), (dialog, which) -> {
|
||||
|
||||
tinyDb.putInt("pollingDelayMinutes", numberPicker.getValue());
|
||||
|
||||
NotificationsMaster.fireWorker(ctx);
|
||||
NotificationsMaster.hireWorker(ctx);
|
||||
|
||||
pollingDelaySelected.setText(String.format(getString(R.string.pollingDelaySelectedText), numberPicker.getValue()));
|
||||
Toasty.info(appCtx, getResources().getString(R.string.settingsSave));
|
||||
|
||||
});
|
||||
|
||||
builder.setNegativeButton(R.string.cancelButton, (dialog, which) -> dialog.dismiss());
|
||||
builder.setView(numberPicker);
|
||||
builder.create().show();
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void initCloseListener() {
|
||||
onClickListener = view -> {
|
||||
finish();
|
||||
};
|
||||
}
|
||||
|
||||
onClickListener = view -> finish();
|
||||
}
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ public class IssuesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
||||
|
||||
TinyDB tinyDb = new TinyDB(context);
|
||||
tinyDb.putString("issueNumber", issueNumber.getText().toString());
|
||||
tinyDb.putString("issueType", "issue");
|
||||
tinyDb.putString("issueType", "Issue");
|
||||
context.startActivity(intent);
|
||||
|
||||
});
|
||||
@ -138,7 +138,7 @@ public class IssuesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
|
||||
|
||||
TinyDB tinyDb = new TinyDB(context);
|
||||
tinyDb.putString("issueNumber", issueNumber.getText().toString());
|
||||
tinyDb.putString("issueType", "issue");
|
||||
tinyDb.putString("issueType", "Issue");
|
||||
context.startActivity(intent);
|
||||
|
||||
});
|
||||
|
@ -0,0 +1,125 @@
|
||||
package org.mian.gitnex.adapters;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.Html;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import org.mian.gitnex.R;
|
||||
import org.mian.gitnex.models.NotificationThread;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Author opyale
|
||||
*/
|
||||
|
||||
public class NotificationsAdapter extends RecyclerView.Adapter<NotificationsAdapter.NotificationsViewHolder> {
|
||||
|
||||
private Context context;
|
||||
private List<NotificationThread> notificationThreads;
|
||||
private OnMoreClickedListener onMoreClickedListener;
|
||||
private OnNotificationClickedListener onNotificationClickedListener;
|
||||
|
||||
public NotificationsAdapter(Context context, List<NotificationThread> notificationThreads, OnMoreClickedListener onMoreClickedListener, OnNotificationClickedListener onNotificationClickedListener) {
|
||||
|
||||
this.context = context;
|
||||
this.notificationThreads = notificationThreads;
|
||||
this.onMoreClickedListener = onMoreClickedListener;
|
||||
this.onNotificationClickedListener = onNotificationClickedListener;
|
||||
|
||||
}
|
||||
|
||||
static class NotificationsViewHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
private LinearLayout frame;
|
||||
private TextView subject;
|
||||
private TextView repository;
|
||||
private ImageView type;
|
||||
private ImageView pinned;
|
||||
private ImageView more;
|
||||
|
||||
public NotificationsViewHolder(@NonNull View itemView) {
|
||||
|
||||
super(itemView);
|
||||
|
||||
frame = itemView.findViewById(R.id.frame);
|
||||
subject = itemView.findViewById(R.id.subject);
|
||||
repository = itemView.findViewById(R.id.repository);
|
||||
type = itemView.findViewById(R.id.type);
|
||||
pinned = itemView.findViewById(R.id.pinned);
|
||||
more = itemView.findViewById(R.id.more);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public NotificationsViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
|
||||
View v = LayoutInflater.from(context).inflate(R.layout.list_notifications, parent, false);
|
||||
return new NotificationsAdapter.NotificationsViewHolder(v);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull NotificationsViewHolder holder, int position) {
|
||||
|
||||
NotificationThread notificationThread = notificationThreads.get(position);
|
||||
|
||||
String url = notificationThread.getSubject().getUrl();
|
||||
String subjectId = "<font color='" + context.getResources().getColor(R.color.lightGray) + "'>" + context.getResources()
|
||||
.getString(R.string.hash) + url.substring(url.lastIndexOf("/") + 1) + "</font>";
|
||||
|
||||
holder.subject.setText(Html.fromHtml(subjectId + " " + notificationThread.getSubject().getTitle()));
|
||||
holder.repository.setText(notificationThread.getRepository().getFullname());
|
||||
|
||||
if(notificationThread.isPinned()) {
|
||||
holder.pinned.setVisibility(View.VISIBLE);
|
||||
}
|
||||
else {
|
||||
holder.pinned.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
switch(notificationThread.getSubject().getType()) {
|
||||
|
||||
case "Pull":
|
||||
holder.type.setImageDrawable(context.getResources().getDrawable(R.drawable.ic_pull_request, null));
|
||||
break;
|
||||
|
||||
case "Issue":
|
||||
holder.type.setImageDrawable(context.getResources().getDrawable(R.drawable.ic_issue, null));
|
||||
break;
|
||||
|
||||
default:
|
||||
holder.type.setImageDrawable(context.getResources().getDrawable(R.drawable.ic_question, null));
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
holder.frame.setOnClickListener(v -> onNotificationClickedListener.onNotificationClicked(notificationThread));
|
||||
holder.more.setOnClickListener(v -> onMoreClickedListener.onMoreClicked(notificationThread));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
|
||||
return notificationThreads.size();
|
||||
}
|
||||
|
||||
public interface OnNotificationClickedListener {
|
||||
|
||||
void onNotificationClicked(NotificationThread notificationThread);
|
||||
}
|
||||
|
||||
public interface OnMoreClickedListener {
|
||||
|
||||
void onMoreClicked(NotificationThread notificationThread);
|
||||
}
|
||||
|
||||
}
|
@ -136,7 +136,7 @@ public class PullRequestsAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
|
||||
tinyDb.putString("prHeadBranch", prHeadBranch.getText().toString());
|
||||
tinyDb.putString("prIsFork", prIsFork.getText().toString());
|
||||
tinyDb.putString("prForkFullName", prForkFullName.getText().toString());
|
||||
tinyDb.putString("issueType", "pr");
|
||||
tinyDb.putString("issueType", "Pull");
|
||||
context.startActivity(intent);
|
||||
|
||||
});
|
||||
@ -155,7 +155,7 @@ public class PullRequestsAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
|
||||
tinyDb.putString("prHeadBranch", prHeadBranch.getText().toString());
|
||||
tinyDb.putString("prIsFork", prIsFork.getText().toString());
|
||||
tinyDb.putString("prForkFullName", prForkFullName.getText().toString());
|
||||
tinyDb.putString("issueType", "pr");
|
||||
tinyDb.putString("issueType", "Pull");
|
||||
context.startActivity(intent);
|
||||
|
||||
});
|
||||
|
@ -121,8 +121,6 @@ public class RepositoriesByOrgAdapter extends RecyclerView.Adapter<RepositoriesB
|
||||
final String instanceUrl = tinyDb.getString("instanceUrl");
|
||||
final String token = "token " + tinyDb.getString(tinyDb.getString("loginUid") + "-token");
|
||||
|
||||
WatchInfo watch = new WatchInfo();
|
||||
|
||||
Call<WatchInfo> call;
|
||||
|
||||
call = RetrofitClient.getInstance(instanceUrl, context).getApiInterface().checkRepoWatchStatus(token, repoOwner, repoName);
|
||||
|
@ -0,0 +1,81 @@
|
||||
package org.mian.gitnex.fragments;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
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.TinyDB;
|
||||
|
||||
/**
|
||||
* Author opyale
|
||||
*/
|
||||
|
||||
public class BottomSheetNotificationsFilterFragment extends BottomSheetDialogFragment {
|
||||
|
||||
private TinyDB tinyDB;
|
||||
private OnDismissedListener onDismissedListener;
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
|
||||
this.tinyDB = new TinyDB(context);
|
||||
super.onAttach(context);
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
|
||||
View view = inflater.inflate(R.layout.bottom_sheet_notifications_filter, container, false);
|
||||
|
||||
TextView readNotifications = view.findViewById(R.id.readNotifications);
|
||||
TextView unreadNotifications = view.findViewById(R.id.unreadNotifications);
|
||||
|
||||
readNotifications.setOnClickListener(v1 -> {
|
||||
|
||||
tinyDB.putString("notificationsFilterState", "read");
|
||||
dismiss();
|
||||
|
||||
});
|
||||
|
||||
unreadNotifications.setOnClickListener(v12 -> {
|
||||
|
||||
tinyDB.putString("notificationsFilterState", "unread");
|
||||
dismiss();
|
||||
|
||||
});
|
||||
|
||||
return view;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dismiss() {
|
||||
|
||||
if(onDismissedListener != null) {
|
||||
|
||||
onDismissedListener.onDismissed();
|
||||
}
|
||||
|
||||
super.dismiss();
|
||||
|
||||
}
|
||||
|
||||
public void setOnDismissedListener(OnDismissedListener onDismissedListener) {
|
||||
|
||||
this.onDismissedListener = onDismissedListener;
|
||||
}
|
||||
|
||||
public interface OnDismissedListener {
|
||||
|
||||
void onDismissed();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,147 @@
|
||||
package org.mian.gitnex.fragments;
|
||||
|
||||
import android.app.Activity;
|
||||
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.actions.NotificationsActions;
|
||||
import org.mian.gitnex.helpers.AppUtil;
|
||||
import org.mian.gitnex.helpers.Toasty;
|
||||
import org.mian.gitnex.models.NotificationThread;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Author opyale
|
||||
*/
|
||||
|
||||
public class BottomSheetNotificationsFragment extends BottomSheetDialogFragment {
|
||||
|
||||
private Context context;
|
||||
private NotificationThread notificationThread;
|
||||
private OnOptionSelectedListener onOptionSelectedListener;
|
||||
|
||||
public void onAttach(Context context, NotificationThread notificationThread, OnOptionSelectedListener onOptionSelectedListener) {
|
||||
|
||||
this.context = context;
|
||||
this.notificationThread = notificationThread;
|
||||
this.onOptionSelectedListener = onOptionSelectedListener;
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
|
||||
View v = inflater.inflate(R.layout.bottom_sheet_notifications, container, false);
|
||||
|
||||
TextView markRead = v.findViewById(R.id.markRead);
|
||||
TextView markUnread = v.findViewById(R.id.markUnread);
|
||||
TextView markPinned = v.findViewById(R.id.markPinned);
|
||||
|
||||
NotificationsActions notificationsActions = new NotificationsActions(context);
|
||||
Activity activity = Objects.requireNonNull(getActivity());
|
||||
|
||||
if(notificationThread.isPinned()) {
|
||||
|
||||
AppUtil.setMultiVisibility(View.GONE, markUnread, markPinned);
|
||||
} else if(notificationThread.isUnread()) {
|
||||
|
||||
markUnread.setVisibility(View.GONE);
|
||||
} else {
|
||||
|
||||
markRead.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
markPinned.setOnClickListener(v12 -> {
|
||||
|
||||
Thread thread = new Thread(() -> {
|
||||
|
||||
try {
|
||||
|
||||
notificationsActions.setNotificationStatus(notificationThread, NotificationsActions.NotificationStatus.PINNED);
|
||||
activity.runOnUiThread(() -> onOptionSelectedListener.onSelected());
|
||||
|
||||
}
|
||||
catch(Exception e) {
|
||||
|
||||
activity.runOnUiThread(() -> Toasty.error(context, getString(R.string.genericError)));
|
||||
Log.e("onError", e.toString());
|
||||
|
||||
} finally {
|
||||
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
thread.start();
|
||||
|
||||
});
|
||||
|
||||
markRead.setOnClickListener(v1 -> {
|
||||
|
||||
Thread thread = new Thread(() -> {
|
||||
|
||||
try {
|
||||
|
||||
notificationsActions.setNotificationStatus(notificationThread, NotificationsActions.NotificationStatus.READ);
|
||||
activity.runOnUiThread(() -> onOptionSelectedListener.onSelected());
|
||||
|
||||
}
|
||||
catch(Exception e) {
|
||||
|
||||
activity.runOnUiThread(() -> Toasty.error(context, getString(R.string.genericError)));
|
||||
Log.e("onError", e.toString());
|
||||
|
||||
} finally {
|
||||
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
thread.start();
|
||||
|
||||
});
|
||||
|
||||
markUnread.setOnClickListener(v13 -> {
|
||||
|
||||
Thread thread = new Thread(() -> {
|
||||
|
||||
try {
|
||||
|
||||
notificationsActions.setNotificationStatus(notificationThread, NotificationsActions.NotificationStatus.UNREAD);
|
||||
activity.runOnUiThread(() -> onOptionSelectedListener.onSelected());
|
||||
|
||||
}
|
||||
catch(Exception e) {
|
||||
|
||||
activity.runOnUiThread(() -> Toasty.error(context, getString(R.string.genericError)));
|
||||
Log.e("onError", e.toString());
|
||||
|
||||
} finally {
|
||||
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
thread.start();
|
||||
|
||||
});
|
||||
|
||||
return v;
|
||||
|
||||
}
|
||||
|
||||
public interface OnOptionSelectedListener {
|
||||
|
||||
void onSelected();
|
||||
}
|
||||
|
||||
}
|
@ -55,7 +55,7 @@ public class BottomSheetSingleIssueFragment extends BottomSheetDialogFragment {
|
||||
TextView subscribeIssue = v.findViewById(R.id.subscribeIssue);
|
||||
TextView unsubscribeIssue = v.findViewById(R.id.unsubscribeIssue);
|
||||
|
||||
if(tinyDB.getString("issueType").equals("pr")) {
|
||||
if(tinyDB.getString("issueType").equalsIgnoreCase("Pull")) {
|
||||
|
||||
editIssue.setText(R.string.editPrText);
|
||||
copyIssueUrl.setText(R.string.copyPrUrlText);
|
||||
@ -199,7 +199,7 @@ public class BottomSheetSingleIssueFragment extends BottomSheetDialogFragment {
|
||||
|
||||
});
|
||||
|
||||
if(tinyDB.getString("issueType").equals("issue")) {
|
||||
if(tinyDB.getString("issueType").equalsIgnoreCase("Issue")) {
|
||||
|
||||
if(tinyDB.getString("issueState").equals("open")) { // close issue
|
||||
|
||||
|
@ -0,0 +1,367 @@
|
||||
package org.mian.gitnex.fragments;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.mian.gitnex.R;
|
||||
import org.mian.gitnex.actions.NotificationsActions;
|
||||
import org.mian.gitnex.activities.IssueDetailActivity;
|
||||
import org.mian.gitnex.adapters.NotificationsAdapter;
|
||||
import org.mian.gitnex.clients.RetrofitClient;
|
||||
import org.mian.gitnex.helpers.AppUtil;
|
||||
import org.mian.gitnex.helpers.InfiniteScrollListener;
|
||||
import org.mian.gitnex.helpers.SnackBar;
|
||||
import org.mian.gitnex.helpers.StaticGlobalVariables;
|
||||
import org.mian.gitnex.helpers.TinyDB;
|
||||
import org.mian.gitnex.models.NotificationThread;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
/**
|
||||
* Author opyale
|
||||
*/
|
||||
|
||||
public class NotificationsFragment extends Fragment implements NotificationsAdapter.OnNotificationClickedListener, NotificationsAdapter.OnMoreClickedListener, BottomSheetNotificationsFragment.OnOptionSelectedListener {
|
||||
|
||||
private List<NotificationThread> notificationThreads;
|
||||
private NotificationsAdapter notificationsAdapter;
|
||||
private NotificationsActions notificationsActions;
|
||||
|
||||
private ImageView markAllAsRead;
|
||||
private ProgressBar progressBar;
|
||||
private RelativeLayout mainLayout;
|
||||
private ProgressBar loadingMoreView;
|
||||
private TextView noDataNotifications;
|
||||
private SwipeRefreshLayout pullToRefresh;
|
||||
|
||||
private Activity activity;
|
||||
private Context context;
|
||||
private TinyDB tinyDB;
|
||||
private Menu menu;
|
||||
|
||||
private int pageCurrentIndex = 1;
|
||||
private int pageResultLimit;
|
||||
private String currentFilterMode = "unread";
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
|
||||
View v = inflater.inflate(R.layout.fragment_notifications, container, false);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
activity = Objects.requireNonNull(getActivity());
|
||||
context = getContext();
|
||||
tinyDB = new TinyDB(context);
|
||||
|
||||
pageResultLimit = StaticGlobalVariables.getCurrentResultLimit(context);
|
||||
tinyDB.putString("notificationsFilterState", currentFilterMode);
|
||||
|
||||
mainLayout = v.findViewById(R.id.mainLayout);
|
||||
markAllAsRead = v.findViewById(R.id.markAllAsRead);
|
||||
noDataNotifications = v.findViewById(R.id.noDataNotifications);
|
||||
loadingMoreView = v.findViewById(R.id.loadingMoreView);
|
||||
progressBar = v.findViewById(R.id.progressBar);
|
||||
|
||||
notificationThreads = new ArrayList<>();
|
||||
notificationsActions = new NotificationsActions(context);
|
||||
notificationsAdapter = new NotificationsAdapter(context, notificationThreads, this, this);
|
||||
|
||||
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(context);
|
||||
|
||||
RecyclerView recyclerView = v.findViewById(R.id.notifications);
|
||||
recyclerView.setHasFixedSize(true);
|
||||
recyclerView.setLayoutManager(linearLayoutManager);
|
||||
recyclerView.setAdapter(notificationsAdapter);
|
||||
recyclerView.addOnScrollListener(new InfiniteScrollListener(pageResultLimit, linearLayoutManager) {
|
||||
|
||||
@Override
|
||||
public void onScrolledToEnd(int firstVisibleItemPosition) {
|
||||
|
||||
pageCurrentIndex++;
|
||||
loadNotifications(true);
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
|
||||
@Override
|
||||
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
|
||||
|
||||
if(currentFilterMode.equalsIgnoreCase("unread")) {
|
||||
|
||||
if(dy > 0 && markAllAsRead.isShown()) {
|
||||
|
||||
markAllAsRead.setVisibility(View.GONE);
|
||||
} else if(dy < 0) {
|
||||
|
||||
markAllAsRead.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
|
||||
|
||||
super.onScrollStateChanged(recyclerView, newState);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
markAllAsRead.setOnClickListener(v1 -> {
|
||||
|
||||
Thread thread = new Thread(() -> {
|
||||
|
||||
try {
|
||||
|
||||
if(notificationsActions.setAllNotificationsRead(new Date())) {
|
||||
|
||||
activity.runOnUiThread(() -> {
|
||||
|
||||
SnackBar.info(context, mainLayout, getString(R.string.markedNotificationsAsRead));
|
||||
loadNotifications(true);
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
catch(IOException e) {
|
||||
|
||||
activity.runOnUiThread(() -> SnackBar.error(context, mainLayout, getString(R.string.genericError)));
|
||||
Log.e("onError", e.toString());
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
thread.start();
|
||||
|
||||
});
|
||||
|
||||
pullToRefresh = v.findViewById(R.id.pullToRefresh);
|
||||
pullToRefresh.setOnRefreshListener(() -> {
|
||||
|
||||
pageCurrentIndex = 1;
|
||||
loadNotifications(false);
|
||||
|
||||
});
|
||||
|
||||
loadNotifications(false);
|
||||
return v;
|
||||
|
||||
}
|
||||
|
||||
private void loadNotifications(boolean append) {
|
||||
|
||||
noDataNotifications.setVisibility(View.GONE);
|
||||
|
||||
if(pageCurrentIndex == 1 || !append) {
|
||||
|
||||
notificationThreads.clear();
|
||||
notificationsAdapter.notifyDataSetChanged();
|
||||
pullToRefresh.setRefreshing(false);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
|
||||
} else {
|
||||
|
||||
loadingMoreView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
String instanceUrl = tinyDB.getString("instanceUrl");
|
||||
String loginUid = tinyDB.getString("loginUid");
|
||||
String instanceToken = "token " + tinyDB.getString(loginUid + "-token");
|
||||
|
||||
String[] filter = tinyDB.getString("notificationsFilterState").equals("read") ?
|
||||
new String[]{"pinned", "read"} :
|
||||
new String[]{"pinned", "unread"};
|
||||
|
||||
Call<List<NotificationThread>> call = RetrofitClient.getInstance(instanceUrl, context)
|
||||
.getApiInterface()
|
||||
.getNotificationThreads(instanceToken, false, filter,
|
||||
StaticGlobalVariables.defaultOldestTimestamp, "",
|
||||
pageCurrentIndex, pageResultLimit);
|
||||
|
||||
call.enqueue(new Callback<List<NotificationThread>>() {
|
||||
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<List<NotificationThread>> call, @NonNull Response<List<NotificationThread>> response) {
|
||||
|
||||
if(response.code() == 200) {
|
||||
|
||||
assert response.body() != null;
|
||||
|
||||
if(!append) {
|
||||
|
||||
notificationThreads.clear();
|
||||
}
|
||||
|
||||
notificationThreads.addAll(response.body());
|
||||
notificationsAdapter.notifyDataSetChanged();
|
||||
|
||||
} else {
|
||||
|
||||
Log.e("onError", String.valueOf(response.code()));
|
||||
}
|
||||
|
||||
onCleanup();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<List<NotificationThread>> call, @NonNull Throwable t) {
|
||||
|
||||
Log.e("onError", t.toString());
|
||||
onCleanup();
|
||||
|
||||
}
|
||||
|
||||
private void onCleanup() {
|
||||
|
||||
AppUtil.setMultiVisibility(View.GONE, loadingMoreView, progressBar);
|
||||
pullToRefresh.setRefreshing(false);
|
||||
|
||||
if(notificationThreads.isEmpty()) {
|
||||
|
||||
noDataNotifications.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void changeFilterMode() {
|
||||
|
||||
int filterIcon = currentFilterMode.equalsIgnoreCase("read") ?
|
||||
R.drawable.ic_filter_closed :
|
||||
R.drawable.ic_filter;
|
||||
|
||||
menu.getItem(0).setIcon(filterIcon);
|
||||
|
||||
if(currentFilterMode.equalsIgnoreCase("read")) {
|
||||
|
||||
markAllAsRead.setVisibility(View.GONE);
|
||||
} else {
|
||||
|
||||
markAllAsRead.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
|
||||
|
||||
this.menu = menu;
|
||||
|
||||
inflater.inflate(R.menu.filter_menu_notifications, menu);
|
||||
|
||||
currentFilterMode = tinyDB.getString("notificationsFilterState");
|
||||
changeFilterMode();
|
||||
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
|
||||
if(item.getItemId() == R.id.filterNotifications) {
|
||||
|
||||
BottomSheetNotificationsFilterFragment bottomSheetNotificationsFilterFragment = new BottomSheetNotificationsFilterFragment();
|
||||
bottomSheetNotificationsFilterFragment.show(getChildFragmentManager(), "notificationsFilterBottomSheet");
|
||||
bottomSheetNotificationsFilterFragment.setOnDismissedListener(() -> {
|
||||
|
||||
pageCurrentIndex = 1;
|
||||
currentFilterMode = tinyDB.getString("notificationsFilterState");
|
||||
|
||||
changeFilterMode();
|
||||
loadNotifications(false);
|
||||
|
||||
});
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotificationClicked(NotificationThread notificationThread) {
|
||||
|
||||
Thread thread = new Thread(() -> {
|
||||
|
||||
try {
|
||||
|
||||
if(notificationThread.isUnread()) {
|
||||
|
||||
notificationsActions.setNotificationStatus(notificationThread, NotificationsActions.NotificationStatus.READ);
|
||||
activity.runOnUiThread(() -> loadNotifications(false));
|
||||
|
||||
}
|
||||
} catch(IOException ignored) {}
|
||||
|
||||
});
|
||||
|
||||
thread.start();
|
||||
|
||||
if(StringUtils.containsAny(notificationThread.getSubject().getType().toLowerCase(), "pull", "issue")) {
|
||||
|
||||
Intent intent = new Intent(context, IssueDetailActivity.class);
|
||||
String issueUrl = notificationThread.getSubject().getUrl();
|
||||
|
||||
tinyDB.putString("issueNumber", issueUrl.substring(issueUrl.lastIndexOf("/") + 1));
|
||||
tinyDB.putString("issueType", notificationThread.getSubject().getType());
|
||||
tinyDB.putString("repoFullName", notificationThread.getRepository().getFullname());
|
||||
|
||||
startActivity(intent);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMoreClicked(NotificationThread notificationThread) {
|
||||
|
||||
BottomSheetNotificationsFragment bottomSheetNotificationsFragment = new BottomSheetNotificationsFragment();
|
||||
bottomSheetNotificationsFragment.onAttach(context, notificationThread, this);
|
||||
bottomSheetNotificationsFragment.show(getChildFragmentManager(), "notificationsBottomSheet");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelected() {
|
||||
|
||||
pageCurrentIndex = 1;
|
||||
loadNotifications(false);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -14,8 +14,10 @@ import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
@ -144,6 +146,15 @@ public class AppUtil {
|
||||
|
||||
}
|
||||
|
||||
public static String getTimestampFromDate(Context context, Date date) {
|
||||
|
||||
TinyDB tinyDB = new TinyDB(context);
|
||||
Locale locale = new Locale(tinyDB.getString("locale"));
|
||||
|
||||
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX", locale).format(date);
|
||||
|
||||
}
|
||||
|
||||
public static String formatFileSizeInDetail(long size) {
|
||||
|
||||
String fileSize = null;
|
||||
@ -299,4 +310,14 @@ public class AppUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static int getPixelsFromDensity(Context context, int dp) {
|
||||
|
||||
return (int) (context.getResources().getDisplayMetrics().density * dp);
|
||||
}
|
||||
|
||||
public static int getPixelsFromScaledDensity(Context context, int sp) {
|
||||
|
||||
return (int) (context.getResources().getDisplayMetrics().scaledDensity * sp);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Piotr Wittchen
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.mian.gitnex.helpers;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
/**
|
||||
* InfiniteScrollListener, which can be added to RecyclerView with addOnScrollListener
|
||||
* to detect moment when RecyclerView was scrolled to the end.
|
||||
*/
|
||||
public abstract class InfiniteScrollListener extends RecyclerView.OnScrollListener {
|
||||
private final int maxItemsPerRequest;
|
||||
private final LinearLayoutManager layoutManager;
|
||||
|
||||
/**
|
||||
* Initializes InfiniteScrollListener, which can be added
|
||||
* to RecyclerView with addOnScrollListener method
|
||||
*
|
||||
* @param maxItemsPerRequest Max items to be loaded in a single request.
|
||||
* @param layoutManager LinearLayoutManager created in the Activity.
|
||||
*/
|
||||
public InfiniteScrollListener(int maxItemsPerRequest, LinearLayoutManager layoutManager) {
|
||||
assert maxItemsPerRequest > 0;
|
||||
assert layoutManager != null;
|
||||
|
||||
this.maxItemsPerRequest = maxItemsPerRequest;
|
||||
this.layoutManager = layoutManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback method to be invoked when the RecyclerView has been scrolled
|
||||
*
|
||||
* @param recyclerView The RecyclerView which scrolled.
|
||||
* @param dx The amount of horizontal scroll.
|
||||
* @param dy The amount of vertical scroll.
|
||||
*/
|
||||
@Override public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
|
||||
super.onScrolled(recyclerView, dx, dy);
|
||||
if (canLoadMoreItems()) {
|
||||
onScrolledToEnd(layoutManager.findFirstVisibleItemPosition());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes RecyclerView by setting new adapter,
|
||||
* calling invalidate method and scrolling to given position
|
||||
*
|
||||
* @param view RecyclerView to be refreshed
|
||||
* @param adapter adapter with new list of items to be loaded
|
||||
* @param position position to which RecyclerView will be scrolled
|
||||
*/
|
||||
protected void refreshView(RecyclerView view, RecyclerView.Adapter adapter, int position) {
|
||||
view.setAdapter(adapter);
|
||||
view.invalidate();
|
||||
view.scrollToPosition(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if more items can be loaded to the RecyclerView
|
||||
*
|
||||
* @return boolean Returns true if can load more items or false if not.
|
||||
*/
|
||||
protected boolean canLoadMoreItems() {
|
||||
final int visibleItemsCount = layoutManager.getChildCount();
|
||||
final int totalItemsCount = layoutManager.getItemCount();
|
||||
final int pastVisibleItemsCount = layoutManager.findFirstVisibleItemPosition();
|
||||
final boolean lastItemShown = visibleItemsCount + pastVisibleItemsCount >= totalItemsCount;
|
||||
return lastItemShown && totalItemsCount >= maxItemsPerRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback method to be invoked when the RecyclerView has been scrolled to the end
|
||||
*
|
||||
* @param firstVisibleItemPosition Id of the first visible item on the list.
|
||||
*/
|
||||
public abstract void onScrolledToEnd(final int firstVisibleItemPosition);
|
||||
}
|
@ -1,38 +1,48 @@
|
||||
package org.mian.gitnex.helpers;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* Author M M Arif
|
||||
*/
|
||||
|
||||
public interface StaticGlobalVariables {
|
||||
public abstract class StaticGlobalVariables {
|
||||
|
||||
// generic values
|
||||
int resultLimitNewGiteaInstances = 25; // Gitea 1.12 and above
|
||||
int resultLimitOldGiteaInstances = 10; // Gitea 1.11 and below
|
||||
public static int resultLimitNewGiteaInstances = 25; // Gitea 1.12 and above
|
||||
public static int resultLimitOldGiteaInstances = 10; // Gitea 1.11 and below
|
||||
public static String defaultOldestTimestamp = "1970-01-01T00:00:00+00:00";
|
||||
|
||||
public static int getCurrentResultLimit(Context context) {
|
||||
|
||||
Version version = new Version(new TinyDB(context).getString("giteaVersion"));
|
||||
return version.higherOrEqual("1.12") ? resultLimitNewGiteaInstances : resultLimitOldGiteaInstances;
|
||||
|
||||
}
|
||||
|
||||
// tags
|
||||
String tagMilestonesFragment = "MilestonesFragment";
|
||||
String tagPullRequestsList = "PullRequestsListFragment";
|
||||
String tagIssuesList = "IssuesListFragment";
|
||||
String tagMilestonesAdapter = "MilestonesAdapter";
|
||||
String draftsRepository = "DraftsRepository";
|
||||
String repositoriesRepository = "RepositoriesRepository";
|
||||
String replyToIssueActivity = "ReplyToIssueActivity";
|
||||
String tagDraftsBottomSheet = "BottomSheetDraftsFragment";
|
||||
String userAccountsRepository = "UserAccountsRepository";
|
||||
public static String tagMilestonesFragment = "MilestonesFragment";
|
||||
public static String tagPullRequestsList = "PullRequestsListFragment";
|
||||
public static String tagIssuesList = "IssuesListFragment";
|
||||
public static String tagMilestonesAdapter = "MilestonesAdapter";
|
||||
public static String draftsRepository = "DraftsRepository";
|
||||
public static String repositoriesRepository = "RepositoriesRepository";
|
||||
public static String replyToIssueActivity = "ReplyToIssueActivity";
|
||||
public static String tagDraftsBottomSheet = "BottomSheetDraftsFragment";
|
||||
public static String userAccountsRepository = "UserAccountsRepository";
|
||||
|
||||
// issues variables
|
||||
int issuesPageInit = 1;
|
||||
String issuesRequestType = "issues";
|
||||
public static int issuesPageInit = 1;
|
||||
public static String issuesRequestType = "issues";
|
||||
|
||||
// pull request
|
||||
int prPageInit = 1;
|
||||
public static int prPageInit = 1;
|
||||
|
||||
// milestone
|
||||
int milestonesPageInit = 1;
|
||||
public static int milestonesPageInit = 1;
|
||||
|
||||
// drafts
|
||||
String draftTypeComment = "comment";
|
||||
String draftTypeIssue = "issue";
|
||||
public static String draftTypeComment = "comment";
|
||||
public static String draftTypeIssue = "issue";
|
||||
|
||||
}
|
||||
|
@ -211,6 +211,10 @@ public class TinyDB {
|
||||
return preferences.getFloat(key, 0);
|
||||
}
|
||||
|
||||
public float getFloat(String key, float defaultValue) {
|
||||
return preferences.getFloat(key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get double value from SharedPreferences at 'key'. If exception thrown, return 'defaultValue'
|
||||
* @param key SharedPreferences key
|
||||
@ -292,6 +296,10 @@ public class TinyDB {
|
||||
return preferences.getBoolean(key, false);
|
||||
}
|
||||
|
||||
public boolean getBoolean(String key, boolean defaultValue) {
|
||||
return preferences.getBoolean(key, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parsed ArrayList of Boolean from SharedPreferences at 'key'
|
||||
* @param key SharedPreferences key
|
||||
@ -357,7 +365,7 @@ public class TinyDB {
|
||||
*/
|
||||
public void putListInt(String key, ArrayList<Integer> intList) {
|
||||
checkForNullKey(key);
|
||||
Integer[] myIntList = intList.toArray(new Integer[intList.size()]);
|
||||
Integer[] myIntList = intList.toArray(new Integer[0]);
|
||||
preferences.edit().putString(key, TextUtils.join("‚‗‚", myIntList)).apply();
|
||||
}
|
||||
|
||||
@ -378,7 +386,7 @@ public class TinyDB {
|
||||
*/
|
||||
public void putListLong(String key, ArrayList<Long> longList) {
|
||||
checkForNullKey(key);
|
||||
Long[] myLongList = longList.toArray(new Long[longList.size()]);
|
||||
Long[] myLongList = longList.toArray(new Long[0]);
|
||||
preferences.edit().putString(key, TextUtils.join("‚‗‚", myLongList)).apply();
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ import org.mian.gitnex.models.Labels;
|
||||
import org.mian.gitnex.models.MergePullRequest;
|
||||
import org.mian.gitnex.models.Milestones;
|
||||
import org.mian.gitnex.models.NewFile;
|
||||
import org.mian.gitnex.models.NotificationThread;
|
||||
import org.mian.gitnex.models.OrgOwner;
|
||||
import org.mian.gitnex.models.Organization;
|
||||
import org.mian.gitnex.models.OrganizationRepository;
|
||||
@ -75,6 +76,27 @@ public interface ApiInterface {
|
||||
@POST("users/{username}/tokens") // create new token with 2fa otp
|
||||
Call<UserTokens> createNewTokenWithOTP(@Header("Authorization") String authorization, @Header("X-Gitea-OTP") int loginOTP, @Path("username") String loginUid, @Body UserTokens jsonStr);
|
||||
|
||||
@GET("notifications") // List users's notification threads
|
||||
Call<List<NotificationThread>> getNotificationThreads(@Header("Authorization") String token, @Query("all") Boolean all, @Query("status-types") String[] statusTypes, @Query("since") String since, @Query("before") String before, @Query("page") Integer page, @Query("limit") Integer limit);
|
||||
|
||||
@PUT("notifications") // Mark notification threads as read, pinned or unread
|
||||
Call<ResponseBody> markNotificationThreadsAsRead(@Header("Authorization") String token, @Query("last_read_at") String last_read_at, @Query("all") Boolean all, @Query("status-types") String[] statusTypes, @Query("to-status") String toStatus);
|
||||
|
||||
@GET("notifications/new") // Check if unread notifications exist
|
||||
Call<JsonElement> checkUnreadNotifications(@Header("Authorization") String token);
|
||||
|
||||
@GET("notifications/threads/{id}") // Get notification thread by ID
|
||||
Call<NotificationThread> getNotificationThread(@Header("Authorization") String token, @Path("id") Integer id);
|
||||
|
||||
@PATCH("notifications/threads/{id}") // Mark notification thread as read by ID
|
||||
Call<ResponseBody> markNotificationThreadAsRead(@Header("Authorization") String token, @Path("id") Integer id, @Query("to-status") String toStatus);
|
||||
|
||||
@GET("repos/{owner}/{repo}/notifications") // List users's notification threads on a specific repo
|
||||
Call<List<NotificationThread>> getRepoNotificationThreads(@Header("Authorization") String token, @Path("owner") String owner, @Path("repo") String repo, @Query("all") String all, @Query("status-types") String[] statusTypes, @Query("since") String since, @Query("before") String before, @Query("page") String page, @Query("limit") String limit);
|
||||
|
||||
@PUT("repos/{owner}/{repo}/notifications") // Mark notification threads as read, pinned or unread on a specific repo
|
||||
Call<ResponseBody> markRepoNotificationThreadsAsRead(@Header("Authorization") String token, @Path("owner") String owner, @Path("repo") String repo, @Query("all") Boolean all, @Query("status-types") String[] statusTypes, @Query("to-status") String toStatus, @Query("last_read_at") String last_read_at);
|
||||
|
||||
@GET("user/orgs") // get user organizations
|
||||
Call<List<UserOrganizations>> getUserOrgs(@Header("Authorization") String token);
|
||||
|
||||
|
@ -0,0 +1,37 @@
|
||||
package org.mian.gitnex.models;
|
||||
|
||||
/**
|
||||
* Author opyale
|
||||
*/
|
||||
|
||||
public class NotificationSubject {
|
||||
private String latest_comment_url;
|
||||
private String title;
|
||||
private String type;
|
||||
private String url;
|
||||
|
||||
public NotificationSubject(String latest_comment_url, String title, String type, String url) {
|
||||
|
||||
this.latest_comment_url = latest_comment_url;
|
||||
this.title = title;
|
||||
this.type = type;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getLatest_comment_url() {
|
||||
return latest_comment_url;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package org.mian.gitnex.models;
|
||||
|
||||
/**
|
||||
* Author opyale
|
||||
*/
|
||||
|
||||
public class NotificationThread {
|
||||
private int id;
|
||||
private boolean pinned;
|
||||
private UserRepositories repository;
|
||||
private NotificationSubject subject;
|
||||
private boolean unread;
|
||||
private String updated_at;
|
||||
private String url;
|
||||
|
||||
public NotificationThread(int id, boolean pinned, UserRepositories repository, NotificationSubject subject, boolean unread, String updated_at, String url) {
|
||||
|
||||
this.id = id;
|
||||
this.pinned = pinned;
|
||||
this.repository = repository;
|
||||
this.subject = subject;
|
||||
this.unread = unread;
|
||||
this.updated_at = updated_at;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public boolean isPinned() {
|
||||
return pinned;
|
||||
}
|
||||
|
||||
public UserRepositories getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
public NotificationSubject getSubject() {
|
||||
return subject;
|
||||
}
|
||||
|
||||
public boolean isUnread() {
|
||||
return unread;
|
||||
}
|
||||
|
||||
public String getUpdated_at() {
|
||||
return updated_at;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
package org.mian.gitnex.notifications;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import androidx.work.Constraints;
|
||||
import androidx.work.ExistingPeriodicWorkPolicy;
|
||||
import androidx.work.NetworkType;
|
||||
import androidx.work.PeriodicWorkRequest;
|
||||
import androidx.work.WorkManager;
|
||||
import org.mian.gitnex.helpers.TinyDB;
|
||||
import org.mian.gitnex.helpers.Version;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Author opyale
|
||||
*/
|
||||
|
||||
public class NotificationsMaster {
|
||||
|
||||
private static int notificationsSupported = -1;
|
||||
|
||||
private static void checkVersion(TinyDB tinyDB) {
|
||||
|
||||
String currentVersion = tinyDB.getString("giteaVersion");
|
||||
|
||||
if(tinyDB.getBoolean("loggedInMode") && !currentVersion.isEmpty()) {
|
||||
|
||||
notificationsSupported = new Version(currentVersion).higherOrEqual("1.12.3") ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static void fireWorker(Context context) {
|
||||
|
||||
WorkManager.getInstance(context).cancelAllWorkByTag(context.getPackageName());
|
||||
}
|
||||
|
||||
public static void hireWorker(Context context) {
|
||||
|
||||
TinyDB tinyDB = new TinyDB(context);
|
||||
|
||||
if(notificationsSupported == -1) {
|
||||
checkVersion(tinyDB);
|
||||
}
|
||||
|
||||
if(notificationsSupported == 1) {
|
||||
|
||||
Constraints.Builder constraints = new Constraints.Builder()
|
||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||
.setRequiresBatteryNotLow(false)
|
||||
.setRequiresStorageNotLow(false)
|
||||
.setRequiresCharging(false);
|
||||
|
||||
if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
|
||||
constraints.setRequiresDeviceIdle(false);
|
||||
}
|
||||
|
||||
PeriodicWorkRequest periodicWorkRequest = new PeriodicWorkRequest.Builder(NotificationsWorker.class, tinyDB.getInt("pollingDelayMinutes"), TimeUnit.MINUTES)
|
||||
.setConstraints(constraints.build())
|
||||
.addTag(context.getPackageName())
|
||||
.build();
|
||||
|
||||
WorkManager.getInstance(context).enqueueUniquePeriodicWork(context.getPackageName(), ExistingPeriodicWorkPolicy.KEEP, periodicWorkRequest);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,160 @@
|
||||
package org.mian.gitnex.notifications;
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.media.RingtoneManager;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.work.Worker;
|
||||
import androidx.work.WorkerParameters;
|
||||
import org.mian.gitnex.R;
|
||||
import org.mian.gitnex.activities.MainActivity;
|
||||
import org.mian.gitnex.clients.RetrofitClient;
|
||||
import org.mian.gitnex.helpers.AppUtil;
|
||||
import org.mian.gitnex.helpers.TinyDB;
|
||||
import org.mian.gitnex.models.NotificationThread;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Response;
|
||||
|
||||
/**
|
||||
* Author opyale
|
||||
*/
|
||||
|
||||
public class NotificationsWorker extends Worker {
|
||||
|
||||
private static final int MAXIMUM_NOTIFICATIONS = 100;
|
||||
private static final long[] VIBRATION_PATTERN = new long[]{ 1000, 1000 };
|
||||
|
||||
private Context context;
|
||||
private TinyDB tinyDB;
|
||||
|
||||
public NotificationsWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
|
||||
|
||||
super(context, workerParams);
|
||||
|
||||
this.context = context;
|
||||
this.tinyDB = new TinyDB(context);
|
||||
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Result doWork() {
|
||||
|
||||
String instanceUrl = tinyDB.getString("instanceUrl");
|
||||
String token = "token " + tinyDB.getString(tinyDB.getString("loginUid") + "-token");
|
||||
|
||||
int notificationLoops = tinyDB.getInt("pollingDelayMinutes") >= 15 ? 1 : Math.min(15 - tinyDB.getInt("pollingDelayMinutes"), 10);
|
||||
|
||||
for(int i=0; i<notificationLoops; i++) {
|
||||
|
||||
long startPollingTime = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
|
||||
String previousRefreshTimestamp = tinyDB.getString("previousRefreshTimestamp", AppUtil.getTimestampFromDate(context, new Date()));
|
||||
|
||||
Call<List<NotificationThread>> call = RetrofitClient.getInstance(instanceUrl, context)
|
||||
.getApiInterface()
|
||||
.getNotificationThreads(token, false, new String[]{"unread"}, previousRefreshTimestamp,
|
||||
null, 1, MAXIMUM_NOTIFICATIONS);
|
||||
|
||||
Response<List<NotificationThread>> response = call.execute();
|
||||
|
||||
if(response.code() == 200) {
|
||||
|
||||
assert response.body() != null;
|
||||
|
||||
List<NotificationThread> notificationThreads = response.body();
|
||||
Log.i("ReceivedNotifications", String.valueOf(notificationThreads.size()));
|
||||
|
||||
if(!notificationThreads.isEmpty()) {
|
||||
|
||||
for(NotificationThread notificationThread : notificationThreads) {
|
||||
|
||||
sendNotification(notificationThread);
|
||||
}
|
||||
}
|
||||
|
||||
tinyDB.putString("previousRefreshTimestamp", AppUtil.getTimestampFromDate(context, new Date()));
|
||||
|
||||
} else {
|
||||
|
||||
Log.e("onError", String.valueOf(response.code()));
|
||||
}
|
||||
} catch(Exception e) {
|
||||
|
||||
Log.e("onError", e.toString());
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
if(notificationLoops > 1 && i < (notificationLoops - 1)) {
|
||||
|
||||
Thread.sleep(60000 - (System.currentTimeMillis() - startPollingTime));
|
||||
}
|
||||
} catch (InterruptedException ignored) {}
|
||||
}
|
||||
|
||||
return Result.success();
|
||||
|
||||
}
|
||||
|
||||
private void sendNotification(NotificationThread notificationThread) {
|
||||
|
||||
Intent intent = new Intent(context, MainActivity.class);
|
||||
intent.putExtra("launchFragment", "notifications");
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
if(notificationManager != null) {
|
||||
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
|
||||
NotificationChannel notificationChannel = new NotificationChannel(context.getPackageName(), context.getString(R.string.app_name),
|
||||
NotificationManager.IMPORTANCE_HIGH);
|
||||
|
||||
notificationChannel.enableLights(true);
|
||||
notificationChannel.setLightColor(Color.GREEN);
|
||||
notificationChannel.enableVibration(true);
|
||||
notificationChannel.setVibrationPattern(VIBRATION_PATTERN);
|
||||
|
||||
notificationManager.createNotificationChannel(notificationChannel);
|
||||
|
||||
}
|
||||
|
||||
String subjectUrl = notificationThread.getSubject().getUrl();
|
||||
String issueId = context.getResources().getString(R.string.hash) + subjectUrl.substring(subjectUrl.lastIndexOf("/") + 1);
|
||||
|
||||
String notificationHeader = issueId + " " + notificationThread.getSubject().getTitle();
|
||||
String notificationBody = String.format(context.getResources().getString(R.string.notificationBody),
|
||||
notificationThread.getSubject().getType());
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, context.getPackageName())
|
||||
.setSmallIcon(R.drawable.gitnex_transparent).setContentTitle(notificationHeader)
|
||||
.setContentText(notificationBody)
|
||||
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setContentIntent(pendingIntent).setVibrate(VIBRATION_PATTERN).setAutoCancel(true);
|
||||
|
||||
int previousNotificationId = tinyDB.getInt("previousNotificationId", 0);
|
||||
int newPreviousNotificationId = previousNotificationId > 71951418 ? 0 : previousNotificationId + 1;
|
||||
|
||||
tinyDB.putInt("previousNotificationId", newPreviousNotificationId);
|
||||
|
||||
notificationManager.notify(previousNotificationId, builder.build());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
20
app/src/main/res/drawable/ic_notifications.xml
Normal file
20
app/src/main/res/drawable/ic_notifications.xml
Normal file
@ -0,0 +1,20 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M18,8A6,6 0,0 0,6 8c0,7 -3,9 -3,9h18s-3,-2 -3,-9"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#368f73"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M13.73,21a2,2 0,0 1,-3.46 0"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#368f73"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
11
app/src/main/res/drawable/ic_pin.xml
Normal file
11
app/src/main/res/drawable/ic_pin.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="#368f73"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M7.886,1.553a1.75,1.75 0,0 1,2.869 0.604l0.633,1.629a5.666,5.666 0,0 0,3.725 3.395l3.959,1.131a1.75,1.75 0,0 1,0.757 2.92L16.06,15l5.594,5.595a0.75,0.75 0,1 1,-1.06 1.06L15,16.061l-3.768,3.768a1.75,1.75 0,0 1,-2.92 -0.757l-1.131,-3.96a5.667,5.667 0,0 0,-3.395 -3.724l-1.63,-0.633a1.75,1.75 0,0 1,-0.603 -2.869l6.333,-6.333zM14.475,14.465l-0.005,0.005 -0.005,0.005 -4.294,4.293a0.25,0.25 0,0 1,-0.417 -0.108l-1.13,-3.96A7.166,7.166 0,0 0,4.33 9.99L2.7,9.356a0.25,0.25 0,0 1,-0.086 -0.41l6.333,-6.332a0.25,0.25 0,0 1,0.41 0.086l0.633,1.63a7.167,7.167 0,0 0,4.71 4.293l3.96,1.131a0.25,0.25 0,0 1,0.108 0.417l-4.293,4.294z"
|
||||
android:fillType="evenOdd"/>
|
||||
</vector>
|
@ -156,4 +156,34 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/pollingDelayFrame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pollingDelayHeaderSelector"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginStart="44dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:text="@string/notificationsPollingHeaderText"
|
||||
android:textColor="?attr/primaryTextColor"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pollingDelaySelected"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:layout_marginStart="44dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:text="@string/pollingDelaySelectedText"
|
||||
android:textColor="?attr/selectedTextColor"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
60
app/src/main/res/layout/bottom_sheet_notifications.xml
Normal file
60
app/src/main/res/layout/bottom_sheet_notifications.xml
Normal file
@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:background="?attr/primaryBackgroundColor">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/markPinned"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:text="@string/pinNotification"
|
||||
android:drawableStart="@drawable/ic_pin"
|
||||
android:drawablePadding="24dp"
|
||||
android:textColor="?attr/primaryTextColor"
|
||||
android:textSize="16sp"
|
||||
android:padding="12dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/markRead"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:text="@string/markAsRead"
|
||||
android:drawableStart="@drawable/ic_unwatch"
|
||||
android:drawablePadding="24dp"
|
||||
android:textColor="?attr/primaryTextColor"
|
||||
android:textSize="16sp"
|
||||
android:padding="12dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/markUnread"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:text="@string/markAsUnread"
|
||||
android:drawableStart="@drawable/ic_watchers"
|
||||
android:drawablePadding="24dp"
|
||||
android:textColor="?attr/primaryTextColor"
|
||||
android:textSize="16sp"
|
||||
android:padding="12dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
</LinearLayout>
|
@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:background="?attr/primaryBackgroundColor">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/unreadNotifications"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:text="@string/isUnread"
|
||||
android:drawableStart="@drawable/ic_watchers"
|
||||
android:drawablePadding="24dp"
|
||||
android:textColor="?attr/primaryTextColor"
|
||||
android:textSize="16sp"
|
||||
android:padding="12dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/readNotifications"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:text="@string/isRead"
|
||||
android:drawableStart="@drawable/ic_unwatch"
|
||||
android:drawablePadding="24dp"
|
||||
android:textColor="?attr/primaryTextColor"
|
||||
android:textSize="16sp"
|
||||
android:padding="12dp" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</LinearLayout>
|
60
app/src/main/res/layout/fragment_notifications.xml
Normal file
60
app/src/main/res/layout/fragment_notifications.xml
Normal file
@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/mainLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/primaryBackgroundColor"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/loadingMoreView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:visibility="gone"
|
||||
android:padding="5dp" />
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/pullToRefresh"
|
||||
android:layout_above="@id/loadingMoreView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/notifications"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/noDataNotifications"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_margin="15dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/noDataNotifications"
|
||||
android:textColor="?attr/primaryTextColor"
|
||||
android:textSize="20sp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/markAllAsRead"
|
||||
android:layout_width="54dp"
|
||||
android:layout_height="54dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_margin="15dp"
|
||||
android:background="@drawable/shape_circle"
|
||||
android:contentDescription="@string/markAsRead"
|
||||
android:padding="@dimen/fab_padding"
|
||||
android:src="@drawable/ic_done"
|
||||
android:tint="@color/colorWhite" />
|
||||
|
||||
</RelativeLayout>
|
84
app/src/main/res/layout/list_notifications.xml
Normal file
84
app/src/main/res/layout/list_notifications.xml
Normal file
@ -0,0 +1,84 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/frame"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/primaryBackgroundColor"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="20dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginRight="20dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/type"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top"
|
||||
android:contentDescription="@string/generalImgContentText"
|
||||
app:srcCompat="@drawable/ic_pull_request" />
|
||||
|
||||
<Space
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pinned"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/generalImgContentText"
|
||||
app:srcCompat="@drawable/ic_pin" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subject"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?attr/primaryTextColor"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/repository"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="5dp"
|
||||
android:textColor="?attr/primaryTextColor" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/more"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:contentDescription="@string/generalImgContentText"
|
||||
app:srcCompat="@drawable/ic_dotted_menu_horizontal" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?attr/dividerColor" />
|
||||
|
||||
</LinearLayout>
|
@ -5,41 +5,59 @@
|
||||
|
||||
<group android:checkableBehavior="single"
|
||||
android:id="@+id/nav_main">
|
||||
|
||||
<item android:id="@+id/nav_home"
|
||||
android:icon="@drawable/ic_repo"
|
||||
android:title="@string/navMyRepos" />
|
||||
|
||||
<item android:id="@+id/nav_starred_repos"
|
||||
android:icon="@drawable/ic_star_unfilled"
|
||||
android:title="@string/navStarredRepos" />
|
||||
|
||||
<item android:id="@+id/nav_organizations"
|
||||
android:icon="@drawable/ic_organization"
|
||||
android:title="@string/navOrgs" />
|
||||
|
||||
<item android:id="@+id/nav_repositories"
|
||||
android:icon="@drawable/ic_repo"
|
||||
android:title="@string/navRepos" />
|
||||
|
||||
<item
|
||||
android:id="@+id/nav_notifications"
|
||||
android:icon="@drawable/ic_notifications"
|
||||
android:title="@string/pageTitleNotifications"
|
||||
android:visible="false" />
|
||||
|
||||
<item android:id="@+id/nav_explore"
|
||||
android:icon="@drawable/ic_search"
|
||||
android:title="@string/navExplore" />
|
||||
|
||||
<item android:id="@+id/nav_comments_draft"
|
||||
android:icon="@drawable/ic_drafts"
|
||||
android:title="@string/titleDrafts" />
|
||||
|
||||
<item android:id="@+id/nav_profile"
|
||||
android:icon="@drawable/ic_person"
|
||||
android:title="@string/navProfile" />
|
||||
|
||||
<item android:id="@+id/nav_administration"
|
||||
android:icon="@drawable/ic_tool"
|
||||
android:title="@string/navAdministration"
|
||||
android:visible="false" />
|
||||
|
||||
</group>
|
||||
|
||||
<group android:checkableBehavior="single"
|
||||
android:id="@+id/nav_menu_settings">
|
||||
|
||||
<item android:id="@+id/nav_settings"
|
||||
android:icon="@drawable/ic_settings"
|
||||
android:title="@string/navSettings" />
|
||||
|
||||
<item android:id="@+id/nav_about"
|
||||
android:icon="@drawable/ic_info"
|
||||
android:title="@string/navAbout" />
|
||||
|
||||
</group>
|
||||
|
||||
<item android:id="@+id/nav_rate_app"
|
||||
@ -48,9 +66,11 @@
|
||||
|
||||
<group android:checkableBehavior="single"
|
||||
android:id="@+id/nav_extra">
|
||||
|
||||
<item android:id="@+id/nav_logout"
|
||||
android:icon="@drawable/ic_logout"
|
||||
android:title="@string/navLogout" />
|
||||
|
||||
</group>
|
||||
|
||||
</menu>
|
||||
|
12
app/src/main/res/menu/filter_menu_notifications.xml
Normal file
12
app/src/main/res/menu/filter_menu_notifications.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/filterNotifications"
|
||||
android:icon="@drawable/ic_filter"
|
||||
android:title="@string/strFilter"
|
||||
android:orderInCategory="0"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
</menu>
|
@ -618,11 +618,31 @@
|
||||
|
||||
<string name="appearanceHintText">Themes, fonts, badges, code block theme</string>
|
||||
<string name="fileViewerHintText">PDF mode, source code theme</string>
|
||||
<string name="securityHintText">SSL certificates, cache</string>
|
||||
<string name="securityHintText">SSL certificates, cache, polling delay</string>
|
||||
<string name="languagesHintText">Languages</string>
|
||||
<string name="reportsHintText">Crash reports</string>
|
||||
|
||||
<string name="archivedRepository">Archived</string>
|
||||
|
||||
<string name="accountDeletedMessage">Account deleted successfully</string>
|
||||
|
||||
<!-- Notifications -->
|
||||
<string name="pageTitleNotifications">Notifications</string>
|
||||
<string name="noDataNotifications">No notifications found</string>
|
||||
|
||||
<string name="notificationBody">You have received a new notification. (%s)</string>
|
||||
|
||||
<string name="notificationsPollingHeaderText">Notifications Polling Delay</string>
|
||||
<string name="pollingDelaySelectedText">%d Minutes</string>
|
||||
<string name="pollingDelayDialogHeaderText">Select Polling Delay</string>
|
||||
<string name="pollingDelayDialogDescriptionText">Choose a minutely delay in which GitNex tries to poll new notifications.</string>
|
||||
|
||||
<string name="markAsRead">Mark as Read</string>
|
||||
<string name="markAsUnread">Mark as Unread</string>
|
||||
<string name="pinNotification">Pin Notification</string>
|
||||
<string name="markedNotificationsAsRead">Successfully marked all notifications as read.</string>
|
||||
|
||||
<string name="isRead">Read</string>
|
||||
<string name="isUnread">Unread</string>
|
||||
|
||||
</resources>
|
||||
|
@ -7,6 +7,7 @@
|
||||
<item name="android:typeface">monospace</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
<item name="android:textColorSecondary">@color/colorWhite</item>
|
||||
<item name="android:textColorPrimary">@color/colorWhite</item>
|
||||
|
||||
<item name="diffAddedColor">@color/diffAddedColor</item>
|
||||
<item name="diffRemovedColor">@color/diffRemovedColor</item>
|
||||
@ -35,6 +36,7 @@
|
||||
<item name="android:typeface">monospace</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
<item name="android:textColorSecondary">@color/lightThemeTextColor</item>
|
||||
<item name="android:textColorPrimary">@color/lightThemeTextColor</item>
|
||||
|
||||
<item name="diffAddedColor">@color/lightThemeDiffAddedColor</item>
|
||||
<item name="diffRemovedColor">@color/lightThemeDiffRemovedColor</item>
|
||||
|
Loading…
Reference in New Issue
Block a user