Revamp different screens, add new features and improvements (#1504) All checks were successful ci/woodpecker/push/locale Pipeline was successful ci/woodpecker/push/check Pipeline was successful ci/woodpecker/push/build Pipeline was successful ci/woodpecker/push/finish Pipeline was successful ci/woodpecker/cron/check Pipeline was successful ci/woodpecker/cron/build Pipeline was successful ci/woodpecker/cron/locale Pipeline was successful
All checks were successful
ci/woodpecker/push/locale Pipeline was successful
ci/woodpecker/push/check Pipeline was successful
ci/woodpecker/push/build Pipeline was successful
ci/woodpecker/push/finish Pipeline was successful
ci/woodpecker/cron/check Pipeline was successful
ci/woodpecker/cron/build Pipeline was successful
ci/woodpecker/cron/locale Pipeline was successful
closes #939 closes #1505 Reviewed-on: #1504 Co-authored-by: M M Arif <mmarif@swatian.com> Co-committed-by: M M Arif <mmarif@swatian.com>
This commit is contained in:
parent bddf7db3a3
commit 1acf19d9af
98 changed files with 5883 additions and 5374 deletions
| @ -67,6 +67,8 @@ dependencies { | |||
implementation 'androidx.viewpager2:viewpager2:1.1.0' | ||||
implementation 'androidx.constraintlayout:constraintlayout:2.2.1' | ||||
implementation "androidx.legacy:legacy-support-v4:1.0.0" | ||||
implementation "androidx.navigation:navigation-fragment:2.9.0" | ||||
implementation "androidx.navigation:navigation-ui:2.9.0" | ||||
implementation "androidx.lifecycle:lifecycle-viewmodel:2.9.1" | ||||
testImplementation 'junit:junit:4.13.2' | ||||
androidTestImplementation 'androidx.test.ext:junit:1.2.1' | ||||
| @ -106,9 +108,9 @@ dependencies { | |||
implementation 'ch.acra:acra-mail:5.11.3' | ||||
implementation 'ch.acra:acra-limiter:5.11.3' | ||||
implementation 'ch.acra:acra-notification:5.11.3' | ||||
implementation 'androidx.room:room-runtime:2.7.1' | ||||
annotationProcessor 'androidx.room:room-compiler:2.7.1' | ||||
implementation "androidx.work:work-runtime:2.10.1" | ||||
implementation 'androidx.room:room-runtime:2.7.2' | ||||
annotationProcessor 'androidx.room:room-compiler:2.7.2' | ||||
implementation "androidx.work:work-runtime:2.10.2" | ||||
implementation "io.mikael:urlbuilder:2.0.9" | ||||
implementation "org.codeberg.gitnex-garage:emoji-java:v5.1.2" | ||||
//noinspection GradleDependency | ||||
| @ -117,7 +119,7 @@ dependencies { | |||
implementation 'com.github.chrisvest:stormpot:2.4.2' | ||||
implementation 'androidx.browser:browser:1.8.0' | ||||
implementation 'com.google.android.flexbox:flexbox:3.0.0' | ||||
implementation('org.codeberg.gitnex:tea4j-autodeploy:c4dd83143c') { | ||||
implementation('org.codeberg.gitnex:tea4j-autodeploy:0ad7eaf429') { | ||||
exclude module: 'org.apache.oltu.oauth2.common' | ||||
} | ||||
implementation 'io.github.amrdeveloper:codeview:1.3.9' | ||||
| |
| @ -127,18 +127,9 @@ | |||
android:name=".activities.CommitDetailActivity" | ||||
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation" | ||||
android:theme="@android:style/Theme.NoTitleBar"/> | ||||
<activity | ||||
android:name=".activities.SettingsAppearanceActivity" | ||||
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation"/> | ||||
<activity | ||||
android:name=".activities.SettingsCodeEditorActivity" | ||||
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation"/> | ||||
<activity | ||||
android:name=".activities.ProfileActivity" | ||||
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation"/> | ||||
<activity | ||||
android:name=".activities.SettingsSecurityActivity" | ||||
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation"/> | ||||
<activity | ||||
android:name=".activities.AddNewTeamMemberActivity" | ||||
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation"/> | ||||
| @ -159,12 +150,6 @@ | |||
android:name=".activities.CreatePullRequestActivity" | ||||
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation" | ||||
android:windowSoftInputMode="adjustResize"/> | ||||
<activity | ||||
android:name=".activities.SettingsGeneralActivity" | ||||
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation"/> | ||||
<activity | ||||
android:name=".activities.SettingsNotificationsActivity" | ||||
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation"/> | ||||
<activity | ||||
android:name=".activities.AdminCronTasksActivity" | ||||
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation"/> | ||||
| @ -179,9 +164,6 @@ | |||
android:name=".activities.CreateNoteActivity" | ||||
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation" | ||||
android:windowSoftInputMode="adjustResize"/> | ||||
<activity | ||||
android:name=".activities.SettingsBackupRestoreActivity" | ||||
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation" /> | ||||
| ||||
<meta-data | ||||
android:name="com.samsung.android.keepalive.density" | ||||
| |
| @ -169,10 +169,12 @@ public class CommitsActivity extends BaseActivity { | |||
repoName, | ||||
branchName, | ||||
null, | ||||
null, | ||||
null, | ||||
true, | ||||
false, | ||||
true, | ||||
1, | ||||
true, | ||||
pageSize, | ||||
resultLimit, | ||||
""); | ||||
| ||||
| @ -232,8 +234,10 @@ public class CommitsActivity extends BaseActivity { | |||
repoName, | ||||
branchName, | ||||
null, | ||||
null, | ||||
null, | ||||
true, | ||||
true, | ||||
false, | ||||
true, | ||||
page, | ||||
resultLimit, | ||||
| |
| @ -170,6 +170,7 @@ public class IssueDetailActivity extends BaseActivity | |||
private int loadingFinished = 0; | ||||
private MentionHelper mentionHelper; | ||||
private boolean pullRequestFetchAttempted = false; | ||||
private boolean issueInitialized = false; | ||||
| ||||
private enum Mode { | ||||
EDIT, | ||||
| @ -1094,6 +1095,11 @@ public class IssueDetailActivity extends BaseActivity | |||
| ||||
private void initWithIssue() { | ||||
| ||||
if (issueInitialized) { | ||||
return; | ||||
} | ||||
issueInitialized = true; | ||||
| ||||
if (!issue.getRepository().hasRepository()) { | ||||
getRepoInfo(); | ||||
} else { | ||||
| @ -1564,7 +1570,7 @@ public class IssueDetailActivity extends BaseActivity | |||
} | ||||
| ||||
private void checkAndInitWithIssue() { | ||||
if (loadingFinishedIssue || pullRequestFetchAttempted) { | ||||
if ((loadingFinishedIssue || pullRequestFetchAttempted) && !issueInitialized) { | ||||
initWithIssue(); | ||||
} | ||||
} | ||||
| |
File diff suppressed because it is too large Load diff
| @ -225,16 +225,15 @@ public class RepoDetailActivity extends BaseActivity implements BottomSheetListe | |||
return true; | ||||
} else if (id == R.id.filterPr) { | ||||
| ||||
BottomSheetPullRequestFilterFragment filterPrBottomSheet = | ||||
new BottomSheetPullRequestFilterFragment(); | ||||
filterPrBottomSheet.show(getSupportFragmentManager(), "repoFilterMenuPrBottomSheet"); | ||||
BottomSheetPullRequestFilterFragment bottomSheet = | ||||
BottomSheetPullRequestFilterFragment.newInstance(repository); | ||||
bottomSheet.show(getSupportFragmentManager(), "pullRequestFilterBottomSheet"); | ||||
return true; | ||||
} else if (id == R.id.filterMilestone) { | ||||
| ||||
BottomSheetMilestonesFilterFragment filterMilestoneBottomSheet = | ||||
new BottomSheetMilestonesFilterFragment(); | ||||
filterMilestoneBottomSheet.show( | ||||
getSupportFragmentManager(), "repoFilterMenuMilestoneBottomSheet"); | ||||
BottomSheetMilestonesFilterFragment bottomSheet = | ||||
BottomSheetMilestonesFilterFragment.newInstance(repository); | ||||
bottomSheet.show(getSupportFragmentManager(), "milestonesFilterBottomSheet"); | ||||
return true; | ||||
} else if (id == R.id.branchCommits) { | ||||
| ||||
| @ -242,11 +241,10 @@ public class RepoDetailActivity extends BaseActivity implements BottomSheetListe | |||
| ||||
ctx.startActivity(intent); | ||||
return true; | ||||
} else if (id == R.id.filterReleases && getAccount().requiresVersion("1.15.0")) { | ||||
BottomSheetReleasesTagsFragment bottomSheetReleasesTagsFragment = | ||||
new BottomSheetReleasesTagsFragment(); | ||||
bottomSheetReleasesTagsFragment.show( | ||||
getSupportFragmentManager(), "repoFilterReleasesMenuBottomSheet"); | ||||
} else if (id == R.id.filterReleases) { | ||||
BottomSheetReleasesTagsFragment bottomSheet = | ||||
BottomSheetReleasesTagsFragment.newInstance(repository); | ||||
bottomSheet.show(getSupportFragmentManager(), "releasesTagsFilterBottomSheet"); | ||||
return true; | ||||
} | ||||
| ||||
| |
| @ -1,463 +0,0 @@ | |||
package org.mian.gitnex.activities; | ||||
| ||||
import android.os.Build; | ||||
import android.os.Bundle; | ||||
import android.os.Handler; | ||||
import android.view.View; | ||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder; | ||||
import com.google.android.material.timepicker.MaterialTimePicker; | ||||
import java.util.LinkedHashMap; | ||||
import java.util.Locale; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.databinding.ActivitySettingsAppearanceBinding; | ||||
import org.mian.gitnex.fragments.SettingsFragment; | ||||
import org.mian.gitnex.helpers.AppDatabaseSettings; | ||||
import org.mian.gitnex.helpers.AppUtil; | ||||
import org.mian.gitnex.helpers.FontsOverride; | ||||
import org.mian.gitnex.helpers.SnackBar; | ||||
| ||||
/** | ||||
* @author M M Arif | ||||
*/ | ||||
public class SettingsAppearanceActivity extends BaseActivity { | ||||
| ||||
private static String[] customFontList; | ||||
private static int customFontSelectedChoice; | ||||
private static String[] themeList; | ||||
private static int themeSelectedChoice; | ||||
private static int langSelectedChoice; | ||||
private static String[] fragmentTabsAnimationList; | ||||
private static int fragmentTabsAnimationSelectedChoice; | ||||
| ||||
@Override | ||||
public void onCreate(Bundle savedInstanceState) { | ||||
| ||||
super.onCreate(savedInstanceState); | ||||
| ||||
ActivitySettingsAppearanceBinding activitySettingsAppearanceBinding = | ||||
ActivitySettingsAppearanceBinding.inflate(getLayoutInflater()); | ||||
setContentView(activitySettingsAppearanceBinding.getRoot()); | ||||
| ||||
LinkedHashMap<String, String> lang = new LinkedHashMap<>(); | ||||
lang.put("sys", getString(R.string.settingsLanguageSystem)); | ||||
for (String langCode : getResources().getStringArray(R.array.languages)) { | ||||
lang.put(langCode, getLanguageDisplayName(langCode)); | ||||
} | ||||
| ||||
customFontList = getResources().getStringArray(R.array.fonts); | ||||
| ||||
fragmentTabsAnimationList = getResources().getStringArray(R.array.fragmentTabsAnimation); | ||||
| ||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S || "S".equals(Build.VERSION.CODENAME)) { | ||||
themeList = getResources().getStringArray(R.array.themesAndroid12); | ||||
} else { | ||||
themeList = getResources().getStringArray(R.array.themes); | ||||
} | ||||
| ||||
activitySettingsAppearanceBinding.topAppBar.setNavigationOnClickListener(v -> finish()); | ||||
| ||||
String lightMinute = | ||||
String.valueOf( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_THEME_AUTO_LIGHT_MIN_KEY)); | ||||
String lightHour = | ||||
String.valueOf( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_THEME_AUTO_LIGHT_HOUR_KEY)); | ||||
if (lightMinute.length() == 1) { | ||||
lightMinute = "0" + lightMinute; | ||||
} | ||||
if (lightHour.length() == 1) { | ||||
lightHour = "0" + lightHour; | ||||
} | ||||
| ||||
String darkMinute = | ||||
String.valueOf( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_THEME_AUTO_DARK_MIN_KEY)); | ||||
String darkHour = | ||||
String.valueOf( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_THEME_AUTO_DARK_HOUR_KEY)); | ||||
if (darkMinute.length() == 1) { | ||||
darkMinute = "0" + darkMinute; | ||||
} | ||||
if (darkHour.length() == 1) { | ||||
darkHour = "0" + darkHour; | ||||
} | ||||
| ||||
fragmentTabsAnimationSelectedChoice = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_TABS_ANIMATION_KEY)); | ||||
customFontSelectedChoice = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_FONT_KEY)); | ||||
themeSelectedChoice = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_THEME_KEY)); | ||||
| ||||
activitySettingsAppearanceBinding.lightThemeSelectedTime.setText( | ||||
ctx.getResources() | ||||
.getString(R.string.settingsThemeTimeSelectedHint, lightHour, lightMinute)); | ||||
activitySettingsAppearanceBinding.darkThemeSelectedTime.setText( | ||||
ctx.getResources() | ||||
.getString(R.string.settingsThemeTimeSelectedHint, darkHour, darkMinute)); | ||||
activitySettingsAppearanceBinding.customFontSelected.setText( | ||||
customFontList[customFontSelectedChoice]); | ||||
activitySettingsAppearanceBinding.themeSelected.setText(themeList[themeSelectedChoice]); | ||||
activitySettingsAppearanceBinding.fragmentTabsAnimationFrameSelected.setText( | ||||
fragmentTabsAnimationList[fragmentTabsAnimationSelectedChoice]); | ||||
| ||||
if (themeList[themeSelectedChoice].startsWith("Auto")) { | ||||
activitySettingsAppearanceBinding.darkThemeTimeSelectionFrame.setVisibility( | ||||
View.VISIBLE); | ||||
activitySettingsAppearanceBinding.lightThemeTimeSelectionFrame.setVisibility( | ||||
View.VISIBLE); | ||||
} else { | ||||
activitySettingsAppearanceBinding.darkThemeTimeSelectionFrame.setVisibility(View.GONE); | ||||
activitySettingsAppearanceBinding.lightThemeTimeSelectionFrame.setVisibility(View.GONE); | ||||
} | ||||
| ||||
activitySettingsAppearanceBinding.switchCounterBadge.setChecked( | ||||
Boolean.parseBoolean( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_COUNTER_KEY))); | ||||
| ||||
// counter badge switcher | ||||
activitySettingsAppearanceBinding.switchCounterBadge.setOnCheckedChangeListener( | ||||
(buttonView, isChecked) -> { | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, String.valueOf(isChecked), AppDatabaseSettings.APP_COUNTER_KEY); | ||||
SnackBar.success( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
activitySettingsAppearanceBinding.counterBadgeFrame.setOnClickListener( | ||||
v -> | ||||
activitySettingsAppearanceBinding.switchCounterBadge.setChecked( | ||||
!activitySettingsAppearanceBinding.switchCounterBadge.isChecked())); | ||||
| ||||
// hide email and language in user profile screen switcher | ||||
activitySettingsAppearanceBinding.switchHideEmailLangInProfile.setChecked( | ||||
Boolean.parseBoolean( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, | ||||
AppDatabaseSettings.APP_USER_PROFILE_HIDE_EMAIL_LANGUAGE_KEY))); | ||||
| ||||
activitySettingsAppearanceBinding.switchHideEmailLangInProfile.setOnCheckedChangeListener( | ||||
(buttonView, isChecked) -> { | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, | ||||
String.valueOf(isChecked), | ||||
AppDatabaseSettings.APP_USER_PROFILE_HIDE_EMAIL_LANGUAGE_KEY); | ||||
SnackBar.success( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
activitySettingsAppearanceBinding.hideEmailLangInProfileFrame.setOnClickListener( | ||||
v -> | ||||
activitySettingsAppearanceBinding.switchHideEmailLangInProfile.setChecked( | ||||
!activitySettingsAppearanceBinding.switchHideEmailLangInProfile | ||||
.isChecked())); | ||||
| ||||
// hide email in app navigation drawer switcher | ||||
activitySettingsAppearanceBinding.switchHideEmailNavDrawer.setChecked( | ||||
Boolean.parseBoolean( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_USER_HIDE_EMAIL_IN_NAV_KEY))); | ||||
| ||||
activitySettingsAppearanceBinding.switchHideEmailNavDrawer.setOnCheckedChangeListener( | ||||
(buttonView, isChecked) -> { | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, | ||||
String.valueOf(isChecked), | ||||
AppDatabaseSettings.APP_USER_HIDE_EMAIL_IN_NAV_KEY); | ||||
SnackBar.success( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
activitySettingsAppearanceBinding.hideEmailNavDrawerFrame.setOnClickListener( | ||||
v -> | ||||
activitySettingsAppearanceBinding.switchHideEmailNavDrawer.setChecked( | ||||
!activitySettingsAppearanceBinding.switchHideEmailNavDrawer | ||||
.isChecked())); | ||||
| ||||
// show labels in lists(issues, pr) - default is color dots | ||||
activitySettingsAppearanceBinding.switchLabelsInListBadge.setChecked( | ||||
Boolean.parseBoolean( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_LABELS_IN_LIST_KEY))); | ||||
| ||||
activitySettingsAppearanceBinding.switchLabelsInListBadge.setOnCheckedChangeListener( | ||||
(buttonView, isChecked) -> { | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, | ||||
String.valueOf(isChecked), | ||||
AppDatabaseSettings.APP_LABELS_IN_LIST_KEY); | ||||
SnackBar.success( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
activitySettingsAppearanceBinding.labelsInListFrame.setOnClickListener( | ||||
v -> | ||||
activitySettingsAppearanceBinding.switchLabelsInListBadge.setChecked( | ||||
!activitySettingsAppearanceBinding.switchLabelsInListBadge | ||||
.isChecked())); | ||||
| ||||
// theme selection dialog | ||||
activitySettingsAppearanceBinding.themeSelectionFrame.setOnClickListener( | ||||
view -> { | ||||
MaterialAlertDialogBuilder materialAlertDialogBuilder = | ||||
new MaterialAlertDialogBuilder(ctx) | ||||
.setTitle(R.string.themeSelectorDialogTitle) | ||||
.setSingleChoiceItems( | ||||
themeList, | ||||
themeSelectedChoice, | ||||
(dialogInterfaceTheme, i) -> { | ||||
themeSelectedChoice = i; | ||||
activitySettingsAppearanceBinding.themeSelected | ||||
.setText(themeList[i]); | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, | ||||
String.valueOf(i), | ||||
AppDatabaseSettings.APP_THEME_KEY); | ||||
| ||||
SettingsFragment.refreshParent = true; | ||||
this.recreate(); | ||||
this.overridePendingTransition(0, 0); | ||||
dialogInterfaceTheme.dismiss(); | ||||
SnackBar.success( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
| ||||
materialAlertDialogBuilder.create().show(); | ||||
}); | ||||
| ||||
activitySettingsAppearanceBinding.lightThemeTimeSelectionFrame.setOnClickListener( | ||||
view -> lightTimePicker()); | ||||
| ||||
activitySettingsAppearanceBinding.darkThemeTimeSelectionFrame.setOnClickListener( | ||||
view -> darkTimePicker()); | ||||
| ||||
// custom font dialog | ||||
activitySettingsAppearanceBinding.customFontFrame.setOnClickListener( | ||||
view -> { | ||||
MaterialAlertDialogBuilder materialAlertDialogBuilder = | ||||
new MaterialAlertDialogBuilder(ctx) | ||||
.setTitle(R.string.settingsCustomFontSelectorDialogTitle) | ||||
.setCancelable(customFontSelectedChoice != -1) | ||||
.setSingleChoiceItems( | ||||
customFontList, | ||||
customFontSelectedChoice, | ||||
(dialogInterfaceCustomFont, i) -> { | ||||
customFontSelectedChoice = i; | ||||
activitySettingsAppearanceBinding.customFontSelected | ||||
.setText(customFontList[i]); | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, | ||||
String.valueOf(i), | ||||
AppDatabaseSettings.APP_FONT_KEY); | ||||
| ||||
new Handler() | ||||
.postDelayed( | ||||
() -> { | ||||
AppUtil.typeface = | ||||
null; // reset typeface | ||||
FontsOverride.setDefaultFont( | ||||
this); | ||||
SettingsFragment.refreshParent = | ||||
true; | ||||
this.recreate(); | ||||
this.overridePendingTransition( | ||||
0, 0); | ||||
}, | ||||
1000); | ||||
| ||||
dialogInterfaceCustomFont.dismiss(); | ||||
SnackBar.success( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
| ||||
materialAlertDialogBuilder.create().show(); | ||||
}); | ||||
| ||||
// fragment tabs animation dialog | ||||
activitySettingsAppearanceBinding.fragmentTabsAnimationFrame.setOnClickListener( | ||||
view -> { | ||||
MaterialAlertDialogBuilder materialAlertDialogBuilder = | ||||
new MaterialAlertDialogBuilder(ctx) | ||||
.setTitle(R.string.fragmentTabsAnimationHeader) | ||||
.setCancelable(fragmentTabsAnimationSelectedChoice != -1) | ||||
.setSingleChoiceItems( | ||||
fragmentTabsAnimationList, | ||||
fragmentTabsAnimationSelectedChoice, | ||||
(dialogInterfaceTabsAnimation, i) -> { | ||||
fragmentTabsAnimationSelectedChoice = i; | ||||
activitySettingsAppearanceBinding | ||||
.fragmentTabsAnimationFrameSelected.setText( | ||||
fragmentTabsAnimationList[i]); | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, | ||||
String.valueOf(i), | ||||
AppDatabaseSettings.APP_TABS_ANIMATION_KEY); | ||||
| ||||
SettingsFragment.refreshParent = true; | ||||
this.recreate(); | ||||
this.overridePendingTransition(0, 0); | ||||
dialogInterfaceTabsAnimation.dismiss(); | ||||
SnackBar.success( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
| ||||
materialAlertDialogBuilder.create().show(); | ||||
}); | ||||
| ||||
// language selector dialog | ||||
activitySettingsAppearanceBinding.helpTranslate.setOnClickListener( | ||||
v12 -> | ||||
AppUtil.openUrlInBrowser( | ||||
this, getResources().getString(R.string.crowdInLink))); | ||||
| ||||
String[] locale = | ||||
AppDatabaseSettings.getSettingsValue(ctx, AppDatabaseSettings.APP_LOCALE_KEY) | ||||
.split("\\|"); | ||||
langSelectedChoice = Integer.parseInt(locale[0]); | ||||
activitySettingsAppearanceBinding.tvLanguageSelected.setText( | ||||
lang.get(lang.keySet().toArray(new String[0])[langSelectedChoice])); | ||||
| ||||
// language dialog | ||||
activitySettingsAppearanceBinding.langFrame.setOnClickListener( | ||||
view -> { | ||||
MaterialAlertDialogBuilder materialAlertDialogBuilder = | ||||
new MaterialAlertDialogBuilder(ctx) | ||||
.setTitle(R.string.settingsLanguageSelectorDialogTitle) | ||||
.setCancelable(langSelectedChoice != -1) | ||||
.setNeutralButton(getString(R.string.cancelButton), null) | ||||
.setSingleChoiceItems( | ||||
lang.values().toArray(new String[0]), | ||||
langSelectedChoice, | ||||
(dialogInterface, i) -> { | ||||
String selectedLanguage = | ||||
lang.keySet().toArray(new String[0])[i]; | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, | ||||
i + "|" + selectedLanguage, | ||||
AppDatabaseSettings.APP_LOCALE_KEY); | ||||
| ||||
SettingsFragment.refreshParent = true; | ||||
this.overridePendingTransition(0, 0); | ||||
dialogInterface.dismiss(); | ||||
SnackBar.success( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
this.recreate(); | ||||
}); | ||||
| ||||
materialAlertDialogBuilder.create().show(); | ||||
}); | ||||
} | ||||
| ||||
public void lightTimePicker() { | ||||
| ||||
int hour = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_THEME_AUTO_LIGHT_HOUR_KEY)); | ||||
int minute = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_THEME_AUTO_LIGHT_MIN_KEY)); | ||||
| ||||
MaterialTimePicker materialTimePicker = | ||||
new MaterialTimePicker.Builder().setHour(hour).setMinute(minute).build(); | ||||
| ||||
materialTimePicker.addOnPositiveButtonClickListener( | ||||
selection -> { | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, | ||||
String.valueOf(materialTimePicker.getHour()), | ||||
AppDatabaseSettings.APP_THEME_AUTO_LIGHT_HOUR_KEY); | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, | ||||
String.valueOf(materialTimePicker.getMinute()), | ||||
AppDatabaseSettings.APP_THEME_AUTO_LIGHT_MIN_KEY); | ||||
SettingsFragment.refreshParent = true; | ||||
overridePendingTransition(0, 0); | ||||
SnackBar.success( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
recreate(); | ||||
}); | ||||
| ||||
materialTimePicker.show(getSupportFragmentManager(), "fragmentManager"); | ||||
} | ||||
| ||||
public void darkTimePicker() { | ||||
| ||||
int hour = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_THEME_AUTO_DARK_HOUR_KEY)); | ||||
int minute = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_THEME_AUTO_DARK_MIN_KEY)); | ||||
| ||||
MaterialTimePicker materialTimePicker = | ||||
new MaterialTimePicker.Builder().setHour(hour).setMinute(minute).build(); | ||||
| ||||
materialTimePicker.addOnPositiveButtonClickListener( | ||||
selection -> { | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, | ||||
String.valueOf(materialTimePicker.getHour()), | ||||
AppDatabaseSettings.APP_THEME_AUTO_DARK_HOUR_KEY); | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, | ||||
String.valueOf(materialTimePicker.getMinute()), | ||||
AppDatabaseSettings.APP_THEME_AUTO_DARK_MIN_KEY); | ||||
SettingsFragment.refreshParent = true; | ||||
overridePendingTransition(0, 0); | ||||
SnackBar.success( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
recreate(); | ||||
}); | ||||
| ||||
materialTimePicker.show(getSupportFragmentManager(), "fragmentManager"); | ||||
} | ||||
| ||||
private static String getLanguageDisplayName(String langCode) { | ||||
Locale english = new Locale("en"); | ||||
| ||||
String[] multiCodeLang = langCode.split("-"); | ||||
String countryCode; | ||||
if (langCode.contains("-")) { | ||||
langCode = multiCodeLang[0]; | ||||
countryCode = multiCodeLang[1]; | ||||
} else { | ||||
countryCode = ""; | ||||
} | ||||
| ||||
Locale translated = new Locale(langCode, countryCode); | ||||
return String.format( | ||||
"%s (%s)", | ||||
translated.getDisplayName(translated), translated.getDisplayName(english)); | ||||
} | ||||
} |
| @ -1,285 +0,0 @@ | |||
package org.mian.gitnex.activities; | ||||
| ||||
import static org.mian.gitnex.helpers.BackupUtil.backupDatabaseFile; | ||||
import static org.mian.gitnex.helpers.BackupUtil.checkpointIfWALEnabled; | ||||
import static org.mian.gitnex.helpers.BackupUtil.copyFile; | ||||
import static org.mian.gitnex.helpers.BackupUtil.copyFileWithStreams; | ||||
import static org.mian.gitnex.helpers.BackupUtil.getTempDir; | ||||
import static org.mian.gitnex.helpers.BackupUtil.unzip; | ||||
import static org.mian.gitnex.helpers.BackupUtil.zip; | ||||
| ||||
import android.app.Activity; | ||||
import android.content.Context; | ||||
import android.content.Intent; | ||||
import android.net.Uri; | ||||
import android.os.Bundle; | ||||
import androidx.activity.result.ActivityResultLauncher; | ||||
import androidx.activity.result.contract.ActivityResultContracts; | ||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder; | ||||
import java.io.File; | ||||
import java.io.FileNotFoundException; | ||||
import java.io.IOException; | ||||
import java.io.InputStream; | ||||
import java.io.OutputStream; | ||||
import java.time.LocalDate; | ||||
import java.util.ArrayList; | ||||
import java.util.List; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.database.api.BaseApi; | ||||
import org.mian.gitnex.database.api.UserAccountsApi; | ||||
import org.mian.gitnex.database.models.UserAccount; | ||||
import org.mian.gitnex.databinding.ActivitySettingsBackupRestoreBinding; | ||||
import org.mian.gitnex.helpers.AppUtil; | ||||
import org.mian.gitnex.helpers.SnackBar; | ||||
| ||||
/** | ||||
* @author M M Arif | ||||
*/ | ||||
public class SettingsBackupRestoreActivity extends BaseActivity { | ||||
| ||||
private final String DATABASE_NAME = "gitnex"; | ||||
private String BACKUP_DATABASE_NAME; | ||||
| ||||
@Override | ||||
public void onCreate(Bundle savedInstanceState) { | ||||
| ||||
super.onCreate(savedInstanceState); | ||||
| ||||
ActivitySettingsBackupRestoreBinding viewBinding = | ||||
ActivitySettingsBackupRestoreBinding.inflate(getLayoutInflater()); | ||||
setContentView(viewBinding.getRoot()); | ||||
| ||||
viewBinding.topAppBar.setNavigationOnClickListener(v -> finish()); | ||||
viewBinding.topAppBar.setTitle( | ||||
getResources() | ||||
.getString( | ||||
R.string.backupRestore, | ||||
getString(R.string.backup), | ||||
getString(R.string.restore))); | ||||
| ||||
BACKUP_DATABASE_NAME = ctx.getString(R.string.appName) + "-" + LocalDate.now() + ".backup"; | ||||
| ||||
viewBinding.backupDataFrame.setOnClickListener( | ||||
v -> { | ||||
MaterialAlertDialogBuilder materialAlertDialogBuilder = | ||||
new MaterialAlertDialogBuilder(ctx) | ||||
.setTitle(R.string.backup) | ||||
.setMessage( | ||||
getResources().getString(R.string.backupFilePopupText)) | ||||
.setNeutralButton( | ||||
R.string.cancelButton, | ||||
(dialog, which) -> dialog.dismiss()) | ||||
.setPositiveButton( | ||||
R.string.backup, | ||||
(dialog, which) -> requestBackupFileDownload()); | ||||
| ||||
materialAlertDialogBuilder.create().show(); | ||||
}); | ||||
| ||||
viewBinding.restoreDataFrame.setOnClickListener( | ||||
restoreDb -> { | ||||
MaterialAlertDialogBuilder materialAlertDialogBuilder = | ||||
new MaterialAlertDialogBuilder(ctx) | ||||
.setTitle(R.string.restore) | ||||
.setMessage( | ||||
getResources().getString(R.string.restoreFilePopupText)) | ||||
.setNeutralButton( | ||||
R.string.cancelButton, | ||||
(dialog, which) -> dialog.dismiss()) | ||||
.setPositiveButton( | ||||
R.string.restore, | ||||
(dialog, which) -> requestRestoreFile()); | ||||
| ||||
materialAlertDialogBuilder.create().show(); | ||||
}); | ||||
} | ||||
| ||||
ActivityResultLauncher<Intent> activityBackupFileLauncher = | ||||
registerForActivityResult( | ||||
new ActivityResultContracts.StartActivityForResult(), | ||||
result -> { | ||||
if (result.getResultCode() == Activity.RESULT_OK) { | ||||
| ||||
assert result.getData() != null; | ||||
| ||||
Uri backupFileUri = result.getData().getData(); | ||||
backupDatabaseThread(backupFileUri); | ||||
} | ||||
}); | ||||
| ||||
private void requestBackupFileDownload() { | ||||
| ||||
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); | ||||
intent.addCategory(Intent.CATEGORY_OPENABLE); | ||||
intent.putExtra(Intent.EXTRA_TITLE, BACKUP_DATABASE_NAME); | ||||
intent.setType("application/octet-stream"); | ||||
| ||||
activityBackupFileLauncher.launch(intent); | ||||
} | ||||
| ||||
private void backupDatabaseThread(Uri backupFileUri) { | ||||
| ||||
List<File> filesToZip = new ArrayList<>(); | ||||
| ||||
Thread backupDatabaseThread = | ||||
new Thread( | ||||
() -> { | ||||
File tempDir = getTempDir(ctx); | ||||
| ||||
try { | ||||
| ||||
checkpointIfWALEnabled(ctx, DATABASE_NAME); | ||||
| ||||
File databaseBackupFile = | ||||
backupDatabaseFile( | ||||
getDatabasePath(DATABASE_NAME).getPath(), | ||||
tempDir.getPath() + "/" + DATABASE_NAME); | ||||
| ||||
filesToZip.add(databaseBackupFile); | ||||
String tempZipFilename = "temp.backup"; | ||||
| ||||
boolean zipFileStatus = | ||||
zip(filesToZip, tempDir.getPath(), tempZipFilename); | ||||
| ||||
if (zipFileStatus) { | ||||
| ||||
File tempZipFile = new File(tempDir, tempZipFilename); | ||||
Uri zipFileUri = Uri.fromFile(tempZipFile); | ||||
| ||||
InputStream inputStream = | ||||
getContentResolver().openInputStream(zipFileUri); | ||||
OutputStream outputStream = | ||||
getContentResolver().openOutputStream(backupFileUri); | ||||
| ||||
boolean copySucceeded = | ||||
copyFileWithStreams(inputStream, outputStream); | ||||
| ||||
SnackBar.success( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.backupFileSuccess)); | ||||
| ||||
if (copySucceeded) { | ||||
tempZipFile.delete(); | ||||
} else { | ||||
SnackBar.error( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.backupFileError)); | ||||
} | ||||
} else { | ||||
SnackBar.error( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.backupFileError)); | ||||
} | ||||
| ||||
} catch (final Exception e) { | ||||
SnackBar.error( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.backupFileError)); | ||||
} finally { | ||||
for (File file : filesToZip) { | ||||
if (file != null && file.exists()) { | ||||
file.delete(); | ||||
} | ||||
} | ||||
} | ||||
}); | ||||
| ||||
backupDatabaseThread.setDaemon(false); | ||||
backupDatabaseThread.start(); | ||||
} | ||||
| ||||
private void requestRestoreFile() { | ||||
| ||||
Intent intentRestore = new Intent(Intent.ACTION_OPEN_DOCUMENT); | ||||
intentRestore.addCategory(Intent.CATEGORY_OPENABLE); | ||||
intentRestore.setType("*/*"); | ||||
String[] mimeTypes = {"application/octet-stream", "application/x-zip"}; | ||||
intentRestore.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes); | ||||
| ||||
activityRestoreFileLauncher.launch(intentRestore); | ||||
} | ||||
| ||||
ActivityResultLauncher<Intent> activityRestoreFileLauncher = | ||||
registerForActivityResult( | ||||
new ActivityResultContracts.StartActivityForResult(), | ||||
result -> { | ||||
if (result.getResultCode() == Activity.RESULT_OK) { | ||||
| ||||
assert result.getData() != null; | ||||
| ||||
Uri restoreFileUri = result.getData().getData(); | ||||
assert restoreFileUri != null; | ||||
| ||||
try { | ||||
InputStream inputStream = | ||||
getContentResolver().openInputStream(restoreFileUri); | ||||
restoreDatabaseThread(inputStream); | ||||
} catch (FileNotFoundException e) { | ||||
SnackBar.error( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.restoreError)); | ||||
} | ||||
} | ||||
}); | ||||
| ||||
private void restoreDatabaseThread(InputStream inputStream) { | ||||
| ||||
Thread restoreDatabaseThread = | ||||
new Thread( | ||||
() -> { | ||||
boolean exceptionOccurred = false; | ||||
| ||||
try { | ||||
| ||||
String tempDir = getTempDir(ctx).getPath(); | ||||
| ||||
unzip(inputStream, tempDir); | ||||
checkpointIfWALEnabled(ctx, DATABASE_NAME); | ||||
restoreDatabaseFile(ctx, tempDir, DATABASE_NAME); | ||||
| ||||
UserAccountsApi userAccountsApi = | ||||
BaseApi.getInstance(ctx, UserAccountsApi.class); | ||||
assert userAccountsApi != null; | ||||
UserAccount account = userAccountsApi.getAccountById(1); | ||||
AppUtil.switchToAccount(ctx, account); | ||||
} catch (final Exception e) { | ||||
| ||||
exceptionOccurred = true; | ||||
SnackBar.error( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.restoreError)); | ||||
} finally { | ||||
if (!exceptionOccurred) { | ||||
| ||||
runOnUiThread(this::restartApp); | ||||
} | ||||
} | ||||
}); | ||||
| ||||
restoreDatabaseThread.setDaemon(false); | ||||
restoreDatabaseThread.start(); | ||||
} | ||||
| ||||
public void restoreDatabaseFile(Context context, String tempDir, String nameOfFileToRestore) | ||||
throws IOException { | ||||
| ||||
File currentDbFile = new File(context.getDatabasePath(DATABASE_NAME).getPath()); | ||||
File newDbFile = new File(tempDir + "/" + nameOfFileToRestore); | ||||
if (newDbFile.exists()) { | ||||
copyFile(newDbFile, currentDbFile, false); | ||||
} | ||||
} | ||||
| ||||
public void restartApp() { | ||||
Intent i = ctx.getPackageManager().getLaunchIntentForPackage(ctx.getPackageName()); | ||||
assert i != null; | ||||
startActivity(Intent.makeRestartActivityTask(i.getComponent())); | ||||
Runtime.getRuntime().exit(0); | ||||
} | ||||
} |
| @ -1,162 +0,0 @@ | |||
package org.mian.gitnex.activities; | ||||
| ||||
import android.os.Bundle; | ||||
import android.view.View; | ||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.databinding.ActivitySettingsCodeEditorBinding; | ||||
import org.mian.gitnex.fragments.SettingsFragment; | ||||
import org.mian.gitnex.helpers.AppDatabaseSettings; | ||||
import org.mian.gitnex.helpers.SnackBar; | ||||
| ||||
/** | ||||
* @author M M Arif | ||||
*/ | ||||
public class SettingsCodeEditorActivity extends BaseActivity { | ||||
| ||||
private static String[] colorList; | ||||
private static int colorSelectedChoice; | ||||
private static String[] indentationList; | ||||
private static int indentationSelectedChoice; | ||||
private static String[] indentationTabsList; | ||||
private static int indentationTabsSelectedChoice; | ||||
| ||||
@Override | ||||
public void onCreate(Bundle savedInstanceState) { | ||||
| ||||
super.onCreate(savedInstanceState); | ||||
| ||||
ActivitySettingsCodeEditorBinding activitySettingsCodeEditorBinding = | ||||
ActivitySettingsCodeEditorBinding.inflate(getLayoutInflater()); | ||||
setContentView(activitySettingsCodeEditorBinding.getRoot()); | ||||
| ||||
activitySettingsCodeEditorBinding.topAppBar.setNavigationOnClickListener(v -> finish()); | ||||
| ||||
// color selector dialog | ||||
colorList = getResources().getStringArray(R.array.ceColors); | ||||
colorSelectedChoice = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_CE_SYNTAX_HIGHLIGHT_KEY)); | ||||
activitySettingsCodeEditorBinding.ceColorSelected.setText(colorList[colorSelectedChoice]); | ||||
| ||||
activitySettingsCodeEditorBinding.ceColorSelectionFrame.setOnClickListener( | ||||
view -> { | ||||
MaterialAlertDialogBuilder materialAlertDialogBuilder = | ||||
new MaterialAlertDialogBuilder(ctx) | ||||
.setTitle(R.string.ceSyntaxHighlightColor) | ||||
.setSingleChoiceItems( | ||||
colorList, | ||||
colorSelectedChoice, | ||||
(dialogInterfaceColor, i) -> { | ||||
colorSelectedChoice = i; | ||||
activitySettingsCodeEditorBinding.ceColorSelected | ||||
.setText(colorList[i]); | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, | ||||
String.valueOf(i), | ||||
AppDatabaseSettings | ||||
.APP_CE_SYNTAX_HIGHLIGHT_KEY); | ||||
| ||||
SettingsFragment.refreshParent = true; | ||||
this.recreate(); | ||||
this.overridePendingTransition(0, 0); | ||||
dialogInterfaceColor.dismiss(); | ||||
SnackBar.success( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
| ||||
materialAlertDialogBuilder.create().show(); | ||||
}); | ||||
| ||||
// indentation selector dialog | ||||
indentationList = getResources().getStringArray(R.array.ceIndentation); | ||||
indentationSelectedChoice = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_CE_INDENTATION_KEY)); | ||||
activitySettingsCodeEditorBinding.indentationSelected.setText( | ||||
indentationList[indentationSelectedChoice]); | ||||
| ||||
activitySettingsCodeEditorBinding.indentationSelectionFrame.setOnClickListener( | ||||
view -> { | ||||
MaterialAlertDialogBuilder materialAlertDialogBuilder = | ||||
new MaterialAlertDialogBuilder(ctx) | ||||
.setTitle(R.string.ceIndentation) | ||||
.setSingleChoiceItems( | ||||
indentationList, | ||||
indentationSelectedChoice, | ||||
(dialogInterfaceColor, i) -> { | ||||
indentationSelectedChoice = i; | ||||
activitySettingsCodeEditorBinding | ||||
.indentationSelected.setText( | ||||
indentationList[i]); | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, | ||||
String.valueOf(i), | ||||
AppDatabaseSettings.APP_CE_INDENTATION_KEY); | ||||
| ||||
SettingsFragment.refreshParent = true; | ||||
this.recreate(); | ||||
this.overridePendingTransition(0, 0); | ||||
dialogInterfaceColor.dismiss(); | ||||
SnackBar.success( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
| ||||
materialAlertDialogBuilder.create().show(); | ||||
}); | ||||
| ||||
// indentation tabs selector dialog | ||||
if (indentationList[indentationSelectedChoice].startsWith("Tabs")) { | ||||
activitySettingsCodeEditorBinding.indentationTabsSelectionFrame.setVisibility( | ||||
View.VISIBLE); | ||||
} else { | ||||
activitySettingsCodeEditorBinding.indentationTabsSelectionFrame.setVisibility( | ||||
View.GONE); | ||||
} | ||||
| ||||
indentationTabsList = getResources().getStringArray(R.array.ceIndentationTabsWidth); | ||||
indentationTabsSelectedChoice = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_CE_TABS_WIDTH_KEY)); | ||||
activitySettingsCodeEditorBinding.indentationTabsSelected.setText( | ||||
indentationTabsList[indentationTabsSelectedChoice]); | ||||
| ||||
activitySettingsCodeEditorBinding.indentationTabsSelectionFrame.setOnClickListener( | ||||
view -> { | ||||
MaterialAlertDialogBuilder materialAlertDialogBuilder = | ||||
new MaterialAlertDialogBuilder(ctx) | ||||
.setTitle(R.string.ceIndentationTabsWidth) | ||||
.setSingleChoiceItems( | ||||
indentationTabsList, | ||||
indentationTabsSelectedChoice, | ||||
(dialogInterfaceColor, i) -> { | ||||
indentationTabsSelectedChoice = i; | ||||
activitySettingsCodeEditorBinding | ||||
.indentationTabsSelected.setText( | ||||
indentationTabsList[i]); | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, | ||||
String.valueOf(i), | ||||
AppDatabaseSettings.APP_CE_TABS_WIDTH_KEY); | ||||
| ||||
SettingsFragment.refreshParent = true; | ||||
this.recreate(); | ||||
this.overridePendingTransition(0, 0); | ||||
dialogInterfaceColor.dismiss(); | ||||
SnackBar.success( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
| ||||
materialAlertDialogBuilder.create().show(); | ||||
}); | ||||
} | ||||
} |
| @ -1,161 +0,0 @@ | |||
package org.mian.gitnex.activities; | ||||
| ||||
import android.os.Bundle; | ||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder; | ||||
import java.util.ArrayList; | ||||
import java.util.Arrays; | ||||
import java.util.List; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.databinding.ActivitySettingsGeneralBinding; | ||||
import org.mian.gitnex.helpers.AppDatabaseSettings; | ||||
import org.mian.gitnex.helpers.SnackBar; | ||||
| ||||
/** | ||||
* @author M M Arif | ||||
*/ | ||||
public class SettingsGeneralActivity extends BaseActivity { | ||||
| ||||
private static int homeScreenSelectedChoice; | ||||
private static int defaultLinkHandlerScreenSelectedChoice; | ||||
private ActivitySettingsGeneralBinding viewBinding; | ||||
| ||||
@Override | ||||
public void onCreate(Bundle savedInstanceState) { | ||||
| ||||
super.onCreate(savedInstanceState); | ||||
| ||||
viewBinding = ActivitySettingsGeneralBinding.inflate(getLayoutInflater()); | ||||
setContentView(viewBinding.getRoot()); | ||||
| ||||
viewBinding.topAppBar.setNavigationOnClickListener(v -> finish()); | ||||
| ||||
// home screen | ||||
String[] appHomeDefaultScreen = | ||||
getResources().getStringArray(R.array.appDefaultHomeScreenNew); | ||||
| ||||
homeScreenSelectedChoice = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_HOME_SCREEN_KEY)); | ||||
| ||||
viewBinding.homeScreenSelected.setText(appHomeDefaultScreen[homeScreenSelectedChoice]); | ||||
| ||||
viewBinding.homeScreenFrame.setOnClickListener( | ||||
setDefaultHomeScreen -> { | ||||
MaterialAlertDialogBuilder materialAlertDialogBuilder = | ||||
new MaterialAlertDialogBuilder(ctx) | ||||
.setTitle(R.string.settingsHomeScreenSelectorDialogTitle) | ||||
.setCancelable(homeScreenSelectedChoice != -1) | ||||
.setSingleChoiceItems( | ||||
appHomeDefaultScreen, | ||||
homeScreenSelectedChoice, | ||||
(dialogInterfaceHomeScreen, i) -> { | ||||
homeScreenSelectedChoice = i; | ||||
viewBinding.homeScreenSelected.setText( | ||||
appHomeDefaultScreen[i]); | ||||
| ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, | ||||
String.valueOf(i), | ||||
AppDatabaseSettings.APP_HOME_SCREEN_KEY); | ||||
| ||||
dialogInterfaceHomeScreen.dismiss(); | ||||
SnackBar.success( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
| ||||
materialAlertDialogBuilder.create().show(); | ||||
}); | ||||
// home screen | ||||
| ||||
// link handler | ||||
String[] linkHandlerDefaultScreenList = | ||||
getResources().getStringArray(R.array.linkHandlerDefaultScreen); | ||||
List<String> linkHandlerDefaultScreen = | ||||
new ArrayList<>(Arrays.asList(linkHandlerDefaultScreenList)); | ||||
| ||||
String[] linksArray = new String[linkHandlerDefaultScreen.size()]; | ||||
linkHandlerDefaultScreen.toArray(linksArray); | ||||
| ||||
defaultLinkHandlerScreenSelectedChoice = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_LINK_HANDLER_KEY)); | ||||
viewBinding.generalDeepLinkSelected.setText( | ||||
linksArray[defaultLinkHandlerScreenSelectedChoice]); | ||||
| ||||
viewBinding.setDefaultLinkHandler.setOnClickListener( | ||||
setDefaultLinkHandler -> { | ||||
MaterialAlertDialogBuilder materialAlertDialogBuilder = | ||||
new MaterialAlertDialogBuilder(ctx) | ||||
.setTitle(R.string.linkSelectorDialogTitle) | ||||
.setCancelable(defaultLinkHandlerScreenSelectedChoice != -1) | ||||
.setSingleChoiceItems( | ||||
linksArray, | ||||
defaultLinkHandlerScreenSelectedChoice, | ||||
(dialogInterfaceHomeScreen, i) -> { | ||||
defaultLinkHandlerScreenSelectedChoice = i; | ||||
viewBinding.generalDeepLinkSelected.setText( | ||||
linksArray[i]); | ||||
| ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, | ||||
String.valueOf(i), | ||||
AppDatabaseSettings.APP_LINK_HANDLER_KEY); | ||||
| ||||
dialogInterfaceHomeScreen.dismiss(); | ||||
SnackBar.success( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
| ||||
materialAlertDialogBuilder.create().show(); | ||||
}); | ||||
// link handler | ||||
| ||||
// custom tabs switcher | ||||
viewBinding.switchTabs.setChecked( | ||||
Boolean.parseBoolean( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_CUSTOM_BROWSER_KEY))); | ||||
viewBinding.switchTabs.setOnCheckedChangeListener( | ||||
(buttonView, isChecked) -> { | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, | ||||
String.valueOf(isChecked), | ||||
AppDatabaseSettings.APP_CUSTOM_BROWSER_KEY); | ||||
SnackBar.success( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
viewBinding.customTabsFrame.setOnClickListener( | ||||
v -> viewBinding.switchTabs.setChecked(!viewBinding.switchTabs.isChecked())); | ||||
// custom tabs switcher | ||||
| ||||
// crash reports switcher | ||||
viewBinding.crashReportsSwitch.setChecked( | ||||
Boolean.parseBoolean( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_CRASH_REPORTS_KEY))); | ||||
viewBinding.crashReportsSwitch.setOnCheckedChangeListener( | ||||
(buttonView, isChecked) -> { | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, | ||||
String.valueOf(isChecked), | ||||
AppDatabaseSettings.APP_CRASH_REPORTS_KEY); | ||||
SnackBar.success( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
viewBinding.enableSendReports.setOnClickListener( | ||||
v -> | ||||
viewBinding.crashReportsSwitch.setChecked( | ||||
!viewBinding.crashReportsSwitch.isChecked())); | ||||
// crash reports switcher | ||||
} | ||||
} |
| @ -1,112 +0,0 @@ | |||
package org.mian.gitnex.activities; | ||||
| ||||
import android.os.Bundle; | ||||
import android.view.View; | ||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.databinding.ActivitySettingsNotificationsBinding; | ||||
import org.mian.gitnex.fragments.SettingsFragment; | ||||
import org.mian.gitnex.helpers.AppDatabaseSettings; | ||||
import org.mian.gitnex.helpers.AppUtil; | ||||
import org.mian.gitnex.helpers.SnackBar; | ||||
import org.mian.gitnex.notifications.Notifications; | ||||
| ||||
/** | ||||
* @author M M Arif | ||||
* @author opyale | ||||
*/ | ||||
public class SettingsNotificationsActivity extends BaseActivity { | ||||
| ||||
private ActivitySettingsNotificationsBinding viewBinding; | ||||
private static String[] pollingDelayList; | ||||
private static int pollingDelayListSelectedChoice; | ||||
| ||||
@Override | ||||
public void onCreate(Bundle savedInstanceState) { | ||||
| ||||
super.onCreate(savedInstanceState); | ||||
| ||||
viewBinding = ActivitySettingsNotificationsBinding.inflate(getLayoutInflater()); | ||||
setContentView(viewBinding.getRoot()); | ||||
| ||||
viewBinding.topAppBar.setNavigationOnClickListener(v -> finish()); | ||||
| ||||
viewBinding.enableNotificationsMode.setChecked( | ||||
Boolean.parseBoolean( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_NOTIFICATIONS_KEY))); | ||||
| ||||
if (!viewBinding.enableNotificationsMode.isChecked()) { | ||||
AppUtil.setMultiVisibility(View.GONE, viewBinding.pollingDelayFrame); | ||||
} | ||||
| ||||
viewBinding.enableNotificationsMode.setOnCheckedChangeListener( | ||||
(buttonView, isChecked) -> { | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, | ||||
String.valueOf(isChecked), | ||||
AppDatabaseSettings.APP_NOTIFICATIONS_KEY); | ||||
| ||||
if (isChecked) { | ||||
Notifications.startWorker(ctx); | ||||
AppUtil.setMultiVisibility(View.VISIBLE, viewBinding.pollingDelayFrame); | ||||
} else { | ||||
Notifications.stopWorker(ctx); | ||||
AppUtil.setMultiVisibility(View.GONE, viewBinding.pollingDelayFrame); | ||||
} | ||||
| ||||
SnackBar.success( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
viewBinding.enableNotificationsFrame.setOnClickListener( | ||||
v -> | ||||
viewBinding.enableNotificationsMode.setChecked( | ||||
!viewBinding.enableNotificationsMode.isChecked())); | ||||
| ||||
// polling delay | ||||
pollingDelayList = getResources().getStringArray(R.array.notificationsPollingDelay); | ||||
pollingDelayListSelectedChoice = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_NOTIFICATIONS_DELAY_KEY)); | ||||
viewBinding.pollingDelaySelected.setText(pollingDelayList[pollingDelayListSelectedChoice]); | ||||
| ||||
viewBinding.pollingDelayFrame.setOnClickListener( | ||||
view -> { | ||||
MaterialAlertDialogBuilder materialAlertDialogBuilder = | ||||
new MaterialAlertDialogBuilder(ctx) | ||||
.setTitle(R.string.pollingDelayDialogHeaderText) | ||||
.setSingleChoiceItems( | ||||
pollingDelayList, | ||||
pollingDelayListSelectedChoice, | ||||
(dialogInterfaceColor, i) -> { | ||||
pollingDelayListSelectedChoice = i; | ||||
viewBinding.pollingDelaySelected.setText( | ||||
pollingDelayList[ | ||||
pollingDelayListSelectedChoice]); | ||||
| ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, | ||||
String.valueOf(i), | ||||
AppDatabaseSettings | ||||
.APP_NOTIFICATIONS_DELAY_KEY); | ||||
| ||||
Notifications.stopWorker(ctx); | ||||
Notifications.startWorker(ctx); | ||||
| ||||
SettingsFragment.refreshParent = true; | ||||
this.recreate(); | ||||
this.overridePendingTransition(0, 0); | ||||
dialogInterfaceColor.dismiss(); | ||||
SnackBar.success( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
| ||||
materialAlertDialogBuilder.create().show(); | ||||
}); | ||||
} | ||||
} |
| @ -1,279 +0,0 @@ | |||
package org.mian.gitnex.activities; | ||||
| ||||
import static androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG; | ||||
import static androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL; | ||||
| ||||
import android.app.KeyguardManager; | ||||
import android.content.Context; | ||||
import android.os.Bundle; | ||||
import androidx.biometric.BiometricManager; | ||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder; | ||||
import java.io.File; | ||||
import java.io.IOException; | ||||
import org.apache.commons.io.FileUtils; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.databinding.ActivitySettingsSecurityBinding; | ||||
import org.mian.gitnex.helpers.AppDatabaseSettings; | ||||
import org.mian.gitnex.helpers.AppUtil; | ||||
import org.mian.gitnex.helpers.SnackBar; | ||||
import org.mian.gitnex.helpers.ssl.MemorizingTrustManager; | ||||
| ||||
/** | ||||
* @author M M Arif | ||||
*/ | ||||
public class SettingsSecurityActivity extends BaseActivity { | ||||
| ||||
private static String[] cacheSizeDataList; | ||||
private static int cacheSizeDataSelectedChoice; | ||||
private static String[] cacheSizeImagesList; | ||||
private static int cacheSizeImagesSelectedChoice; | ||||
| ||||
@Override | ||||
public void onCreate(Bundle savedInstanceState) { | ||||
| ||||
super.onCreate(savedInstanceState); | ||||
| ||||
ActivitySettingsSecurityBinding activitySettingsSecurityBinding = | ||||
ActivitySettingsSecurityBinding.inflate(getLayoutInflater()); | ||||
setContentView(activitySettingsSecurityBinding.getRoot()); | ||||
| ||||
activitySettingsSecurityBinding.topAppBar.setNavigationOnClickListener(v -> finish()); | ||||
| ||||
cacheSizeDataList = getResources().getStringArray(R.array.cacheSizeList); | ||||
cacheSizeImagesList = getResources().getStringArray(R.array.cacheSizeList); | ||||
| ||||
cacheSizeDataSelectedChoice = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_DATA_CACHE_KEY)); | ||||
cacheSizeImagesSelectedChoice = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_IMAGES_CACHE_KEY)); | ||||
| ||||
activitySettingsSecurityBinding.cacheSizeDataSelected.setText( | ||||
cacheSizeDataList[cacheSizeDataSelectedChoice]); | ||||
activitySettingsSecurityBinding.cacheSizeImagesSelected.setText( | ||||
cacheSizeImagesList[cacheSizeImagesSelectedChoice]); | ||||
| ||||
activitySettingsSecurityBinding.switchBiometric.setChecked( | ||||
Boolean.parseBoolean( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
ctx, AppDatabaseSettings.APP_BIOMETRIC_KEY))); | ||||
| ||||
// biometric switcher | ||||
activitySettingsSecurityBinding.switchBiometric.setOnCheckedChangeListener( | ||||
(buttonView, isChecked) -> { | ||||
if (isChecked) { | ||||
| ||||
BiometricManager biometricManager = BiometricManager.from(ctx); | ||||
KeyguardManager keyguardManager = | ||||
(KeyguardManager) ctx.getSystemService(Context.KEYGUARD_SERVICE); | ||||
| ||||
if (!keyguardManager.isDeviceSecure()) { | ||||
| ||||
switch (biometricManager.canAuthenticate( | ||||
BIOMETRIC_STRONG | DEVICE_CREDENTIAL)) { | ||||
case BiometricManager.BIOMETRIC_SUCCESS: | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, "true", AppDatabaseSettings.APP_BIOMETRIC_KEY); | ||||
SnackBar.success( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
break; | ||||
case BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE: | ||||
case BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED: | ||||
case BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED: | ||||
case BiometricManager.BIOMETRIC_STATUS_UNKNOWN: | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, "false", AppDatabaseSettings.APP_BIOMETRIC_KEY); | ||||
activitySettingsSecurityBinding.switchBiometric.setChecked( | ||||
false); | ||||
SnackBar.error( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.biometricNotSupported)); | ||||
break; | ||||
case BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE: | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, "false", AppDatabaseSettings.APP_BIOMETRIC_KEY); | ||||
activitySettingsSecurityBinding.switchBiometric.setChecked( | ||||
false); | ||||
SnackBar.error( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.biometricNotAvailable)); | ||||
break; | ||||
case BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED: | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, "false", AppDatabaseSettings.APP_BIOMETRIC_KEY); | ||||
activitySettingsSecurityBinding.switchBiometric.setChecked( | ||||
false); | ||||
SnackBar.info( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.enrollBiometric)); | ||||
break; | ||||
} | ||||
} else { | ||||
| ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, "true", AppDatabaseSettings.APP_BIOMETRIC_KEY); | ||||
SnackBar.success( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
} | ||||
} else { | ||||
| ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, "false", AppDatabaseSettings.APP_BIOMETRIC_KEY); | ||||
SnackBar.success( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
} | ||||
}); | ||||
| ||||
activitySettingsSecurityBinding.biometricFrame.setOnClickListener( | ||||
v -> | ||||
activitySettingsSecurityBinding.switchBiometric.setChecked( | ||||
!activitySettingsSecurityBinding.switchBiometric.isChecked())); | ||||
| ||||
// clear cache setter | ||||
File cacheDir = appCtx.getCacheDir(); | ||||
activitySettingsSecurityBinding.clearCacheSelected.setText( | ||||
FileUtils.byteCountToDisplaySize((int) FileUtils.sizeOfDirectory(cacheDir))); | ||||
| ||||
// clear cache | ||||
activitySettingsSecurityBinding.clearCacheSelectionFrame.setOnClickListener( | ||||
v1 -> { | ||||
MaterialAlertDialogBuilder materialAlertDialogBuilder = | ||||
new MaterialAlertDialogBuilder(ctx) | ||||
.setTitle(R.string.clearCacheDialogHeader) | ||||
.setMessage( | ||||
getResources() | ||||
.getString(R.string.clearCacheDialogMessage)) | ||||
.setNeutralButton( | ||||
R.string.cancelButton, | ||||
(dialog, which) -> dialog.dismiss()) | ||||
.setPositiveButton( | ||||
R.string.menuDeleteText, | ||||
(dialog, which) -> { | ||||
try { | ||||
| ||||
FileUtils.deleteDirectory(cacheDir); | ||||
FileUtils.forceMkdir(cacheDir); | ||||
this.recreate(); | ||||
this.overridePendingTransition(0, 0); | ||||
} catch (IOException e) { | ||||
| ||||
// Log.e("SettingsSecurity", e.toString()); | ||||
} | ||||
}); | ||||
| ||||
materialAlertDialogBuilder.create().show(); | ||||
}); | ||||
| ||||
// cache size images selection dialog | ||||
activitySettingsSecurityBinding.cacheSizeImagesSelectionFrame.setOnClickListener( | ||||
view -> { | ||||
MaterialAlertDialogBuilder materialAlertDialogBuilder = | ||||
new MaterialAlertDialogBuilder(ctx) | ||||
.setTitle(R.string.cacheSizeImagesDialogHeader) | ||||
.setCancelable(cacheSizeImagesSelectedChoice != -1) | ||||
.setSingleChoiceItems( | ||||
cacheSizeImagesList, | ||||
cacheSizeImagesSelectedChoice, | ||||
(dialogInterfaceTheme, i) -> { | ||||
cacheSizeImagesSelectedChoice = i; | ||||
activitySettingsSecurityBinding | ||||
.cacheSizeImagesSelected.setText( | ||||
cacheSizeImagesList[i]); | ||||
| ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, | ||||
cacheSizeImagesList[i], | ||||
AppDatabaseSettings | ||||
.APP_IMAGES_CACHE_SIZE_KEY); | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, | ||||
String.valueOf(i), | ||||
AppDatabaseSettings.APP_IMAGES_CACHE_KEY); | ||||
| ||||
dialogInterfaceTheme.dismiss(); | ||||
SnackBar.success( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
| ||||
materialAlertDialogBuilder.create().show(); | ||||
}); | ||||
| ||||
// cache size data selection dialog | ||||
activitySettingsSecurityBinding.cacheSizeDataSelectionFrame.setOnClickListener( | ||||
view -> { | ||||
MaterialAlertDialogBuilder materialAlertDialogBuilder = | ||||
new MaterialAlertDialogBuilder(ctx) | ||||
.setTitle(R.string.cacheSizeDataDialogHeader) | ||||
.setCancelable(cacheSizeDataSelectedChoice != -1) | ||||
.setSingleChoiceItems( | ||||
cacheSizeDataList, | ||||
cacheSizeDataSelectedChoice, | ||||
(dialogInterfaceTheme, i) -> { | ||||
cacheSizeDataSelectedChoice = i; | ||||
activitySettingsSecurityBinding | ||||
.cacheSizeDataSelected.setText( | ||||
cacheSizeDataList[i]); | ||||
| ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, | ||||
cacheSizeDataList[i], | ||||
AppDatabaseSettings | ||||
.APP_DATA_CACHE_SIZE_KEY); | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
ctx, | ||||
String.valueOf(i), | ||||
AppDatabaseSettings.APP_DATA_CACHE_KEY); | ||||
| ||||
dialogInterfaceTheme.dismiss(); | ||||
SnackBar.success( | ||||
ctx, | ||||
findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
| ||||
materialAlertDialogBuilder.create().show(); | ||||
}); | ||||
| ||||
// certs deletion | ||||
activitySettingsSecurityBinding.certsFrame.setOnClickListener( | ||||
v1 -> { | ||||
MaterialAlertDialogBuilder materialAlertDialogBuilder = | ||||
new MaterialAlertDialogBuilder(ctx) | ||||
.setTitle(R.string.settingsCertsPopupTitle) | ||||
.setMessage( | ||||
getResources() | ||||
.getString(R.string.settingsCertsPopupMessage)) | ||||
.setNeutralButton( | ||||
R.string.cancelButton, | ||||
(dialog, which) -> dialog.dismiss()) | ||||
.setPositiveButton( | ||||
R.string.menuDeleteText, | ||||
(dialog, which) -> { | ||||
appCtx.getSharedPreferences( | ||||
MemorizingTrustManager | ||||
.KEYSTORE_NAME, | ||||
Context.MODE_PRIVATE) | ||||
.edit() | ||||
.remove(MemorizingTrustManager.KEYSTORE_KEY) | ||||
.apply(); | ||||
AppUtil.logout(this); | ||||
}); | ||||
| ||||
materialAlertDialogBuilder.create().show(); | ||||
}); | ||||
} | ||||
} |
| @ -40,7 +40,7 @@ import org.mian.gitnex.helpers.contexts.RepositoryContext; | |||
/** | ||||
* @author M M Arif | ||||
*/ | ||||
public class DashboardAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { | ||||
public class ActivitiesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { | ||||
| ||||
private final Context context; | ||||
TinyDB tinyDb; | ||||
| @ -50,7 +50,7 @@ public class DashboardAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold | |||
private Intent intent; | ||||
public boolean isUserOrg = false; | ||||
| ||||
public DashboardAdapter(List<Activity> dataList, Context ctx) { | ||||
public ActivitiesAdapter(List<Activity> dataList, Context ctx) { | ||||
this.context = ctx; | ||||
this.activityList = dataList; | ||||
this.tinyDb = TinyDB.getInstance(ctx); | ||||
| @ -59,8 +59,8 @@ public class DashboardAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold | |||
@NonNull @Override | ||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { | ||||
LayoutInflater inflater = LayoutInflater.from(context); | ||||
return new DashboardAdapter.DashboardHolder( | ||||
inflater.inflate(R.layout.list_dashboard_activity, parent, false)); | ||||
return new ActivitiesAdapter.DashboardHolder( | ||||
inflater.inflate(R.layout.list_activities, parent, false)); | ||||
} | ||||
| ||||
@Override | ||||
| @ -73,7 +73,7 @@ public class DashboardAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold | |||
loadMoreListener.onLoadMore(); | ||||
} | ||||
| ||||
((DashboardAdapter.DashboardHolder) holder).bindData(activityList.get(position), position); | ||||
((ActivitiesAdapter.DashboardHolder) holder).bindData(activityList.get(position), position); | ||||
} | ||||
| ||||
@Override |
| @ -78,30 +78,30 @@ public class CommitStatusesAdapter | |||
holder.status = currentItem; | ||||
holder.name.setText(currentItem.getContext()); | ||||
holder.description.setText(currentItem.getDescription()); | ||||
switch (currentItem.getStatus().toLowerCase()) { | ||||
case "pending": | ||||
switch (currentItem.getStatus()) { | ||||
case PENDING: | ||||
holder.icon.setImageResource(R.drawable.ic_dot_fill); | ||||
ImageViewCompat.setImageTintList( | ||||
holder.icon, | ||||
ColorStateList.valueOf( | ||||
ctx.getResources().getColor(R.color.lightYellow, null))); | ||||
break; | ||||
case "success": | ||||
case SUCCESS: | ||||
holder.icon.setImageResource(R.drawable.ic_check); | ||||
ImageViewCompat.setImageTintList( | ||||
holder.icon, | ||||
ColorStateList.valueOf( | ||||
ctx.getResources().getColor(R.color.colorLightGreen, null))); | ||||
break; | ||||
case "error": | ||||
case "failure": | ||||
case ERROR: | ||||
case FAILURE: | ||||
holder.icon.setImageResource(R.drawable.ic_close); | ||||
ImageViewCompat.setImageTintList( | ||||
holder.icon, | ||||
ColorStateList.valueOf( | ||||
ctx.getResources().getColor(R.color.iconIssuePrClosedColor, null))); | ||||
break; | ||||
case "warning": | ||||
case WARNING: | ||||
holder.icon.setImageResource(R.drawable.ic_warning); | ||||
ImageViewCompat.setImageTintList( | ||||
holder.icon, | ||||
| |
| @ -0,0 +1,191 @@ | |||
package org.mian.gitnex.adapters; | ||||
| ||||
import android.annotation.SuppressLint; | ||||
import android.content.Context; | ||||
import android.content.Intent; | ||||
import android.view.LayoutInflater; | ||||
import android.view.View; | ||||
import android.view.ViewGroup; | ||||
import android.widget.ImageView; | ||||
import android.widget.LinearLayout; | ||||
import android.widget.TextView; | ||||
import androidx.annotation.NonNull; | ||||
import androidx.navigation.NavController; | ||||
import androidx.navigation.Navigation; | ||||
import androidx.recyclerview.widget.RecyclerView; | ||||
import java.util.ArrayList; | ||||
import java.util.List; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.activities.ProfileActivity; | ||||
import org.mian.gitnex.helpers.TinyDB; | ||||
| ||||
/** | ||||
* @author mmarif | ||||
*/ | ||||
public class HomeDashboardAdapter | ||||
extends RecyclerView.Adapter<HomeDashboardAdapter.CategoryViewHolder> { | ||||
| ||||
private final Context context; | ||||
private final List<CategoryData> categories; | ||||
private final TinyDB tinyDB; | ||||
private String username; | ||||
| ||||
public static class ItemData { | ||||
public int iconResId; | ||||
public String title; | ||||
public String destinationId; | ||||
| ||||
public ItemData(int iconResId, String title, String destinationId) { | ||||
this.iconResId = iconResId; | ||||
this.title = title; | ||||
this.destinationId = destinationId; | ||||
} | ||||
} | ||||
| ||||
public static class CategoryData { | ||||
public String header; | ||||
public List<ItemData> items; | ||||
| ||||
public CategoryData(String header, List<ItemData> items) { | ||||
this.header = header; | ||||
this.items = items; | ||||
} | ||||
} | ||||
| ||||
public HomeDashboardAdapter(Context context) { | ||||
this.context = context; | ||||
this.tinyDB = TinyDB.getInstance(context); | ||||
this.categories = new ArrayList<>(); | ||||
this.username = tinyDB.getString("username"); | ||||
| ||||
// Personal | ||||
List<ItemData> personalItems = new ArrayList<>(); | ||||
personalItems.add( | ||||
new ItemData( | ||||
R.drawable.ic_trending, | ||||
context.getString(R.string.navMostVisited), | ||||
"nav_most_visited")); | ||||
personalItems.add( | ||||
new ItemData( | ||||
R.drawable.ic_person, | ||||
context.getString(R.string.navProfile), | ||||
"profileActivity")); | ||||
personalItems.add( | ||||
new ItemData( | ||||
R.drawable.ic_notes, context.getString(R.string.navNotes), "nav_notes")); | ||||
categories.add(new CategoryData(context.getString(R.string.personal), personalItems)); | ||||
| ||||
// Settings | ||||
List<ItemData> settingsItems = new ArrayList<>(); | ||||
settingsItems.add( | ||||
new ItemData( | ||||
R.drawable.ic_account_settings, | ||||
context.getString(R.string.navAccount), | ||||
"nav_account_settings")); | ||||
settingsItems.add( | ||||
new ItemData( | ||||
R.drawable.ic_tool, | ||||
context.getString(R.string.navAdministration), | ||||
"nav_administration")); | ||||
settingsItems.add( | ||||
new ItemData( | ||||
R.drawable.ic_settings, | ||||
context.getString(R.string.navSettings), | ||||
"nav_settings")); | ||||
categories.add(new CategoryData(context.getString(R.string.navSettings), settingsItems)); | ||||
} | ||||
| ||||
@NonNull @Override | ||||
public CategoryViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { | ||||
View view = | ||||
LayoutInflater.from(context) | ||||
.inflate(R.layout.list_home_dashboard_item, parent, false); | ||||
return new CategoryViewHolder(view); | ||||
} | ||||
| ||||
@Override | ||||
public void onBindViewHolder(@NonNull CategoryViewHolder holder, int position) { | ||||
CategoryData category = categories.get(position); | ||||
holder.header.setVisibility(View.VISIBLE); | ||||
holder.header.setText(category.header); | ||||
holder.categoryCard.setVisibility(View.VISIBLE); | ||||
holder.itemContainer.setOrientation(LinearLayout.VERTICAL); | ||||
holder.itemContainer.removeAllViews(); | ||||
for (ItemData item : category.items) { | ||||
View itemView = | ||||
LayoutInflater.from(context) | ||||
.inflate( | ||||
R.layout.list_home_dashboard_subitem, | ||||
holder.itemContainer, | ||||
false); | ||||
ImageView icon = itemView.findViewById(R.id.itemIcon); | ||||
TextView title = itemView.findViewById(R.id.itemTitle); | ||||
icon.setImageResource(item.iconResId); | ||||
title.setText(item.title); | ||||
| ||||
if (item.destinationId.equals("nav_administration")) { | ||||
itemView.setVisibility(tinyDB.getBoolean("isAdmin") ? View.VISIBLE : View.GONE); | ||||
} else { | ||||
itemView.setVisibility(View.VISIBLE); | ||||
} | ||||
| ||||
itemView.setOnClickListener( | ||||
v -> { | ||||
NavController navController = Navigation.findNavController(v); | ||||
switch (item.destinationId) { | ||||
case "nav_my_issues": | ||||
navController.navigate(R.id.action_to_myIssues); | ||||
break; | ||||
case "nav_most_visited": | ||||
navController.navigate(R.id.action_to_mostVisitedRepos); | ||||
break; | ||||
case "profileActivity": | ||||
Intent intentProfile = new Intent(context, ProfileActivity.class); | ||||
intentProfile.putExtra("username", username); | ||||
context.startActivity(intentProfile); | ||||
break; | ||||
case "nav_notes": | ||||
navController.navigate(R.id.action_to_notes); | ||||
break; | ||||
case "nav_account_settings": | ||||
navController.navigate(R.id.action_to_accountSettings); | ||||
break; | ||||
case "nav_administration": | ||||
navController.navigate(R.id.action_to_administration); | ||||
break; | ||||
case "nav_settings": | ||||
navController.navigate(R.id.action_to_settings); | ||||
break; | ||||
} | ||||
}); | ||||
holder.itemContainer.addView(itemView); | ||||
} | ||||
} | ||||
| ||||
@Override | ||||
public int getItemCount() { | ||||
return categories.size(); | ||||
} | ||||
| ||||
public static class CategoryViewHolder extends RecyclerView.ViewHolder { | ||||
TextView header; | ||||
LinearLayout itemContainer; | ||||
com.google.android.material.card.MaterialCardView categoryCard; | ||||
| ||||
CategoryViewHolder(View itemView) { | ||||
super(itemView); | ||||
header = itemView.findViewById(R.id.sectionHeader); | ||||
itemContainer = itemView.findViewById(R.id.itemContainer); | ||||
categoryCard = itemView.findViewById(R.id.categoryCard); | ||||
} | ||||
} | ||||
| ||||
@SuppressLint("NotifyDataSetChanged") | ||||
public void updateUserInfo(String username, boolean isAdmin, String serverVersion) { | ||||
this.username = username; | ||||
tinyDB.putString("username", username); | ||||
tinyDB.putBoolean("isAdmin", isAdmin); | ||||
tinyDB.putString("serverVersion", serverVersion); | ||||
notifyDataSetChanged(); | ||||
} | ||||
} |
| @ -175,13 +175,14 @@ public class NotificationsAdapter extends RecyclerView.Adapter<RecyclerView.View | |||
type, | ||||
ColorStateList.valueOf( | ||||
context.getResources() | ||||
.getColor(R.color.iconIssuePrClosedColor))); | ||||
.getColor(R.color.iconIssuePrClosedColor, null))); | ||||
break; | ||||
case "merged": | ||||
ImageViewCompat.setImageTintList( | ||||
type, | ||||
ColorStateList.valueOf( | ||||
context.getResources().getColor(R.color.iconPrMergedColor))); | ||||
context.getResources() | ||||
.getColor(R.color.iconPrMergedColor, null))); | ||||
break; | ||||
| ||||
default: | ||||
| |
| @ -12,7 +12,6 @@ import android.view.ViewGroup; | |||
import android.widget.Button; | ||||
import android.widget.ImageView; | ||||
import androidx.annotation.NonNull; | ||||
import androidx.drawerlayout.widget.DrawerLayout; | ||||
import androidx.recyclerview.widget.LinearLayoutManager; | ||||
import androidx.recyclerview.widget.RecyclerView; | ||||
import com.bumptech.glide.Glide; | ||||
| @ -29,16 +28,13 @@ import org.mian.gitnex.helpers.UrlHelper; | |||
public class UserAccountsNavAdapter | ||||
extends RecyclerView.Adapter<UserAccountsNavAdapter.UserAccountsViewHolder> { | ||||
| ||||
private final DrawerLayout drawer; | ||||
private final List<UserAccount> userAccountsList; | ||||
private final Context context; | ||||
| ||||
public UserAccountsNavAdapter( | ||||
Context ctx, List<UserAccount> userAccountsListMain, DrawerLayout drawerLayout) { | ||||
public UserAccountsNavAdapter(Context ctx, List<UserAccount> userAccountsListMain) { | ||||
| ||||
this.context = ctx; | ||||
this.userAccountsList = userAccountsListMain; | ||||
this.drawer = drawerLayout; | ||||
} | ||||
| ||||
@NonNull @Override | ||||
| @ -109,7 +105,6 @@ public class UserAccountsNavAdapter | |||
itemView.setOnClickListener( | ||||
item -> { | ||||
customDialogUserAccountsList(); | ||||
drawer.closeDrawers(); | ||||
}); | ||||
} | ||||
} | ||||
| |
| @ -15,15 +15,12 @@ import androidx.viewpager2.widget.ViewPager2; | |||
import com.google.android.material.tabs.TabLayout; | ||||
import com.google.android.material.tabs.TabLayoutMediator; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.activities.BaseActivity; | ||||
import org.mian.gitnex.activities.MainActivity; | ||||
import org.mian.gitnex.helpers.AppDatabaseSettings; | ||||
import org.mian.gitnex.helpers.AppUtil; | ||||
import org.mian.gitnex.helpers.ViewPager2Transformers; | ||||
import org.mian.gitnex.helpers.contexts.AccountContext; | ||||
| ||||
/** | ||||
* @author M M Arif | ||||
* @author mmarif | ||||
*/ | ||||
public class AccountSettingsFragment extends Fragment { | ||||
| ||||
| @ -41,23 +38,9 @@ public class AccountSettingsFragment extends Fragment { | |||
ctx = getContext(); | ||||
| ||||
view = inflater.inflate(R.layout.fragment_account_settings, container, false); | ||||
setHasOptionsMenu(false); | ||||
| ||||
((MainActivity) requireActivity()) | ||||
.setActionBarTitle(getResources().getString(R.string.navAccount)); | ||||
| ||||
myTypeface = AppUtil.getTypeface(ctx); | ||||
| ||||
AccountContext account = ((BaseActivity) requireActivity()).getAccount(); | ||||
if (account.getUserInfo() != null) { | ||||
viewData(); | ||||
} else { | ||||
((MainActivity) requireActivity()) | ||||
.setProfileInitListener( | ||||
(text) -> { | ||||
viewData(); | ||||
}); | ||||
} | ||||
viewData(); | ||||
| ||||
return view; | ||||
} | ||||
| |
| @ -14,23 +14,21 @@ import androidx.recyclerview.widget.LinearLayoutManager; | |||
import java.util.ArrayList; | ||||
import java.util.List; | ||||
import org.gitnex.tea4j.v2.models.Activity; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.activities.BaseActivity; | ||||
import org.mian.gitnex.activities.MainActivity; | ||||
import org.mian.gitnex.adapters.DashboardAdapter; | ||||
import org.mian.gitnex.databinding.FragmentDashboardBinding; | ||||
import org.mian.gitnex.adapters.ActivitiesAdapter; | ||||
import org.mian.gitnex.databinding.FragmentActivitiesBinding; | ||||
import org.mian.gitnex.helpers.TinyDB; | ||||
import org.mian.gitnex.viewmodels.DashboardViewModel; | ||||
import org.mian.gitnex.viewmodels.ActivitiesViewModel; | ||||
| ||||
/** | ||||
* @author M M Arif | ||||
*/ | ||||
public class DashboardFragment extends Fragment { | ||||
public class ActivitiesFragment extends Fragment { | ||||
| ||||
protected TinyDB tinyDB; | ||||
private DashboardViewModel dashboardViewModel; | ||||
private FragmentDashboardBinding binding; | ||||
private DashboardAdapter adapter; | ||||
private ActivitiesViewModel viewModel; | ||||
private FragmentActivitiesBinding binding; | ||||
private ActivitiesAdapter adapter; | ||||
private List<Activity> activityList; | ||||
private int page = 1; | ||||
private String username; | ||||
| @ -39,23 +37,20 @@ public class DashboardFragment extends Fragment { | |||
public View onCreateView( | ||||
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { | ||||
| ||||
binding = FragmentDashboardBinding.inflate(inflater, container, false); | ||||
binding = FragmentActivitiesBinding.inflate(inflater, container, false); | ||||
| ||||
Context ctx = getContext(); | ||||
| ||||
((MainActivity) requireActivity()) | ||||
.setActionBarTitle(getResources().getString(R.string.dashboard)); | ||||
| ||||
activityList = new ArrayList<>(); | ||||
| ||||
dashboardViewModel = new ViewModelProvider(this).get(DashboardViewModel.class); | ||||
viewModel = new ViewModelProvider(this).get(ActivitiesViewModel.class); | ||||
| ||||
username = ((BaseActivity) requireActivity()).getAccount().getAccount().getUserName(); | ||||
| ||||
binding.recyclerView.setHasFixedSize(true); | ||||
binding.recyclerView.setLayoutManager(new LinearLayoutManager(ctx)); | ||||
| ||||
adapter = new DashboardAdapter(activityList, ctx); | ||||
adapter = new ActivitiesAdapter(activityList, ctx); | ||||
| ||||
binding.pullToRefresh.setOnRefreshListener( | ||||
() -> | ||||
| @ -82,20 +77,20 @@ public class DashboardFragment extends Fragment { | |||
| ||||
private void fetchDataAsync(String username) { | ||||
| ||||
dashboardViewModel | ||||
viewModel | ||||
.getActivitiesList(username, getContext(), binding) | ||||
.observe( | ||||
getViewLifecycleOwner(), | ||||
activityListMain -> { | ||||
adapter = new DashboardAdapter(activityListMain, getContext()); | ||||
adapter = new ActivitiesAdapter(activityListMain, getContext()); | ||||
adapter.setLoadMoreListener( | ||||
new DashboardAdapter.OnLoadMoreListener() { | ||||
new ActivitiesAdapter.OnLoadMoreListener() { | ||||
| ||||
@Override | ||||
public void onLoadMore() { | ||||
| ||||
page += 1; | ||||
dashboardViewModel.loadMoreActivities( | ||||
viewModel.loadMoreActivities( | ||||
username, page, getContext(), adapter, binding); | ||||
binding.progressBar.setVisibility(View.VISIBLE); | ||||
} |
| @ -159,7 +159,7 @@ public class BottomSheetIssueDependenciesFragment extends BottomSheetDialogFragm | |||
| ||||
Call<Void> call = | ||||
RetrofitClient.getApiInterface(requireContext()) | ||||
.customIssueRemoveIssueDependencies( | ||||
.issueRemoveIssueDependencies2( | ||||
issue.getRepository().getOwner(), | ||||
issue.getRepository().getName(), | ||||
String.valueOf(issue.getIssue().getId()), | ||||
| |
| @ -2,7 +2,6 @@ package org.mian.gitnex.fragments; | |||
| ||||
import android.content.Context; | ||||
import android.os.Bundle; | ||||
import android.util.Log; | ||||
import android.view.LayoutInflater; | ||||
import android.view.View; | ||||
import android.view.ViewGroup; | ||||
| @ -10,6 +9,7 @@ import androidx.annotation.NonNull; | |||
import androidx.annotation.Nullable; | ||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment; | ||||
import org.mian.gitnex.databinding.BottomSheetMilestonesFilterBinding; | ||||
import org.mian.gitnex.helpers.contexts.RepositoryContext; | ||||
import org.mian.gitnex.structs.BottomSheetListener; | ||||
| ||||
/** | ||||
| @ -18,6 +18,16 @@ import org.mian.gitnex.structs.BottomSheetListener; | |||
public class BottomSheetMilestonesFilterFragment extends BottomSheetDialogFragment { | ||||
| ||||
private BottomSheetListener bmListener; | ||||
private BottomSheetMilestonesFilterBinding binding; | ||||
private RepositoryContext repository; | ||||
| ||||
public static BottomSheetMilestonesFilterFragment newInstance(RepositoryContext repository) { | ||||
BottomSheetMilestonesFilterFragment fragment = new BottomSheetMilestonesFilterFragment(); | ||||
Bundle args = new Bundle(); | ||||
args.putSerializable(RepositoryContext.INTENT_EXTRA, repository); | ||||
fragment.setArguments(args); | ||||
return fragment; | ||||
} | ||||
| ||||
@Nullable @Override | ||||
public View onCreateView( | ||||
| @ -25,33 +35,61 @@ public class BottomSheetMilestonesFilterFragment extends BottomSheetDialogFragme | |||
@Nullable ViewGroup container, | ||||
@Nullable Bundle savedInstanceState) { | ||||
| ||||
BottomSheetMilestonesFilterBinding bottomSheetMilestonesFilterBinding = | ||||
BottomSheetMilestonesFilterBinding.inflate(inflater, container, false); | ||||
binding = BottomSheetMilestonesFilterBinding.inflate(inflater, container, false); | ||||
| ||||
bottomSheetMilestonesFilterBinding.openMilestone.setOnClickListener( | ||||
v1 -> { | ||||
bmListener.onButtonClicked("openMilestone"); | ||||
dismiss(); | ||||
if (getArguments() != null) { | ||||
repository = | ||||
(RepositoryContext) | ||||
getArguments().getSerializable(RepositoryContext.INTENT_EXTRA); | ||||
} | ||||
if (repository == null) { | ||||
throw new IllegalStateException("RepositoryContext is required"); | ||||
} | ||||
| ||||
binding.openChip.setChecked(repository.getMilestoneState() == RepositoryContext.State.OPEN); | ||||
binding.closedChip.setChecked( | ||||
repository.getMilestoneState() == RepositoryContext.State.CLOSED); | ||||
| ||||
binding.openChip.setOnCheckedChangeListener( | ||||
(buttonView, isChecked) -> { | ||||
if (isChecked | ||||
&& repository.getMilestoneState() != RepositoryContext.State.OPEN) { | ||||
repository.setMilestoneState(RepositoryContext.State.OPEN); | ||||
bmListener.onButtonClicked("openMilestone"); | ||||
dismiss(); | ||||
} else if (!isChecked) { | ||||
buttonView.setChecked(true); | ||||
} | ||||
}); | ||||
| ||||
bottomSheetMilestonesFilterBinding.closedMilestone.setOnClickListener( | ||||
v12 -> { | ||||
bmListener.onButtonClicked("closedMilestone"); | ||||
dismiss(); | ||||
binding.closedChip.setOnCheckedChangeListener( | ||||
(buttonView, isChecked) -> { | ||||
if (isChecked | ||||
&& repository.getMilestoneState() != RepositoryContext.State.CLOSED) { | ||||
repository.setMilestoneState(RepositoryContext.State.CLOSED); | ||||
bmListener.onButtonClicked("closedMilestone"); | ||||
dismiss(); | ||||
} else if (!isChecked) { | ||||
buttonView.setChecked(true); | ||||
} | ||||
}); | ||||
| ||||
return bottomSheetMilestonesFilterBinding.getRoot(); | ||||
return binding.getRoot(); | ||||
} | ||||
| ||||
@Override | ||||
public void onAttach(@NonNull Context context) { | ||||
| ||||
super.onAttach(context); | ||||
| ||||
try { | ||||
bmListener = (BottomSheetListener) context; | ||||
} catch (ClassCastException e) { | ||||
Log.e("MilestonesFilterBs", e.toString()); | ||||
throw new ClassCastException(context + " must implement BottomSheetListener"); | ||||
} | ||||
} | ||||
| ||||
@Override | ||||
public void onDestroyView() { | ||||
super.onDestroyView(); | ||||
binding = null; | ||||
} | ||||
} | ||||
| |
| @ -1,116 +0,0 @@ | |||
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 androidx.annotation.NonNull; | ||||
import androidx.annotation.Nullable; | ||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment; | ||||
import org.mian.gitnex.databinding.BottomSheetMyIssuesFilterBinding; | ||||
| ||||
/** | ||||
* @author M M Arif | ||||
*/ | ||||
public class BottomSheetMyIssuesFilterFragment extends BottomSheetDialogFragment { | ||||
| ||||
private BottomSheetListener bmListener; | ||||
private String selectedState = "open"; | ||||
private String selectedFilter = "created_by_me"; | ||||
private static final String ARG_FILTER = "currentFilter"; | ||||
| ||||
public static BottomSheetMyIssuesFilterFragment newInstance(String currentFilter) { | ||||
| ||||
BottomSheetMyIssuesFilterFragment fragment = new BottomSheetMyIssuesFilterFragment(); | ||||
Bundle args = new Bundle(); | ||||
args.putString(ARG_FILTER, currentFilter); | ||||
fragment.setArguments(args); | ||||
return fragment; | ||||
} | ||||
| ||||
@Override | ||||
public void onCreate(@Nullable Bundle savedInstanceState) { | ||||
| ||||
super.onCreate(savedInstanceState); | ||||
Bundle args = getArguments(); | ||||
| ||||
if (args != null && args.containsKey(ARG_FILTER)) { | ||||
String currentFilter = args.getString(ARG_FILTER, "open_created_by_me"); | ||||
String[] parts = currentFilter.split("_"); | ||||
selectedState = parts[0]; | ||||
selectedFilter = parts.length > 1 ? parts[1] : "created_by_me"; | ||||
} | ||||
} | ||||
| ||||
@Nullable @Override | ||||
public View onCreateView( | ||||
@NonNull LayoutInflater inflater, | ||||
@Nullable ViewGroup container, | ||||
@Nullable Bundle savedInstanceState) { | ||||
| ||||
BottomSheetMyIssuesFilterBinding binding = | ||||
BottomSheetMyIssuesFilterBinding.inflate(inflater, container, false); | ||||
| ||||
binding.chipOpen.setChecked(false); | ||||
binding.chipClosed.setChecked(false); | ||||
binding.chipCreatedByMe.setChecked(false); | ||||
binding.chipAssignedToMe.setChecked(false); | ||||
| ||||
binding.chipOpen.setChecked(selectedState.equals("open")); | ||||
binding.chipClosed.setChecked(selectedState.equals("closed")); | ||||
binding.chipCreatedByMe.setChecked(selectedFilter.equals("created_by_me")); | ||||
binding.chipAssignedToMe.setChecked(selectedFilter.equals("assignedToMe")); | ||||
| ||||
binding.stateChipGroup.setOnCheckedStateChangeListener( | ||||
(group, checkedIds) -> { | ||||
if (!checkedIds.isEmpty()) { | ||||
int checkedId = checkedIds.get(0); | ||||
if (checkedId == binding.chipOpen.getId()) { | ||||
selectedState = "open"; | ||||
} else if (checkedId == binding.chipClosed.getId()) { | ||||
selectedState = "closed"; | ||||
} | ||||
applyFilter(); | ||||
} | ||||
}); | ||||
| ||||
binding.filterChipGroup.setOnCheckedStateChangeListener( | ||||
(group, checkedIds) -> { | ||||
if (!checkedIds.isEmpty()) { | ||||
| ||||
int checkedId = checkedIds.get(0); | ||||
if (checkedId == binding.chipCreatedByMe.getId()) { | ||||
selectedFilter = "created_by_me"; | ||||
binding.chipAssignedToMe.setChecked(false); | ||||
} else if (checkedId == binding.chipAssignedToMe.getId()) { | ||||
selectedFilter = "assignedToMe"; | ||||
binding.chipCreatedByMe.setChecked(false); | ||||
} | ||||
applyFilter(); | ||||
} | ||||
}); | ||||
| ||||
return binding.getRoot(); | ||||
} | ||||
| ||||
private void applyFilter() { | ||||
String result = selectedState + "_" + selectedFilter; | ||||
bmListener.onButtonClicked(result); | ||||
dismiss(); | ||||
} | ||||
| ||||
@Override | ||||
public void onAttach(@NonNull Context context) { | ||||
super.onAttach(context); | ||||
try { | ||||
bmListener = (BottomSheetListener) context; | ||||
} catch (ClassCastException e) { | ||||
throw new ClassCastException(context + " must implement BottomSheetListener"); | ||||
} | ||||
} | ||||
| ||||
public interface BottomSheetListener { | ||||
void onButtonClicked(String text); | ||||
} | ||||
} |
| @ -1,46 +0,0 @@ | |||
package org.mian.gitnex.fragments; | ||||
| ||||
import android.os.Bundle; | ||||
import android.view.LayoutInflater; | ||||
import android.view.View; | ||||
import android.view.ViewGroup; | ||||
import androidx.annotation.NonNull; | ||||
import androidx.annotation.Nullable; | ||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment; | ||||
import org.mian.gitnex.databinding.BottomSheetNotificationsFilterBinding; | ||||
import org.mian.gitnex.structs.BottomSheetListener; | ||||
| ||||
/** | ||||
* @author opyale | ||||
*/ | ||||
public class BottomSheetNotificationsFilterFragment extends BottomSheetDialogFragment { | ||||
| ||||
private BottomSheetListener listener; | ||||
| ||||
@Nullable @Override | ||||
public View onCreateView( | ||||
@NonNull LayoutInflater inflater, | ||||
@Nullable ViewGroup container, | ||||
@Nullable Bundle savedInstanceState) { | ||||
BottomSheetNotificationsFilterBinding binding = | ||||
BottomSheetNotificationsFilterBinding.inflate(inflater, container, false); | ||||
| ||||
binding.readNotifications.setOnClickListener( | ||||
v1 -> { | ||||
listener.onButtonClicked("read"); | ||||
dismiss(); | ||||
}); | ||||
| ||||
binding.unreadNotifications.setOnClickListener( | ||||
v12 -> { | ||||
listener.onButtonClicked("unread"); | ||||
dismiss(); | ||||
}); | ||||
| ||||
return binding.getRoot(); | ||||
} | ||||
| ||||
public void setOnClickListener(BottomSheetListener listener) { | ||||
this.listener = listener; | ||||
} | ||||
} |
| @ -9,14 +9,25 @@ import androidx.annotation.NonNull; | |||
import androidx.annotation.Nullable; | ||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment; | ||||
import org.mian.gitnex.databinding.BottomSheetPullRequestFilterBinding; | ||||
import org.mian.gitnex.helpers.contexts.RepositoryContext; | ||||
import org.mian.gitnex.structs.BottomSheetListener; | ||||
| ||||
/** | ||||
* @author M M Arif | ||||
* @author mmarif | ||||
*/ | ||||
public class BottomSheetPullRequestFilterFragment extends BottomSheetDialogFragment { | ||||
| ||||
private BottomSheetListener bmListener; | ||||
private BottomSheetPullRequestFilterBinding binding; | ||||
private RepositoryContext repository; | ||||
| ||||
public static BottomSheetPullRequestFilterFragment newInstance(RepositoryContext repository) { | ||||
BottomSheetPullRequestFilterFragment fragment = new BottomSheetPullRequestFilterFragment(); | ||||
Bundle args = new Bundle(); | ||||
args.putSerializable(RepositoryContext.INTENT_EXTRA, repository); | ||||
fragment.setArguments(args); | ||||
return fragment; | ||||
} | ||||
| ||||
@Nullable @Override | ||||
public View onCreateView( | ||||
| @ -24,33 +35,58 @@ public class BottomSheetPullRequestFilterFragment extends BottomSheetDialogFragm | |||
@Nullable ViewGroup container, | ||||
@Nullable Bundle savedInstanceState) { | ||||
| ||||
BottomSheetPullRequestFilterBinding bottomSheetPullRequestFilterBinding = | ||||
BottomSheetPullRequestFilterBinding.inflate(inflater, container, false); | ||||
binding = BottomSheetPullRequestFilterBinding.inflate(inflater, container, false); | ||||
| ||||
bottomSheetPullRequestFilterBinding.openPr.setOnClickListener( | ||||
v1 -> { | ||||
bmListener.onButtonClicked("openPr"); | ||||
dismiss(); | ||||
if (getArguments() != null) { | ||||
repository = | ||||
(RepositoryContext) | ||||
getArguments().getSerializable(RepositoryContext.INTENT_EXTRA); | ||||
} | ||||
if (repository == null) { | ||||
throw new IllegalStateException("RepositoryContext is required"); | ||||
} | ||||
| ||||
binding.openChip.setChecked(repository.getPrState() == RepositoryContext.State.OPEN); | ||||
binding.closedChip.setChecked(repository.getPrState() == RepositoryContext.State.CLOSED); | ||||
| ||||
binding.openChip.setOnCheckedChangeListener( | ||||
(buttonView, isChecked) -> { | ||||
if (isChecked && repository.getPrState() != RepositoryContext.State.OPEN) { | ||||
repository.setPrState(RepositoryContext.State.OPEN); | ||||
bmListener.onButtonClicked("openPr"); | ||||
dismiss(); | ||||
} else if (!isChecked) { | ||||
buttonView.setChecked(true); | ||||
} | ||||
}); | ||||
| ||||
bottomSheetPullRequestFilterBinding.closedPr.setOnClickListener( | ||||
v12 -> { | ||||
bmListener.onButtonClicked("closedPr"); | ||||
dismiss(); | ||||
binding.closedChip.setOnCheckedChangeListener( | ||||
(buttonView, isChecked) -> { | ||||
if (isChecked && repository.getPrState() != RepositoryContext.State.CLOSED) { | ||||
repository.setPrState(RepositoryContext.State.CLOSED); | ||||
bmListener.onButtonClicked("closedPr"); | ||||
dismiss(); | ||||
} else if (!isChecked) { | ||||
buttonView.setChecked(true); | ||||
} | ||||
}); | ||||
| ||||
return bottomSheetPullRequestFilterBinding.getRoot(); | ||||
return binding.getRoot(); | ||||
} | ||||
| ||||
@Override | ||||
public void onAttach(@NonNull Context context) { | ||||
| ||||
super.onAttach(context); | ||||
| ||||
try { | ||||
bmListener = (BottomSheetListener) context; | ||||
} catch (ClassCastException e) { | ||||
throw new ClassCastException(context + " must implement BottomSheetListener"); | ||||
} | ||||
} | ||||
| ||||
@Override | ||||
public void onDestroyView() { | ||||
super.onDestroyView(); | ||||
binding = null; | ||||
} | ||||
} | ||||
| |
| @ -9,6 +9,7 @@ import androidx.annotation.NonNull; | |||
import androidx.annotation.Nullable; | ||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment; | ||||
import org.mian.gitnex.databinding.BottomSheetReleasesTagsBinding; | ||||
import org.mian.gitnex.helpers.contexts.RepositoryContext; | ||||
import org.mian.gitnex.structs.BottomSheetListener; | ||||
| ||||
/** | ||||
| @ -17,17 +18,15 @@ import org.mian.gitnex.structs.BottomSheetListener; | |||
public class BottomSheetReleasesTagsFragment extends BottomSheetDialogFragment { | ||||
| ||||
private BottomSheetListener bmListener; | ||||
private BottomSheetReleasesTagsBinding binding; | ||||
private RepositoryContext repository; | ||||
| ||||
@Override | ||||
public void onAttach(@NonNull Context context) { | ||||
| ||||
super.onAttach(context); | ||||
| ||||
try { | ||||
bmListener = (BottomSheetListener) context; | ||||
} catch (ClassCastException e) { | ||||
throw new ClassCastException(context + " must implement BottomSheetListener"); | ||||
} | ||||
public static BottomSheetReleasesTagsFragment newInstance(RepositoryContext repository) { | ||||
BottomSheetReleasesTagsFragment fragment = new BottomSheetReleasesTagsFragment(); | ||||
Bundle args = new Bundle(); | ||||
args.putSerializable(RepositoryContext.INTENT_EXTRA, repository); | ||||
fragment.setArguments(args); | ||||
return fragment; | ||||
} | ||||
| ||||
@Nullable @Override | ||||
| @ -36,21 +35,58 @@ public class BottomSheetReleasesTagsFragment extends BottomSheetDialogFragment { | |||
@Nullable ViewGroup container, | ||||
@Nullable Bundle savedInstanceState) { | ||||
| ||||
BottomSheetReleasesTagsBinding binding = | ||||
BottomSheetReleasesTagsBinding.inflate(inflater, container, false); | ||||
binding = BottomSheetReleasesTagsBinding.inflate(inflater, container, false); | ||||
| ||||
binding.tags.setOnClickListener( | ||||
v1 -> { | ||||
bmListener.onButtonClicked("tags"); | ||||
dismiss(); | ||||
if (getArguments() != null) { | ||||
repository = | ||||
(RepositoryContext) | ||||
getArguments().getSerializable(RepositoryContext.INTENT_EXTRA); | ||||
} | ||||
if (repository == null) { | ||||
throw new IllegalStateException("RepositoryContext is required"); | ||||
} | ||||
| ||||
binding.releasesChip.setChecked(!repository.isReleasesViewTypeIsTag()); | ||||
binding.tagsChip.setChecked(repository.isReleasesViewTypeIsTag()); | ||||
| ||||
binding.releasesChip.setOnCheckedChangeListener( | ||||
(buttonView, isChecked) -> { | ||||
if (isChecked && repository.isReleasesViewTypeIsTag()) { | ||||
repository.setReleasesViewTypeIsTag(false); | ||||
bmListener.onButtonClicked("releases"); | ||||
dismiss(); | ||||
} else if (!isChecked) { | ||||
buttonView.setChecked(true); | ||||
} | ||||
}); | ||||
| ||||
binding.releases.setOnClickListener( | ||||
v12 -> { | ||||
bmListener.onButtonClicked("releases"); | ||||
dismiss(); | ||||
binding.tagsChip.setOnCheckedChangeListener( | ||||
(buttonView, isChecked) -> { | ||||
if (isChecked && !repository.isReleasesViewTypeIsTag()) { | ||||
repository.setReleasesViewTypeIsTag(true); | ||||
bmListener.onButtonClicked("tags"); | ||||
dismiss(); | ||||
} else if (!isChecked) { | ||||
buttonView.setChecked(true); | ||||
} | ||||
}); | ||||
| ||||
return binding.getRoot(); | ||||
} | ||||
| ||||
@Override | ||||
public void onAttach(@NonNull Context context) { | ||||
super.onAttach(context); | ||||
try { | ||||
bmListener = (BottomSheetListener) context; | ||||
} catch (ClassCastException e) { | ||||
throw new ClassCastException(context + " must implement BottomSheetListener"); | ||||
} | ||||
} | ||||
| ||||
@Override | ||||
public void onDestroyView() { | ||||
super.onDestroyView(); | ||||
binding = null; | ||||
} | ||||
} | ||||
| |
| @ -0,0 +1,79 @@ | |||
package org.mian.gitnex.fragments; | ||||
| ||||
import android.os.Bundle; | ||||
import android.view.LayoutInflater; | ||||
import android.view.View; | ||||
import android.view.ViewGroup; | ||||
import androidx.annotation.NonNull; | ||||
import androidx.annotation.Nullable; | ||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.activities.BaseActivity; | ||||
import org.mian.gitnex.databinding.BottomSheetSettingsAboutBinding; | ||||
import org.mian.gitnex.helpers.AppUtil; | ||||
| ||||
/** | ||||
* @author mmarif | ||||
*/ | ||||
public class BottomSheetSettingsAboutFragment extends BottomSheetDialogFragment { | ||||
| ||||
private BottomSheetSettingsAboutBinding binding; | ||||
| ||||
@Nullable @Override | ||||
public View onCreateView( | ||||
@NonNull LayoutInflater inflater, | ||||
@Nullable ViewGroup container, | ||||
@Nullable Bundle savedInstanceState) { | ||||
binding = BottomSheetSettingsAboutBinding.inflate(inflater, container, false); | ||||
| ||||
// Set app version and build | ||||
binding.appVersionBuild.setText( | ||||
getString( | ||||
R.string.appVersionBuild, | ||||
AppUtil.getAppVersion(requireContext()), | ||||
AppUtil.getAppBuildNo(requireContext()))); | ||||
| ||||
// Set server version | ||||
binding.userServerVersion.setText( | ||||
((BaseActivity) requireActivity()).getAccount().getServerVersion().toString()); | ||||
| ||||
// Set up link click listeners | ||||
binding.donationLinkPatreon.setOnClickListener( | ||||
v -> { | ||||
AppUtil.openUrlInBrowser( | ||||
requireContext(), getString(R.string.supportLinkPatreon)); | ||||
dismiss(); | ||||
}); | ||||
| ||||
binding.translateLink.setOnClickListener( | ||||
v -> { | ||||
AppUtil.openUrlInBrowser(requireContext(), getString(R.string.crowdInLink)); | ||||
dismiss(); | ||||
}); | ||||
| ||||
binding.appWebsite.setOnClickListener( | ||||
v -> { | ||||
AppUtil.openUrlInBrowser(requireContext(), getString(R.string.appWebsiteLink)); | ||||
dismiss(); | ||||
}); | ||||
| ||||
binding.feedback.setOnClickListener( | ||||
v -> { | ||||
AppUtil.openUrlInBrowser(requireContext(), getString(R.string.feedbackLink)); | ||||
dismiss(); | ||||
}); | ||||
| ||||
// Hide donation link for pro users | ||||
if (AppUtil.isPro(requireContext())) { | ||||
binding.layoutFrame1.setVisibility(View.GONE); | ||||
} | ||||
| ||||
return binding.getRoot(); | ||||
} | ||||
| ||||
@Override | ||||
public void onDestroyView() { | ||||
super.onDestroyView(); | ||||
binding = null; // Prevent memory leaks | ||||
} | ||||
} |
| @ -0,0 +1,492 @@ | |||
package org.mian.gitnex.fragments; | ||||
| ||||
import android.os.Build; | ||||
import android.os.Bundle; | ||||
import android.os.Handler; | ||||
import android.util.Log; | ||||
import android.view.LayoutInflater; | ||||
import android.view.View; | ||||
import android.view.ViewGroup; | ||||
import androidx.annotation.NonNull; | ||||
import androidx.annotation.Nullable; | ||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment; | ||||
import com.google.android.material.chip.Chip; | ||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder; | ||||
import com.google.android.material.timepicker.MaterialTimePicker; | ||||
import java.util.LinkedHashMap; | ||||
import java.util.Locale; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.databinding.BottomSheetSettingsAppearanceBinding; | ||||
import org.mian.gitnex.helpers.AppDatabaseSettings; | ||||
import org.mian.gitnex.helpers.AppUtil; | ||||
import org.mian.gitnex.helpers.FontsOverride; | ||||
import org.mian.gitnex.helpers.SnackBar; | ||||
| ||||
/** | ||||
* @author mmarif | ||||
*/ | ||||
public class BottomSheetSettingsAppearanceFragment extends BottomSheetDialogFragment { | ||||
| ||||
private static final String TAG = "BottomSheetSettingsAppearance"; | ||||
private BottomSheetSettingsAppearanceBinding binding; | ||||
private static int customFontSelectedChoice; | ||||
private static String[] themeList; | ||||
private static int themeSelectedChoice; | ||||
private static int langSelectedChoice; | ||||
private static int fragmentTabsAnimationSelectedChoice; | ||||
private LinkedHashMap<String, String> lang; | ||||
| ||||
@Nullable @Override | ||||
public View onCreateView( | ||||
@NonNull LayoutInflater inflater, | ||||
@Nullable ViewGroup container, | ||||
@Nullable Bundle savedInstanceState) { | ||||
binding = BottomSheetSettingsAppearanceBinding.inflate(inflater, container, false); | ||||
| ||||
lang = new LinkedHashMap<>(); | ||||
lang.put("sys", getString(R.string.settingsLanguageSystem)); | ||||
for (String langCode : getResources().getStringArray(R.array.languages)) { | ||||
lang.put(langCode, getLanguageDisplayName(langCode)); | ||||
} | ||||
| ||||
String[] customFontList = getResources().getStringArray(R.array.fonts); | ||||
String[] fragmentTabsAnimationList = | ||||
getResources().getStringArray(R.array.fragmentTabsAnimation); | ||||
themeList = | ||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S || "S".equals(Build.VERSION.CODENAME) | ||||
? getResources().getStringArray(R.array.themesAndroid12) | ||||
: getResources().getStringArray(R.array.themes); | ||||
| ||||
customFontSelectedChoice = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), AppDatabaseSettings.APP_FONT_KEY)); | ||||
themeSelectedChoice = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), AppDatabaseSettings.APP_THEME_KEY)); | ||||
fragmentTabsAnimationSelectedChoice = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), AppDatabaseSettings.APP_TABS_ANIMATION_KEY)); | ||||
String[] locale = | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), AppDatabaseSettings.APP_LOCALE_KEY) | ||||
.split("\\|"); | ||||
langSelectedChoice = Integer.parseInt(locale[0]); | ||||
| ||||
String lightMinute = | ||||
String.valueOf( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), | ||||
AppDatabaseSettings.APP_THEME_AUTO_LIGHT_MIN_KEY)); | ||||
String lightHour = | ||||
String.valueOf( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), | ||||
AppDatabaseSettings.APP_THEME_AUTO_LIGHT_HOUR_KEY)); | ||||
lightMinute = lightMinute.length() == 1 ? "0" + lightMinute : lightMinute; | ||||
lightHour = lightHour.length() == 1 ? "0" + lightHour : lightHour; | ||||
| ||||
String darkMinute = | ||||
String.valueOf( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), AppDatabaseSettings.APP_THEME_AUTO_DARK_MIN_KEY)); | ||||
String darkHour = | ||||
String.valueOf( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), | ||||
AppDatabaseSettings.APP_THEME_AUTO_DARK_HOUR_KEY)); | ||||
darkMinute = darkMinute.length() == 1 ? "0" + darkMinute : darkMinute; | ||||
darkHour = darkHour.length() == 1 ? "0" + darkHour : darkHour; | ||||
| ||||
for (int i = 0; i < themeList.length; i++) { | ||||
Chip chip = (Chip) inflater.inflate(R.layout.chip_item, binding.themeChipGroup, false); | ||||
chip.setId(View.generateViewId()); | ||||
chip.setText(themeList[i]); | ||||
chip.setCheckable(true); | ||||
chip.setClickable(true); | ||||
chip.setFocusable(true); | ||||
if (i == themeSelectedChoice) chip.setChecked(true); | ||||
binding.themeChipGroup.addView(chip); | ||||
} | ||||
| ||||
for (int i = 0; i < customFontList.length; i++) { | ||||
Chip chip = | ||||
(Chip) inflater.inflate(R.layout.chip_item, binding.customFontChipGroup, false); | ||||
chip.setId(View.generateViewId()); | ||||
chip.setText(customFontList[i]); | ||||
chip.setCheckable(true); | ||||
chip.setClickable(true); | ||||
chip.setFocusable(true); | ||||
if (i == customFontSelectedChoice) chip.setChecked(true); | ||||
binding.customFontChipGroup.addView(chip); | ||||
} | ||||
| ||||
for (int i = 0; i < fragmentTabsAnimationList.length; i++) { | ||||
Chip chip = | ||||
(Chip) | ||||
inflater.inflate( | ||||
R.layout.chip_item, | ||||
binding.fragmentTabsAnimationChipGroup, | ||||
false); | ||||
chip.setId(View.generateViewId()); | ||||
chip.setText(fragmentTabsAnimationList[i]); | ||||
chip.setCheckable(true); | ||||
chip.setClickable(true); | ||||
chip.setFocusable(true); | ||||
if (i == fragmentTabsAnimationSelectedChoice) chip.setChecked(true); | ||||
binding.fragmentTabsAnimationChipGroup.addView(chip); | ||||
} | ||||
| ||||
binding.lightThemeSelectedTime.setText( | ||||
getResources() | ||||
.getString(R.string.settingsThemeTimeSelectedHint, lightHour, lightMinute)); | ||||
binding.darkThemeSelectedTime.setText( | ||||
getResources() | ||||
.getString(R.string.settingsThemeTimeSelectedHint, darkHour, darkMinute)); | ||||
binding.tvLanguageSelected.setText( | ||||
lang.get(lang.keySet().toArray(new String[0])[langSelectedChoice])); | ||||
binding.switchCounterBadge.setChecked( | ||||
Boolean.parseBoolean( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), AppDatabaseSettings.APP_COUNTER_KEY))); | ||||
binding.switchHideEmailLangInProfile.setChecked( | ||||
Boolean.parseBoolean( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), | ||||
AppDatabaseSettings.APP_USER_PROFILE_HIDE_EMAIL_LANGUAGE_KEY))); | ||||
binding.switchHideEmailNavDrawer.setChecked( | ||||
Boolean.parseBoolean( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), | ||||
AppDatabaseSettings.APP_USER_HIDE_EMAIL_IN_NAV_KEY))); | ||||
binding.switchLabelsInListBadge.setChecked( | ||||
Boolean.parseBoolean( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), AppDatabaseSettings.APP_LABELS_IN_LIST_KEY))); | ||||
| ||||
binding.lightThemeTimeSelectionFrame.setVisibility( | ||||
themeList[themeSelectedChoice].startsWith("Auto") ? View.VISIBLE : View.GONE); | ||||
binding.darkThemeTimeSelectionFrame.setVisibility( | ||||
themeList[themeSelectedChoice].startsWith("Auto") ? View.VISIBLE : View.GONE); | ||||
| ||||
binding.themeChipGroup.setOnCheckedStateChangeListener( | ||||
(group, checkedIds) -> { | ||||
Log.d(TAG, "Theme chip checked: " + checkedIds); | ||||
if (checkedIds.size() == 1) { | ||||
int newSelection = getThemeChipPosition(checkedIds.get(0)); | ||||
Log.d(TAG, "Theme new selection: " + newSelection); | ||||
if (newSelection != themeSelectedChoice) { | ||||
themeSelectedChoice = newSelection; | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
String.valueOf(newSelection), | ||||
AppDatabaseSettings.APP_THEME_KEY); | ||||
binding.lightThemeTimeSelectionFrame.setVisibility( | ||||
themeList[newSelection].startsWith("Auto") | ||||
? View.VISIBLE | ||||
: View.GONE); | ||||
binding.darkThemeTimeSelectionFrame.setVisibility( | ||||
themeList[newSelection].startsWith("Auto") | ||||
? View.VISIBLE | ||||
: View.GONE); | ||||
SettingsFragment.refreshParent = true; | ||||
requireActivity().recreate(); | ||||
SnackBar.success( | ||||
requireContext(), | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
} | ||||
} | ||||
}); | ||||
| ||||
binding.lightThemeTimeSelectionFrame.setOnClickListener(v -> lightTimePicker()); | ||||
binding.darkThemeTimeSelectionFrame.setOnClickListener(v -> darkTimePicker()); | ||||
| ||||
binding.customFontChipGroup.setOnCheckedStateChangeListener( | ||||
(group, checkedIds) -> { | ||||
Log.d(TAG, "Font chip checked: " + checkedIds); | ||||
if (checkedIds.size() == 1) { | ||||
int newSelection = getCustomFontChipPosition(checkedIds.get(0)); | ||||
Log.d(TAG, "Font new selection: " + newSelection); | ||||
if (newSelection != customFontSelectedChoice) { | ||||
customFontSelectedChoice = newSelection; | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
String.valueOf(newSelection), | ||||
AppDatabaseSettings.APP_FONT_KEY); | ||||
new Handler() | ||||
.postDelayed( | ||||
() -> { | ||||
AppUtil.typeface = null; // reset typeface | ||||
FontsOverride.setDefaultFont(requireContext()); | ||||
SettingsFragment.refreshParent = true; | ||||
requireActivity().recreate(); | ||||
}, | ||||
1000); | ||||
SnackBar.success( | ||||
requireContext(), | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
} | ||||
} | ||||
}); | ||||
| ||||
binding.fragmentTabsAnimationChipGroup.setOnCheckedStateChangeListener( | ||||
(group, checkedIds) -> { | ||||
Log.d(TAG, "Tabs animation chip checked: " + checkedIds); | ||||
if (checkedIds.size() == 1) { | ||||
int newSelection = getFragmentTabsAnimationChipPosition(checkedIds.get(0)); | ||||
Log.d(TAG, "Tabs animation new selection: " + newSelection); | ||||
if (newSelection != fragmentTabsAnimationSelectedChoice) { | ||||
fragmentTabsAnimationSelectedChoice = newSelection; | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
String.valueOf(newSelection), | ||||
AppDatabaseSettings.APP_TABS_ANIMATION_KEY); | ||||
SettingsFragment.refreshParent = true; | ||||
requireActivity().recreate(); | ||||
SnackBar.success( | ||||
requireContext(), | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
} | ||||
} | ||||
}); | ||||
| ||||
binding.counterBadgeFrame.setOnClickListener( | ||||
v -> | ||||
binding.switchCounterBadge.setChecked( | ||||
!binding.switchCounterBadge.isChecked())); | ||||
binding.switchCounterBadge.setOnCheckedChangeListener( | ||||
(buttonView, isChecked) -> { | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
String.valueOf(isChecked), | ||||
AppDatabaseSettings.APP_COUNTER_KEY); | ||||
SnackBar.success( | ||||
requireContext(), | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
| ||||
binding.hideEmailLangInProfileFrame.setOnClickListener( | ||||
v -> | ||||
binding.switchHideEmailLangInProfile.setChecked( | ||||
!binding.switchHideEmailLangInProfile.isChecked())); | ||||
binding.switchHideEmailLangInProfile.setOnCheckedChangeListener( | ||||
(buttonView, isChecked) -> { | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
String.valueOf(isChecked), | ||||
AppDatabaseSettings.APP_USER_PROFILE_HIDE_EMAIL_LANGUAGE_KEY); | ||||
SnackBar.success( | ||||
requireContext(), | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
| ||||
binding.hideEmailNavDrawerFrame.setOnClickListener( | ||||
v -> | ||||
binding.switchHideEmailNavDrawer.setChecked( | ||||
!binding.switchHideEmailNavDrawer.isChecked())); | ||||
binding.switchHideEmailNavDrawer.setOnCheckedChangeListener( | ||||
(buttonView, isChecked) -> { | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
String.valueOf(isChecked), | ||||
AppDatabaseSettings.APP_USER_HIDE_EMAIL_IN_NAV_KEY); | ||||
SnackBar.success( | ||||
requireContext(), | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
| ||||
binding.labelsInListFrame.setOnClickListener( | ||||
v -> | ||||
binding.switchLabelsInListBadge.setChecked( | ||||
!binding.switchLabelsInListBadge.isChecked())); | ||||
binding.switchLabelsInListBadge.setOnCheckedChangeListener( | ||||
(buttonView, isChecked) -> { | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
String.valueOf(isChecked), | ||||
AppDatabaseSettings.APP_LABELS_IN_LIST_KEY); | ||||
SnackBar.success( | ||||
requireContext(), | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
| ||||
binding.langFrame.setOnClickListener( | ||||
v -> { | ||||
MaterialAlertDialogBuilder builder = | ||||
new MaterialAlertDialogBuilder(requireContext()) | ||||
.setTitle(R.string.settingsLanguageSelectorDialogTitle) | ||||
.setCancelable(langSelectedChoice != -1) | ||||
.setNeutralButton(R.string.cancelButton, null) | ||||
.setSingleChoiceItems( | ||||
lang.values().toArray(new String[0]), | ||||
langSelectedChoice, | ||||
(dialog, i) -> { | ||||
String selectedLanguage = | ||||
lang.keySet().toArray(new String[0])[i]; | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
i + "|" + selectedLanguage, | ||||
AppDatabaseSettings.APP_LOCALE_KEY); | ||||
SettingsFragment.refreshParent = true; | ||||
requireActivity().recreate(); | ||||
dialog.dismiss(); | ||||
SnackBar.success( | ||||
requireContext(), | ||||
requireActivity() | ||||
.findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
builder.create().show(); | ||||
}); | ||||
| ||||
binding.helpTranslate.setOnClickListener( | ||||
v -> | ||||
AppUtil.openUrlInBrowser( | ||||
requireContext(), getResources().getString(R.string.crowdInLink))); | ||||
| ||||
return binding.getRoot(); | ||||
} | ||||
| ||||
private void lightTimePicker() { | ||||
int hour = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), | ||||
AppDatabaseSettings.APP_THEME_AUTO_LIGHT_HOUR_KEY)); | ||||
int minute = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), | ||||
AppDatabaseSettings.APP_THEME_AUTO_LIGHT_MIN_KEY)); | ||||
MaterialTimePicker picker = | ||||
new MaterialTimePicker.Builder().setHour(hour).setMinute(minute).build(); | ||||
picker.addOnPositiveButtonClickListener( | ||||
v -> { | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
String.valueOf(picker.getHour()), | ||||
AppDatabaseSettings.APP_THEME_AUTO_LIGHT_HOUR_KEY); | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
String.valueOf(picker.getMinute()), | ||||
AppDatabaseSettings.APP_THEME_AUTO_LIGHT_MIN_KEY); | ||||
SettingsFragment.refreshParent = true; | ||||
requireActivity().recreate(); | ||||
String minuteStr = | ||||
picker.getMinute() < 10 | ||||
? "0" + picker.getMinute() | ||||
: String.valueOf(picker.getMinute()); | ||||
String hourStr = | ||||
picker.getHour() < 10 | ||||
? "0" + picker.getHour() | ||||
: String.valueOf(picker.getHour()); | ||||
binding.lightThemeSelectedTime.setText( | ||||
getResources() | ||||
.getString( | ||||
R.string.settingsThemeTimeSelectedHint, | ||||
hourStr, | ||||
minuteStr)); | ||||
SnackBar.success( | ||||
requireContext(), | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
picker.show(getParentFragmentManager(), "lightTimePicker"); | ||||
} | ||||
| ||||
private void darkTimePicker() { | ||||
int hour = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), | ||||
AppDatabaseSettings.APP_THEME_AUTO_DARK_HOUR_KEY)); | ||||
int minute = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), AppDatabaseSettings.APP_THEME_AUTO_DARK_MIN_KEY)); | ||||
MaterialTimePicker picker = | ||||
new MaterialTimePicker.Builder().setHour(hour).setMinute(minute).build(); | ||||
picker.addOnPositiveButtonClickListener( | ||||
v -> { | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
String.valueOf(picker.getHour()), | ||||
AppDatabaseSettings.APP_THEME_AUTO_DARK_HOUR_KEY); | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
String.valueOf(picker.getMinute()), | ||||
AppDatabaseSettings.APP_THEME_AUTO_DARK_MIN_KEY); | ||||
SettingsFragment.refreshParent = true; | ||||
requireActivity().recreate(); | ||||
String minuteStr = | ||||
picker.getMinute() < 10 | ||||
? "0" + picker.getMinute() | ||||
: String.valueOf(picker.getMinute()); | ||||
String hourStr = | ||||
picker.getHour() < 10 | ||||
? "0" + picker.getHour() | ||||
: String.valueOf(picker.getHour()); | ||||
binding.darkThemeSelectedTime.setText( | ||||
getResources() | ||||
.getString( | ||||
R.string.settingsThemeTimeSelectedHint, | ||||
hourStr, | ||||
minuteStr)); | ||||
SnackBar.success( | ||||
requireContext(), | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
picker.show(getParentFragmentManager(), "darkTimePicker"); | ||||
} | ||||
| ||||
private int getThemeChipPosition(int checkedId) { | ||||
for (int i = 0; i < binding.themeChipGroup.getChildCount(); i++) { | ||||
Chip chip = (Chip) binding.themeChipGroup.getChildAt(i); | ||||
if (chip.getId() == checkedId) return i; | ||||
} | ||||
return themeSelectedChoice; | ||||
} | ||||
| ||||
private int getCustomFontChipPosition(int checkedId) { | ||||
for (int i = 0; i < binding.customFontChipGroup.getChildCount(); i++) { | ||||
Chip chip = (Chip) binding.customFontChipGroup.getChildAt(i); | ||||
if (chip.getId() == checkedId) return i; | ||||
} | ||||
return customFontSelectedChoice; | ||||
} | ||||
| ||||
private int getFragmentTabsAnimationChipPosition(int checkedId) { | ||||
for (int i = 0; i < binding.fragmentTabsAnimationChipGroup.getChildCount(); i++) { | ||||
Chip chip = (Chip) binding.fragmentTabsAnimationChipGroup.getChildAt(i); | ||||
if (chip.getId() == checkedId) return i; | ||||
} | ||||
return fragmentTabsAnimationSelectedChoice; | ||||
} | ||||
| ||||
private String getLanguageDisplayName(String langCode) { | ||||
Locale english = new Locale("en"); | ||||
String[] multiCodeLang = langCode.split("-"); | ||||
String countryCode = langCode.contains("-") ? multiCodeLang[1] : ""; | ||||
langCode = multiCodeLang[0]; | ||||
Locale translated = new Locale(langCode, countryCode); | ||||
return String.format( | ||||
"%s (%s)", | ||||
translated.getDisplayName(translated), translated.getDisplayName(english)); | ||||
} | ||||
| ||||
@Override | ||||
public void onDestroyView() { | ||||
super.onDestroyView(); | ||||
binding = null; | ||||
} | ||||
} |
| @ -0,0 +1,266 @@ | |||
package org.mian.gitnex.fragments; | ||||
| ||||
import android.app.Activity; | ||||
import android.content.Context; | ||||
import android.content.Intent; | ||||
import android.net.Uri; | ||||
import android.os.Bundle; | ||||
import android.view.LayoutInflater; | ||||
import android.view.View; | ||||
import android.view.ViewGroup; | ||||
import androidx.activity.result.ActivityResultLauncher; | ||||
import androidx.activity.result.contract.ActivityResultContracts; | ||||
import androidx.annotation.NonNull; | ||||
import androidx.annotation.Nullable; | ||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment; | ||||
import java.io.File; | ||||
import java.io.FileNotFoundException; | ||||
import java.io.IOException; | ||||
import java.io.InputStream; | ||||
import java.io.OutputStream; | ||||
import java.time.LocalDate; | ||||
import java.util.ArrayList; | ||||
import java.util.List; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.database.api.BaseApi; | ||||
import org.mian.gitnex.database.api.UserAccountsApi; | ||||
import org.mian.gitnex.database.models.UserAccount; | ||||
import org.mian.gitnex.databinding.BottomSheetSettingsBackupRestoreBinding; | ||||
import org.mian.gitnex.helpers.AppUtil; | ||||
import org.mian.gitnex.helpers.BackupUtil; | ||||
import org.mian.gitnex.helpers.SnackBar; | ||||
| ||||
/** | ||||
* @author mmarif | ||||
*/ | ||||
public class BottomSheetSettingsBackupRestoreFragment extends BottomSheetDialogFragment { | ||||
| ||||
private BottomSheetSettingsBackupRestoreBinding binding; | ||||
private Context ctx; | ||||
private final String DATABASE_NAME = "gitnex"; | ||||
private String BACKUP_DATABASE_NAME; | ||||
| ||||
@Nullable @Override | ||||
public View onCreateView( | ||||
@NonNull LayoutInflater inflater, | ||||
@Nullable ViewGroup container, | ||||
@Nullable Bundle savedInstanceState) { | ||||
binding = BottomSheetSettingsBackupRestoreBinding.inflate(inflater, container, false); | ||||
ctx = requireContext(); | ||||
| ||||
BACKUP_DATABASE_NAME = ctx.getString(R.string.appName) + "-" + LocalDate.now() + ".backup"; | ||||
| ||||
binding.backupButton.setOnClickListener(v -> requestBackupFileDownload()); | ||||
binding.restoreButton.setOnClickListener(v -> requestRestoreFile()); | ||||
| ||||
binding.bottomSheetHeader.setText( | ||||
getString( | ||||
R.string.backupRestore, | ||||
getString(R.string.backup), | ||||
getString(R.string.restore))); | ||||
| ||||
return binding.getRoot(); | ||||
} | ||||
| ||||
private final ActivityResultLauncher<Intent> activityBackupFileLauncher = | ||||
registerForActivityResult( | ||||
new ActivityResultContracts.StartActivityForResult(), | ||||
result -> { | ||||
if (result.getResultCode() == Activity.RESULT_OK | ||||
&& result.getData() != null) { | ||||
Uri backupFileUri = result.getData().getData(); | ||||
backupDatabaseThread(backupFileUri); | ||||
} | ||||
}); | ||||
| ||||
private void requestBackupFileDownload() { | ||||
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); | ||||
intent.addCategory(Intent.CATEGORY_OPENABLE); | ||||
intent.putExtra(Intent.EXTRA_TITLE, BACKUP_DATABASE_NAME); | ||||
intent.setType("application/octet-stream"); | ||||
activityBackupFileLauncher.launch(intent); | ||||
} | ||||
| ||||
private void backupDatabaseThread(Uri backupFileUri) { | ||||
List<File> filesToZip = new ArrayList<>(); | ||||
Thread backupDatabaseThread = | ||||
new Thread( | ||||
() -> { | ||||
File tempDir = BackupUtil.getTempDir(ctx); | ||||
try { | ||||
BackupUtil.checkpointIfWALEnabled(ctx, DATABASE_NAME); | ||||
File databaseBackupFile = | ||||
BackupUtil.backupDatabaseFile( | ||||
ctx.getDatabasePath(DATABASE_NAME).getPath(), | ||||
tempDir.getPath() + "/" + DATABASE_NAME); | ||||
filesToZip.add(databaseBackupFile); | ||||
String tempZipFilename = "temp.backup"; | ||||
boolean zipFileStatus = | ||||
BackupUtil.zip( | ||||
filesToZip, tempDir.getPath(), tempZipFilename); | ||||
if (zipFileStatus) { | ||||
File tempZipFile = new File(tempDir, tempZipFilename); | ||||
Uri zipFileUri = Uri.fromFile(tempZipFile); | ||||
InputStream inputStream = | ||||
ctx.getContentResolver().openInputStream(zipFileUri); | ||||
OutputStream outputStream = | ||||
ctx.getContentResolver() | ||||
.openOutputStream(backupFileUri); | ||||
boolean copySucceeded = | ||||
BackupUtil.copyFileWithStreams( | ||||
inputStream, outputStream); | ||||
requireActivity() | ||||
.runOnUiThread( | ||||
() -> { | ||||
if (copySucceeded) { | ||||
SnackBar.success( | ||||
ctx, | ||||
requireActivity() | ||||
.findViewById( | ||||
android.R.id | ||||
.content), | ||||
getString( | ||||
R.string | ||||
.backupFileSuccess)); | ||||
} else { | ||||
SnackBar.error( | ||||
ctx, | ||||
requireActivity() | ||||
.findViewById( | ||||
android.R.id | ||||
.content), | ||||
getString( | ||||
R.string | ||||
.backupFileError)); | ||||
} | ||||
}); | ||||
if (copySucceeded) { | ||||
tempZipFile.delete(); | ||||
} | ||||
} else { | ||||
requireActivity() | ||||
.runOnUiThread( | ||||
() -> | ||||
SnackBar.error( | ||||
ctx, | ||||
requireActivity() | ||||
.findViewById( | ||||
android.R.id | ||||
.content), | ||||
getString( | ||||
R.string | ||||
.backupFileError))); | ||||
} | ||||
} catch (Exception e) { | ||||
requireActivity() | ||||
.runOnUiThread( | ||||
() -> | ||||
SnackBar.error( | ||||
ctx, | ||||
requireActivity() | ||||
.findViewById( | ||||
android.R.id | ||||
.content), | ||||
getString( | ||||
R.string.backupFileError))); | ||||
} finally { | ||||
for (File file : filesToZip) { | ||||
if (file != null && file.exists()) { | ||||
file.delete(); | ||||
} | ||||
} | ||||
} | ||||
}); | ||||
backupDatabaseThread.setDaemon(false); | ||||
backupDatabaseThread.start(); | ||||
} | ||||
| ||||
private final ActivityResultLauncher<Intent> activityRestoreFileLauncher = | ||||
registerForActivityResult( | ||||
new ActivityResultContracts.StartActivityForResult(), | ||||
result -> { | ||||
if (result.getResultCode() == Activity.RESULT_OK | ||||
&& result.getData() != null) { | ||||
Uri restoreFileUri = result.getData().getData(); | ||||
try { | ||||
assert restoreFileUri != null; | ||||
InputStream inputStream = | ||||
ctx.getContentResolver().openInputStream(restoreFileUri); | ||||
restoreDatabaseThread(inputStream); | ||||
} catch (FileNotFoundException e) { | ||||
SnackBar.error( | ||||
ctx, | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.restoreError)); | ||||
} | ||||
} | ||||
}); | ||||
| ||||
private void requestRestoreFile() { | ||||
Intent intentRestore = new Intent(Intent.ACTION_OPEN_DOCUMENT); | ||||
intentRestore.addCategory(Intent.CATEGORY_OPENABLE); | ||||
intentRestore.setType("*/*"); | ||||
String[] mimeTypes = {"application/octet-stream", "application/x-zip"}; | ||||
intentRestore.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes); | ||||
activityRestoreFileLauncher.launch(intentRestore); | ||||
} | ||||
| ||||
private void restoreDatabaseThread(InputStream inputStream) { | ||||
Thread restoreDatabaseThread = | ||||
new Thread( | ||||
() -> { | ||||
boolean exceptionOccurred = false; | ||||
try { | ||||
String tempDir = BackupUtil.getTempDir(ctx).getPath(); | ||||
BackupUtil.unzip(inputStream, tempDir); | ||||
BackupUtil.checkpointIfWALEnabled(ctx, DATABASE_NAME); | ||||
restoreDatabaseFile(ctx, tempDir, DATABASE_NAME); | ||||
UserAccountsApi userAccountsApi = | ||||
BaseApi.getInstance(ctx, UserAccountsApi.class); | ||||
assert userAccountsApi != null; | ||||
UserAccount account = userAccountsApi.getAccountById(1); | ||||
AppUtil.switchToAccount(ctx, account); | ||||
} catch (Exception e) { | ||||
exceptionOccurred = true; | ||||
requireActivity() | ||||
.runOnUiThread( | ||||
() -> | ||||
SnackBar.error( | ||||
ctx, | ||||
requireActivity() | ||||
.findViewById( | ||||
android.R.id | ||||
.content), | ||||
getString(R.string.restoreError))); | ||||
} finally { | ||||
if (!exceptionOccurred) { | ||||
requireActivity().runOnUiThread(this::restartApp); | ||||
} | ||||
} | ||||
}); | ||||
restoreDatabaseThread.setDaemon(false); | ||||
restoreDatabaseThread.start(); | ||||
} | ||||
| ||||
private void restoreDatabaseFile(Context context, String tempDir, String nameOfFileToRestore) | ||||
throws IOException { | ||||
File currentDbFile = new File(context.getDatabasePath(DATABASE_NAME).getPath()); | ||||
File newDbFile = new File(tempDir + "/" + nameOfFileToRestore); | ||||
if (newDbFile.exists()) { | ||||
BackupUtil.copyFile(newDbFile, currentDbFile, false); | ||||
} | ||||
} | ||||
| ||||
private void restartApp() { | ||||
Intent i = ctx.getPackageManager().getLaunchIntentForPackage(ctx.getPackageName()); | ||||
assert i != null; | ||||
startActivity(Intent.makeRestartActivityTask(i.getComponent())); | ||||
Runtime.getRuntime().exit(0); | ||||
} | ||||
| ||||
@Override | ||||
public void onDestroyView() { | ||||
super.onDestroyView(); | ||||
binding = null; // Prevent memory leaks | ||||
} | ||||
} |
| @ -0,0 +1,186 @@ | |||
package org.mian.gitnex.fragments; | ||||
| ||||
import android.os.Bundle; | ||||
import android.view.LayoutInflater; | ||||
import android.view.View; | ||||
import android.view.ViewGroup; | ||||
import androidx.annotation.NonNull; | ||||
import androidx.annotation.Nullable; | ||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.databinding.BottomSheetSettingsCodeEditorBinding; | ||||
import org.mian.gitnex.helpers.AppDatabaseSettings; | ||||
import org.mian.gitnex.helpers.SnackBar; | ||||
| ||||
/** | ||||
* @author mmarif | ||||
*/ | ||||
public class BottomSheetSettingsCodeEditorFragment extends BottomSheetDialogFragment { | ||||
| ||||
private BottomSheetSettingsCodeEditorBinding binding; | ||||
private static int colorSelectedChoice; | ||||
private static int indentationSelectedChoice; | ||||
private static int indentationTabsSelectedChoice; | ||||
private static String[] indentationList; | ||||
| ||||
@Nullable @Override | ||||
public View onCreateView( | ||||
@NonNull LayoutInflater inflater, | ||||
@Nullable ViewGroup container, | ||||
@Nullable Bundle savedInstanceState) { | ||||
binding = BottomSheetSettingsCodeEditorBinding.inflate(inflater, container, false); | ||||
| ||||
indentationList = getResources().getStringArray(R.array.ceIndentation); | ||||
colorSelectedChoice = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), AppDatabaseSettings.APP_CE_SYNTAX_HIGHLIGHT_KEY)); | ||||
indentationSelectedChoice = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), AppDatabaseSettings.APP_CE_INDENTATION_KEY)); | ||||
indentationTabsSelectedChoice = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), AppDatabaseSettings.APP_CE_TABS_WIDTH_KEY)); | ||||
| ||||
setColorChipSelection(colorSelectedChoice); | ||||
setIndentationChipSelection(indentationSelectedChoice); | ||||
setIndentationTabsChipSelection(indentationTabsSelectedChoice); | ||||
| ||||
updateTabsWidthVisibility(); | ||||
| ||||
binding.ceColorChipGroup.setOnCheckedStateChangeListener( | ||||
(group, checkedIds) -> { | ||||
if (checkedIds.size() == 1) { | ||||
int newSelection = getColorChipPosition(checkedIds.get(0)); | ||||
if (newSelection != colorSelectedChoice) { | ||||
colorSelectedChoice = newSelection; | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
String.valueOf(newSelection), | ||||
AppDatabaseSettings.APP_CE_SYNTAX_HIGHLIGHT_KEY); | ||||
SettingsFragment.refreshParent = true; | ||||
SnackBar.success( | ||||
requireContext(), | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
} | ||||
} | ||||
}); | ||||
| ||||
binding.indentationChipGroup.setOnCheckedStateChangeListener( | ||||
(group, checkedIds) -> { | ||||
if (checkedIds.size() == 1) { | ||||
int newSelection = getIndentationChipPosition(checkedIds.get(0)); | ||||
if (newSelection != indentationSelectedChoice) { | ||||
indentationSelectedChoice = newSelection; | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
String.valueOf(newSelection), | ||||
AppDatabaseSettings.APP_CE_INDENTATION_KEY); | ||||
updateTabsWidthVisibility(); | ||||
SettingsFragment.refreshParent = true; | ||||
SnackBar.success( | ||||
requireContext(), | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
} | ||||
} | ||||
}); | ||||
| ||||
binding.indentationTabsChipGroup.setOnCheckedStateChangeListener( | ||||
(group, checkedIds) -> { | ||||
if (checkedIds.size() == 1) { | ||||
int newSelection = getIndentationTabsChipPosition(checkedIds.get(0)); | ||||
if (newSelection != indentationTabsSelectedChoice) { | ||||
indentationTabsSelectedChoice = newSelection; | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
String.valueOf(newSelection), | ||||
AppDatabaseSettings.APP_CE_TABS_WIDTH_KEY); | ||||
SettingsFragment.refreshParent = true; | ||||
SnackBar.success( | ||||
requireContext(), | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
} | ||||
} | ||||
}); | ||||
| ||||
return binding.getRoot(); | ||||
} | ||||
| ||||
private void setColorChipSelection(int position) { | ||||
switch (position) { | ||||
case 0: | ||||
binding.chipColorDark.setChecked(true); | ||||
break; | ||||
case 1: | ||||
binding.chipColorLight.setChecked(true); | ||||
break; | ||||
} | ||||
} | ||||
| ||||
private int getColorChipPosition(int checkedId) { | ||||
if (checkedId == R.id.chipColorDark) return 0; | ||||
if (checkedId == R.id.chipColorLight) return 1; | ||||
return colorSelectedChoice; | ||||
} | ||||
| ||||
private void setIndentationChipSelection(int position) { | ||||
switch (position) { | ||||
case 0: | ||||
binding.chipIndentSpaces.setChecked(true); | ||||
break; | ||||
case 1: | ||||
binding.chipIndentTabs.setChecked(true); | ||||
break; | ||||
} | ||||
} | ||||
| ||||
private int getIndentationChipPosition(int checkedId) { | ||||
if (checkedId == R.id.chipIndentSpaces) return 0; | ||||
if (checkedId == R.id.chipIndentTabs) return 1; | ||||
return indentationSelectedChoice; | ||||
} | ||||
| ||||
private void setIndentationTabsChipSelection(int position) { | ||||
switch (position) { | ||||
case 0: | ||||
binding.chipTabs2.setChecked(true); | ||||
break; | ||||
case 1: | ||||
binding.chipTabs4.setChecked(true); | ||||
break; | ||||
case 2: | ||||
binding.chipTabs6.setChecked(true); | ||||
break; | ||||
case 3: | ||||
binding.chipTabs8.setChecked(true); | ||||
break; | ||||
} | ||||
} | ||||
| ||||
private int getIndentationTabsChipPosition(int checkedId) { | ||||
if (checkedId == R.id.chipTabs2) return 0; | ||||
if (checkedId == R.id.chipTabs4) return 1; | ||||
if (checkedId == R.id.chipTabs6) return 2; | ||||
if (checkedId == R.id.chipTabs8) return 3; | ||||
return indentationTabsSelectedChoice; | ||||
} | ||||
| ||||
private void updateTabsWidthVisibility() { | ||||
boolean isTabsSelected = | ||||
indentationList[indentationSelectedChoice].startsWith( | ||||
getString(R.string.ceIndentationTabs)); | ||||
binding.indentationTabsSelectionFrame.setVisibility( | ||||
isTabsSelected ? View.VISIBLE : View.GONE); | ||||
} | ||||
| ||||
@Override | ||||
public void onDestroyView() { | ||||
super.onDestroyView(); | ||||
binding = null; | ||||
} | ||||
} |
| @ -0,0 +1,213 @@ | |||
package org.mian.gitnex.fragments; | ||||
| ||||
import android.os.Bundle; | ||||
import android.view.LayoutInflater; | ||||
import android.view.View; | ||||
import android.view.ViewGroup; | ||||
import androidx.annotation.NonNull; | ||||
import androidx.annotation.Nullable; | ||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.databinding.BottomSheetSettingsGeneralBinding; | ||||
import org.mian.gitnex.helpers.AppDatabaseSettings; | ||||
import org.mian.gitnex.helpers.SnackBar; | ||||
| ||||
/** | ||||
* @author mmarif | ||||
*/ | ||||
public class BottomSheetSettingsGeneralFragment extends BottomSheetDialogFragment { | ||||
| ||||
private BottomSheetSettingsGeneralBinding binding; | ||||
private static int homeScreenSelectedChoice; | ||||
private static int defaultLinkHandlerScreenSelectedChoice; | ||||
| ||||
@Nullable @Override | ||||
public View onCreateView( | ||||
@NonNull LayoutInflater inflater, | ||||
@Nullable ViewGroup container, | ||||
@Nullable Bundle savedInstanceState) { | ||||
binding = BottomSheetSettingsGeneralBinding.inflate(inflater, container, false); | ||||
| ||||
homeScreenSelectedChoice = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), AppDatabaseSettings.APP_HOME_SCREEN_KEY)); | ||||
defaultLinkHandlerScreenSelectedChoice = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), AppDatabaseSettings.APP_LINK_HANDLER_KEY)); | ||||
| ||||
setHomeScreenChipSelection(homeScreenSelectedChoice); | ||||
setLinkHandlerChipSelection(defaultLinkHandlerScreenSelectedChoice); | ||||
binding.switchTabs.setChecked( | ||||
Boolean.parseBoolean( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), AppDatabaseSettings.APP_CUSTOM_BROWSER_KEY))); | ||||
binding.crashReportsSwitch.setChecked( | ||||
Boolean.parseBoolean( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), AppDatabaseSettings.APP_CRASH_REPORTS_KEY))); | ||||
| ||||
binding.homeScreenChipGroup.setOnCheckedStateChangeListener( | ||||
(group, checkedIds) -> { | ||||
if (checkedIds.size() == 1) { | ||||
int newSelection = getHomeScreenChipPosition(checkedIds.get(0)); | ||||
if (newSelection != homeScreenSelectedChoice) { | ||||
homeScreenSelectedChoice = newSelection; | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
String.valueOf(newSelection), | ||||
AppDatabaseSettings.APP_HOME_SCREEN_KEY); | ||||
SettingsFragment.refreshParent = true; | ||||
SnackBar.success( | ||||
requireContext(), | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
} | ||||
} | ||||
}); | ||||
| ||||
binding.linkHandlerChipGroup.setOnCheckedStateChangeListener( | ||||
(group, checkedIds) -> { | ||||
if (checkedIds.size() == 1) { | ||||
int newSelection = getLinkHandlerChipPosition(checkedIds.get(0)); | ||||
if (newSelection != defaultLinkHandlerScreenSelectedChoice) { | ||||
defaultLinkHandlerScreenSelectedChoice = newSelection; | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
String.valueOf(newSelection), | ||||
AppDatabaseSettings.APP_LINK_HANDLER_KEY); | ||||
SettingsFragment.refreshParent = true; | ||||
SnackBar.success( | ||||
requireContext(), | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
} | ||||
} | ||||
}); | ||||
| ||||
binding.customTabsFrame.setOnClickListener( | ||||
v -> binding.switchTabs.setChecked(!binding.switchTabs.isChecked())); | ||||
binding.switchTabs.setOnCheckedChangeListener( | ||||
(buttonView, isChecked) -> { | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
String.valueOf(isChecked), | ||||
AppDatabaseSettings.APP_CUSTOM_BROWSER_KEY); | ||||
SnackBar.success( | ||||
requireContext(), | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
| ||||
binding.enableSendReports.setOnClickListener( | ||||
v -> | ||||
binding.crashReportsSwitch.setChecked( | ||||
!binding.crashReportsSwitch.isChecked())); | ||||
binding.crashReportsSwitch.setOnCheckedChangeListener( | ||||
(buttonView, isChecked) -> { | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
String.valueOf(isChecked), | ||||
AppDatabaseSettings.APP_CRASH_REPORTS_KEY); | ||||
SnackBar.success( | ||||
requireContext(), | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
| ||||
return binding.getRoot(); | ||||
} | ||||
| ||||
private void setHomeScreenChipSelection(int position) { | ||||
switch (position) { | ||||
case 0: | ||||
binding.chipHomeScreen0.setChecked(true); | ||||
break; | ||||
case 1: | ||||
binding.chipHomeScreen1.setChecked(true); | ||||
break; | ||||
case 2: | ||||
binding.chipHomeScreen2.setChecked(true); | ||||
break; | ||||
case 3: | ||||
binding.chipHomeScreen3.setChecked(true); | ||||
break; | ||||
case 4: | ||||
binding.chipHomeScreen4.setChecked(true); | ||||
break; | ||||
case 5: | ||||
binding.chipHomeScreen5.setChecked(true); | ||||
break; | ||||
case 6: | ||||
binding.chipHomeScreen6.setChecked(true); | ||||
break; | ||||
case 7: | ||||
binding.chipHomeScreen7.setChecked(true); | ||||
break; | ||||
case 8: | ||||
binding.chipHomeScreen8.setChecked(true); | ||||
break; | ||||
case 9: | ||||
binding.chipHomeScreen9.setChecked(true); | ||||
break; | ||||
case 10: | ||||
binding.chipHomeScreen10.setChecked(true); | ||||
break; | ||||
case 11: | ||||
binding.chipHomeScreen11.setChecked(true); | ||||
break; | ||||
} | ||||
} | ||||
| ||||
private int getHomeScreenChipPosition(int checkedId) { | ||||
if (checkedId == R.id.chipHomeScreen0) return 0; | ||||
if (checkedId == R.id.chipHomeScreen1) return 1; | ||||
if (checkedId == R.id.chipHomeScreen2) return 2; | ||||
if (checkedId == R.id.chipHomeScreen3) return 3; | ||||
if (checkedId == R.id.chipHomeScreen4) return 4; | ||||
if (checkedId == R.id.chipHomeScreen5) return 5; | ||||
if (checkedId == R.id.chipHomeScreen6) return 6; | ||||
if (checkedId == R.id.chipHomeScreen7) return 7; | ||||
if (checkedId == R.id.chipHomeScreen8) return 8; | ||||
if (checkedId == R.id.chipHomeScreen9) return 9; | ||||
if (checkedId == R.id.chipHomeScreen10) return 10; | ||||
if (checkedId == R.id.chipHomeScreen11) return 11; | ||||
return homeScreenSelectedChoice; | ||||
} | ||||
| ||||
private void setLinkHandlerChipSelection(int position) { | ||||
switch (position) { | ||||
case 0: | ||||
binding.chipLinkHandler0.setChecked(true); | ||||
break; | ||||
case 1: | ||||
binding.chipLinkHandler1.setChecked(true); | ||||
break; | ||||
case 2: | ||||
binding.chipLinkHandler2.setChecked(true); | ||||
break; | ||||
case 3: | ||||
binding.chipLinkHandler3.setChecked(true); | ||||
break; | ||||
case 4: | ||||
binding.chipLinkHandler4.setChecked(true); | ||||
break; | ||||
} | ||||
} | ||||
| ||||
private int getLinkHandlerChipPosition(int checkedId) { | ||||
if (checkedId == R.id.chipLinkHandler0) return 0; | ||||
if (checkedId == R.id.chipLinkHandler1) return 1; | ||||
if (checkedId == R.id.chipLinkHandler2) return 2; | ||||
if (checkedId == R.id.chipLinkHandler3) return 3; | ||||
if (checkedId == R.id.chipLinkHandler4) return 4; | ||||
return defaultLinkHandlerScreenSelectedChoice; | ||||
} | ||||
| ||||
@Override | ||||
public void onDestroyView() { | ||||
super.onDestroyView(); | ||||
binding = null; | ||||
} | ||||
} |
| @ -0,0 +1,130 @@ | |||
package org.mian.gitnex.fragments; | ||||
| ||||
import android.os.Bundle; | ||||
import android.view.LayoutInflater; | ||||
import android.view.View; | ||||
import android.view.ViewGroup; | ||||
import androidx.annotation.NonNull; | ||||
import androidx.annotation.Nullable; | ||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.databinding.BottomSheetSettingsNotificationsBinding; | ||||
import org.mian.gitnex.helpers.AppDatabaseSettings; | ||||
import org.mian.gitnex.helpers.AppUtil; | ||||
import org.mian.gitnex.helpers.SnackBar; | ||||
import org.mian.gitnex.notifications.Notifications; | ||||
| ||||
/** | ||||
* @author mmarif | ||||
*/ | ||||
public class BottomSheetSettingsNotificationsFragment extends BottomSheetDialogFragment { | ||||
| ||||
private BottomSheetSettingsNotificationsBinding binding; | ||||
private static int pollingDelayListSelectedChoice; | ||||
| ||||
@Nullable @Override | ||||
public View onCreateView( | ||||
@NonNull LayoutInflater inflater, | ||||
@Nullable ViewGroup container, | ||||
@Nullable Bundle savedInstanceState) { | ||||
binding = BottomSheetSettingsNotificationsBinding.inflate(inflater, container, false); | ||||
| ||||
// Initialize polling delay | ||||
pollingDelayListSelectedChoice = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), AppDatabaseSettings.APP_NOTIFICATIONS_DELAY_KEY)); | ||||
setChipSelection(pollingDelayListSelectedChoice); | ||||
| ||||
// Enable notifications switch | ||||
binding.enableNotificationsMode.setChecked( | ||||
Boolean.parseBoolean( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), AppDatabaseSettings.APP_NOTIFICATIONS_KEY))); | ||||
| ||||
if (!binding.enableNotificationsMode.isChecked()) { | ||||
AppUtil.setMultiVisibility(View.GONE, binding.pollingDelayFrame); | ||||
} | ||||
| ||||
binding.enableNotificationsMode.setOnCheckedChangeListener( | ||||
(buttonView, isChecked) -> { | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
String.valueOf(isChecked), | ||||
AppDatabaseSettings.APP_NOTIFICATIONS_KEY); | ||||
| ||||
if (isChecked) { | ||||
Notifications.startWorker(requireContext()); | ||||
AppUtil.setMultiVisibility(View.VISIBLE, binding.pollingDelayFrame); | ||||
} else { | ||||
Notifications.stopWorker(requireContext()); | ||||
AppUtil.setMultiVisibility(View.GONE, binding.pollingDelayFrame); | ||||
} | ||||
| ||||
SnackBar.success( | ||||
requireContext(), | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
}); | ||||
| ||||
binding.enableNotificationsFrame.setOnClickListener( | ||||
v -> | ||||
binding.enableNotificationsMode.setChecked( | ||||
!binding.enableNotificationsMode.isChecked())); | ||||
| ||||
// Polling delay selection | ||||
binding.pollingDelayChipGroup.setOnCheckedChangeListener( | ||||
(group, checkedId) -> { | ||||
int newSelection = getChipPosition(checkedId); | ||||
if (newSelection != pollingDelayListSelectedChoice) { | ||||
pollingDelayListSelectedChoice = newSelection; | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
String.valueOf(newSelection), | ||||
AppDatabaseSettings.APP_NOTIFICATIONS_DELAY_KEY); | ||||
| ||||
Notifications.stopWorker(requireContext()); | ||||
Notifications.startWorker(requireContext()); | ||||
| ||||
SettingsFragment.refreshParent = true; | ||||
SnackBar.success( | ||||
requireContext(), | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
} | ||||
}); | ||||
| ||||
return binding.getRoot(); | ||||
} | ||||
| ||||
private void setChipSelection(int position) { | ||||
switch (position) { | ||||
case 0: | ||||
binding.chip15Minutes.setChecked(true); | ||||
break; | ||||
case 1: | ||||
binding.chip30Minutes.setChecked(true); | ||||
break; | ||||
case 2: | ||||
binding.chip45Minutes.setChecked(true); | ||||
break; | ||||
case 3: | ||||
binding.chip1Hour.setChecked(true); | ||||
break; | ||||
} | ||||
} | ||||
| ||||
private int getChipPosition(int checkedId) { | ||||
if (checkedId == R.id.chip15Minutes) return 0; | ||||
if (checkedId == R.id.chip30Minutes) return 1; | ||||
if (checkedId == R.id.chip45Minutes) return 2; | ||||
if (checkedId == R.id.chip1Hour) return 3; | ||||
return pollingDelayListSelectedChoice; // Fallback to current selection | ||||
} | ||||
| ||||
@Override | ||||
public void onDestroyView() { | ||||
super.onDestroyView(); | ||||
binding = null; // Prevent memory leaks | ||||
} | ||||
} |
| @ -0,0 +1,304 @@ | |||
package org.mian.gitnex.fragments; | ||||
| ||||
import static androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG; | ||||
import static androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL; | ||||
| ||||
import android.app.KeyguardManager; | ||||
import android.content.Context; | ||||
import android.os.Bundle; | ||||
import android.view.LayoutInflater; | ||||
import android.view.View; | ||||
import android.view.ViewGroup; | ||||
import androidx.annotation.NonNull; | ||||
import androidx.annotation.Nullable; | ||||
import androidx.biometric.BiometricManager; | ||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment; | ||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder; | ||||
import java.io.File; | ||||
import java.io.IOException; | ||||
import org.apache.commons.io.FileUtils; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.databinding.BottomSheetSettingsSecurityBinding; | ||||
import org.mian.gitnex.helpers.AppDatabaseSettings; | ||||
import org.mian.gitnex.helpers.AppUtil; | ||||
import org.mian.gitnex.helpers.SnackBar; | ||||
import org.mian.gitnex.helpers.ssl.MemorizingTrustManager; | ||||
| ||||
/** | ||||
* @author mmarif | ||||
*/ | ||||
public class BottomSheetSettingsSecurityFragment extends BottomSheetDialogFragment { | ||||
| ||||
private BottomSheetSettingsSecurityBinding binding; | ||||
private static String[] cacheSizeDataList; | ||||
private static int cacheSizeDataSelectedChoice; | ||||
private static String[] cacheSizeImagesList; | ||||
private static int cacheSizeImagesSelectedChoice; | ||||
| ||||
@Nullable @Override | ||||
public View onCreateView( | ||||
@NonNull LayoutInflater inflater, | ||||
@Nullable ViewGroup container, | ||||
@Nullable Bundle savedInstanceState) { | ||||
binding = BottomSheetSettingsSecurityBinding.inflate(inflater, container, false); | ||||
| ||||
cacheSizeDataList = getResources().getStringArray(R.array.cacheSizeList); | ||||
cacheSizeImagesList = getResources().getStringArray(R.array.cacheSizeList); | ||||
| ||||
cacheSizeDataSelectedChoice = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), AppDatabaseSettings.APP_DATA_CACHE_KEY)); | ||||
cacheSizeImagesSelectedChoice = | ||||
Integer.parseInt( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), AppDatabaseSettings.APP_IMAGES_CACHE_KEY)); | ||||
| ||||
setCacheSizeDataChipSelection(cacheSizeDataSelectedChoice); | ||||
setCacheSizeImagesChipSelection(cacheSizeImagesSelectedChoice); | ||||
binding.switchBiometric.setChecked( | ||||
Boolean.parseBoolean( | ||||
AppDatabaseSettings.getSettingsValue( | ||||
requireContext(), AppDatabaseSettings.APP_BIOMETRIC_KEY))); | ||||
| ||||
File cacheDir = requireContext().getCacheDir(); | ||||
binding.clearCacheButton.setText( | ||||
getString( | ||||
R.string.clear_cache_button_text, | ||||
FileUtils.byteCountToDisplaySize( | ||||
(int) FileUtils.sizeOfDirectory(cacheDir)))); | ||||
| ||||
binding.switchBiometric.setOnCheckedChangeListener( | ||||
(buttonView, isChecked) -> { | ||||
if (isChecked) { | ||||
BiometricManager biometricManager = BiometricManager.from(requireContext()); | ||||
KeyguardManager keyguardManager = | ||||
(KeyguardManager) | ||||
requireContext().getSystemService(Context.KEYGUARD_SERVICE); | ||||
| ||||
if (!keyguardManager.isDeviceSecure()) { | ||||
switch (biometricManager.canAuthenticate( | ||||
BIOMETRIC_STRONG | DEVICE_CREDENTIAL)) { | ||||
case BiometricManager.BIOMETRIC_SUCCESS: | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
"true", | ||||
AppDatabaseSettings.APP_BIOMETRIC_KEY); | ||||
SnackBar.success( | ||||
requireContext(), | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
break; | ||||
case BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE: | ||||
case BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED: | ||||
case BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED: | ||||
case BiometricManager.BIOMETRIC_STATUS_UNKNOWN: | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
"false", | ||||
AppDatabaseSettings.APP_BIOMETRIC_KEY); | ||||
binding.switchBiometric.setChecked(false); | ||||
SnackBar.error( | ||||
requireContext(), | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.biometricNotSupported)); | ||||
break; | ||||
case BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE: | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
"false", | ||||
AppDatabaseSettings.APP_BIOMETRIC_KEY); | ||||
binding.switchBiometric.setChecked(false); | ||||
SnackBar.error( | ||||
requireContext(), | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.biometricNotAvailable)); | ||||
break; | ||||
case BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED: | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
"false", | ||||
AppDatabaseSettings.APP_BIOMETRIC_KEY); | ||||
binding.switchBiometric.setChecked(false); | ||||
SnackBar.info( | ||||
requireContext(), | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.enrollBiometric)); | ||||
break; | ||||
} | ||||
} else { | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
"true", | ||||
AppDatabaseSettings.APP_BIOMETRIC_KEY); | ||||
SnackBar.success( | ||||
requireContext(), | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
} | ||||
} else { | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), "false", AppDatabaseSettings.APP_BIOMETRIC_KEY); | ||||
SnackBar.success( | ||||
requireContext(), | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
} | ||||
}); | ||||
| ||||
binding.biometricFrame.setOnClickListener( | ||||
v -> binding.switchBiometric.setChecked(!binding.switchBiometric.isChecked())); | ||||
| ||||
binding.cacheSizeDataChipGroup.setOnCheckedStateChangeListener( | ||||
(group, checkedIds) -> { | ||||
if (checkedIds.size() == 1) { | ||||
int newSelection = getCacheSizeDataChipPosition(checkedIds.get(0)); | ||||
if (newSelection != cacheSizeDataSelectedChoice) { | ||||
cacheSizeDataSelectedChoice = newSelection; | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
cacheSizeDataList[newSelection], | ||||
AppDatabaseSettings.APP_DATA_CACHE_SIZE_KEY); | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
String.valueOf(newSelection), | ||||
AppDatabaseSettings.APP_DATA_CACHE_KEY); | ||||
SettingsFragment.refreshParent = true; | ||||
SnackBar.success( | ||||
requireContext(), | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
} | ||||
} | ||||
}); | ||||
| ||||
binding.cacheSizeImagesChipGroup.setOnCheckedStateChangeListener( | ||||
(group, checkedIds) -> { | ||||
if (checkedIds.size() == 1) { | ||||
int newSelection = getCacheSizeImagesChipPosition(checkedIds.get(0)); | ||||
if (newSelection != cacheSizeImagesSelectedChoice) { | ||||
cacheSizeImagesSelectedChoice = newSelection; | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
cacheSizeImagesList[newSelection], | ||||
AppDatabaseSettings.APP_IMAGES_CACHE_SIZE_KEY); | ||||
AppDatabaseSettings.updateSettingsValue( | ||||
requireContext(), | ||||
String.valueOf(newSelection), | ||||
AppDatabaseSettings.APP_IMAGES_CACHE_KEY); | ||||
SettingsFragment.refreshParent = true; | ||||
SnackBar.success( | ||||
requireContext(), | ||||
requireActivity().findViewById(android.R.id.content), | ||||
getString(R.string.settingsSave)); | ||||
} | ||||
} | ||||
}); | ||||
| ||||
binding.clearCacheButton.setOnClickListener( | ||||
v -> { | ||||
MaterialAlertDialogBuilder dialog = | ||||
new MaterialAlertDialogBuilder(requireContext()) | ||||
.setTitle(R.string.clearCacheDialogHeader) | ||||
.setMessage(getString(R.string.clearCacheDialogMessage)) | ||||
.setNeutralButton( | ||||
R.string.cancelButton, (d, which) -> d.dismiss()) | ||||
.setPositiveButton( | ||||
R.string.menuDeleteText, | ||||
(d, which) -> { | ||||
try { | ||||
File cacheDir1 = requireContext().getCacheDir(); | ||||
FileUtils.deleteDirectory(cacheDir1); | ||||
FileUtils.forceMkdir(cacheDir1); | ||||
requireActivity().recreate(); | ||||
requireActivity() | ||||
.overridePendingTransition(0, 0); | ||||
} catch (IOException ignored) { | ||||
} | ||||
}); | ||||
dialog.show(); | ||||
}); | ||||
| ||||
binding.deleteCertsButton.setOnClickListener( | ||||
v -> { | ||||
MaterialAlertDialogBuilder dialog = | ||||
new MaterialAlertDialogBuilder(requireContext()) | ||||
.setTitle(R.string.settingsCertsPopupTitle) | ||||
.setMessage(getString(R.string.settingsCertsPopupMessage)) | ||||
.setNeutralButton( | ||||
R.string.cancelButton, (d, which) -> d.dismiss()) | ||||
.setPositiveButton( | ||||
R.string.menuDeleteText, | ||||
(d, which) -> { | ||||
requireContext() | ||||
.getSharedPreferences( | ||||
MemorizingTrustManager | ||||
.KEYSTORE_NAME, | ||||
Context.MODE_PRIVATE) | ||||
.edit() | ||||
.remove(MemorizingTrustManager.KEYSTORE_KEY) | ||||
.apply(); | ||||
AppUtil.logout(requireContext()); | ||||
}); | ||||
dialog.show(); | ||||
}); | ||||
| ||||
return binding.getRoot(); | ||||
} | ||||
| ||||
private void setCacheSizeDataChipSelection(int position) { | ||||
switch (position) { | ||||
case 0: | ||||
binding.chipDataCache0.setChecked(true); | ||||
break; | ||||
case 1: | ||||
binding.chipDataCache1.setChecked(true); | ||||
break; | ||||
case 2: | ||||
binding.chipDataCache2.setChecked(true); | ||||
break; | ||||
case 3: | ||||
binding.chipDataCache3.setChecked(true); | ||||
break; | ||||
} | ||||
} | ||||
| ||||
private int getCacheSizeDataChipPosition(int checkedId) { | ||||
if (checkedId == R.id.chipDataCache0) return 0; | ||||
if (checkedId == R.id.chipDataCache1) return 1; | ||||
if (checkedId == R.id.chipDataCache2) return 2; | ||||
if (checkedId == R.id.chipDataCache3) return 3; | ||||
return cacheSizeDataSelectedChoice; | ||||
} | ||||
| ||||
private void setCacheSizeImagesChipSelection(int position) { | ||||
switch (position) { | ||||
case 0: | ||||
binding.chipImagesCache0.setChecked(true); | ||||
break; | ||||
case 1: | ||||
binding.chipImagesCache1.setChecked(true); | ||||
break; | ||||
case 2: | ||||
binding.chipImagesCache2.setChecked(true); | ||||
break; | ||||
case 3: | ||||
binding.chipImagesCache3.setChecked(true); | ||||
break; | ||||
} | ||||
} | ||||
| ||||
private int getCacheSizeImagesChipPosition(int checkedId) { | ||||
if (checkedId == R.id.chipImagesCache0) return 0; | ||||
if (checkedId == R.id.chipImagesCache1) return 1; | ||||
if (checkedId == R.id.chipImagesCache2) return 2; | ||||
if (checkedId == R.id.chipImagesCache3) return 3; | ||||
return cacheSizeImagesSelectedChoice; | ||||
} | ||||
| ||||
@Override | ||||
public void onDestroyView() { | ||||
super.onDestroyView(); | ||||
binding = null; | ||||
} | ||||
} |
| @ -15,7 +15,6 @@ import androidx.viewpager2.widget.ViewPager2; | |||
import com.google.android.material.tabs.TabLayout; | ||||
import com.google.android.material.tabs.TabLayoutMediator; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.activities.MainActivity; | ||||
import org.mian.gitnex.helpers.AppDatabaseSettings; | ||||
import org.mian.gitnex.helpers.AppUtil; | ||||
import org.mian.gitnex.helpers.ViewPager2Transformers; | ||||
| @ -35,9 +34,6 @@ public class ExploreFragment extends Fragment { | |||
| ||||
Context ctx = getContext(); | ||||
| ||||
((MainActivity) requireActivity()) | ||||
.setActionBarTitle(getResources().getString(R.string.pageTitleExplore)); | ||||
| ||||
ViewPager2 viewPager = view.findViewById(R.id.containerExplore); | ||||
viewPager.setOffscreenPageLimit(1); | ||||
TabLayout tabLayout = view.findViewById(R.id.tabsExplore); | ||||
| |
| @ -16,7 +16,7 @@ import androidx.core.view.MenuProvider; | |||
import androidx.fragment.app.Fragment; | ||||
import androidx.lifecycle.Lifecycle; | ||||
import androidx.recyclerview.widget.LinearLayoutManager; | ||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder; | ||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment; | ||||
import java.util.ArrayList; | ||||
import java.util.List; | ||||
import org.gitnex.tea4j.v2.models.Repository; | ||||
| @ -25,7 +25,7 @@ import org.mian.gitnex.R; | |||
import org.mian.gitnex.activities.MainActivity; | ||||
import org.mian.gitnex.adapters.ExploreRepositoriesAdapter; | ||||
import org.mian.gitnex.clients.RetrofitClient; | ||||
import org.mian.gitnex.databinding.CustomExploreRepositoriesDialogBinding; | ||||
import org.mian.gitnex.databinding.BottomSheetExploreFiltersBinding; | ||||
import org.mian.gitnex.databinding.FragmentExploreRepoBinding; | ||||
import org.mian.gitnex.helpers.Constants; | ||||
import org.mian.gitnex.helpers.SnackBar; | ||||
| @ -35,7 +35,7 @@ import retrofit2.Callback; | |||
import retrofit2.Response; | ||||
| ||||
/** | ||||
* @author M M Arif | ||||
* @author mmarif | ||||
*/ | ||||
public class ExploreRepositoriesFragment extends Fragment { | ||||
| ||||
| @ -48,9 +48,6 @@ public class ExploreRepositoriesFragment extends Fragment { | |||
private int resultLimit; | ||||
private List<Repository> dataList; | ||||
private ExploreRepositoriesAdapter adapter; | ||||
| ||||
private CustomExploreRepositoriesDialogBinding filterBinding; | ||||
| ||||
private boolean includeTopic = false; | ||||
private boolean includeDescription = false; | ||||
private boolean includeTemplate = false; | ||||
| @ -75,16 +72,15 @@ public class ExploreRepositoriesFragment extends Fragment { | |||
@Override | ||||
public void onCreateMenu( | ||||
@NonNull Menu menu, @NonNull MenuInflater menuInflater) { | ||||
| ||||
menu.clear(); | ||||
menuInflater.inflate(R.menu.search_menu, menu); | ||||
menuInflater.inflate(R.menu.filter_menu_explore, menu); | ||||
MenuItem filter = menu.findItem(R.id.filter_explore); | ||||
menuInflater.inflate(R.menu.generic_nav_dotted_menu, menu); | ||||
| ||||
filter.setOnMenuItemClickListener( | ||||
filter_ -> { | ||||
showFilterOptions(); | ||||
return false; | ||||
MenuItem filterItem = menu.findItem(R.id.genericMenu); | ||||
filterItem.setOnMenuItemClickListener( | ||||
item -> { | ||||
showFilterBottomSheet(); | ||||
return true; | ||||
}); | ||||
| ||||
MenuItem searchItem = menu.findItem(R.id.action_search); | ||||
| @ -97,7 +93,6 @@ public class ExploreRepositoriesFragment extends Fragment { | |||
searchView.setOnQueryTextListener( | ||||
new androidx.appcompat.widget.SearchView | ||||
.OnQueryTextListener() { | ||||
| ||||
@Override | ||||
public boolean onQueryTextSubmit(String query) { | ||||
viewBinding.progressBar.setVisibility(View.VISIBLE); | ||||
| @ -126,7 +121,7 @@ public class ExploreRepositoriesFragment extends Fragment { | |||
searchQuery = query; | ||||
searchView.setQuery(null, false); | ||||
searchItem.collapseActionView(); | ||||
return false; | ||||
return true; | ||||
} | ||||
| ||||
@Override | ||||
| @ -175,7 +170,6 @@ public class ExploreRepositoriesFragment extends Fragment { | |||
} | ||||
| ||||
private void loadInitial(String searchKeyword, int resultLimit) { | ||||
| ||||
Call<SearchResults> call = | ||||
RetrofitClient.getApiInterface(context) | ||||
.repoSearch( | ||||
| @ -218,28 +212,19 @@ public class ExploreRepositoriesFragment extends Fragment { | |||
viewBinding.noData.setVisibility(View.VISIBLE); | ||||
viewBinding.progressBar.setVisibility(View.GONE); | ||||
} else { | ||||
Toasty.error( | ||||
requireActivity(), | ||||
requireActivity() | ||||
.getResources() | ||||
.getString(R.string.genericError)); | ||||
Toasty.error(requireActivity(), getString(R.string.genericError)); | ||||
} | ||||
} | ||||
| ||||
@Override | ||||
public void onFailure(@NonNull Call<SearchResults> call, @NonNull Throwable t) { | ||||
| ||||
Toasty.error( | ||||
requireActivity(), | ||||
requireActivity() | ||||
.getResources() | ||||
.getString(R.string.genericServerResponseError)); | ||||
requireActivity(), getString(R.string.genericServerResponseError)); | ||||
} | ||||
}); | ||||
} | ||||
| ||||
private void loadMore(String searchKeyword, int resultLimit, int page) { | ||||
| ||||
viewBinding.progressBar.setVisibility(View.VISIBLE); | ||||
Call<SearchResults> call = | ||||
RetrofitClient.getApiInterface(context) | ||||
| @ -284,67 +269,111 @@ public class ExploreRepositoriesFragment extends Fragment { | |||
adapter.notifyDataChanged(); | ||||
viewBinding.progressBar.setVisibility(View.GONE); | ||||
} else { | ||||
Toasty.error( | ||||
requireActivity(), | ||||
requireActivity() | ||||
.getResources() | ||||
.getString(R.string.genericError)); | ||||
Toasty.error(requireActivity(), getString(R.string.genericError)); | ||||
} | ||||
} | ||||
| ||||
@Override | ||||
public void onFailure(@NonNull Call<SearchResults> call, @NonNull Throwable t) { | ||||
| ||||
Toasty.error( | ||||
requireActivity(), | ||||
requireActivity() | ||||
.getResources() | ||||
.getString(R.string.genericServerResponseError)); | ||||
requireActivity(), getString(R.string.genericServerResponseError)); | ||||
} | ||||
}); | ||||
} | ||||
| ||||
private void showFilterOptions() { | ||||
| ||||
MaterialAlertDialogBuilder materialAlertDialogBuilder = | ||||
new MaterialAlertDialogBuilder( | ||||
context, R.style.ThemeOverlay_Material3_Dialog_Alert); | ||||
filterBinding = | ||||
CustomExploreRepositoriesDialogBinding.inflate(LayoutInflater.from(context)); | ||||
| ||||
View view = filterBinding.getRoot(); | ||||
materialAlertDialogBuilder.setView(view); | ||||
| ||||
filterBinding.includeTopic.setOnClickListener( | ||||
includeTopic -> this.includeTopic = filterBinding.includeTopic.isChecked()); | ||||
| ||||
filterBinding.includeDesc.setOnClickListener( | ||||
includeDesc -> this.includeDescription = filterBinding.includeDesc.isChecked()); | ||||
| ||||
filterBinding.includeTemplate.setOnClickListener( | ||||
includeTemplate -> | ||||
this.includeTemplate = filterBinding.includeTemplate.isChecked()); | ||||
| ||||
filterBinding.onlyArchived.setOnClickListener( | ||||
onlyArchived -> this.onlyArchived = filterBinding.onlyArchived.isChecked()); | ||||
| ||||
filterBinding.includeTopic.setChecked(includeTopic); | ||||
filterBinding.includeDesc.setChecked(includeDescription); | ||||
filterBinding.includeTemplate.setChecked(includeTemplate); | ||||
filterBinding.onlyArchived.setChecked(onlyArchived); | ||||
| ||||
materialAlertDialogBuilder.setNeutralButton(getString(R.string.close), null); | ||||
materialAlertDialogBuilder.show(); | ||||
private void showFilterBottomSheet() { | ||||
BottomSheetFilterFragment bottomSheet = | ||||
BottomSheetFilterFragment.newInstance( | ||||
includeTopic, | ||||
includeDescription, | ||||
includeTemplate, | ||||
onlyArchived, | ||||
(topic, desc, template, archived) -> { | ||||
includeTopic = topic; | ||||
includeDescription = desc; | ||||
includeTemplate = template; | ||||
onlyArchived = archived; | ||||
loadInitial(searchQuery, resultLimit); | ||||
}); | ||||
bottomSheet.show(getChildFragmentManager(), "exploreFiltersBottomSheet"); | ||||
} | ||||
| ||||
@Override | ||||
public void onResume() { | ||||
super.onResume(); | ||||
| ||||
if (MainActivity.reloadRepos) { | ||||
dataList.clear(); | ||||
loadInitial(searchQuery, resultLimit); | ||||
MainActivity.reloadRepos = false; | ||||
} | ||||
} | ||||
| ||||
public static class BottomSheetFilterFragment extends BottomSheetDialogFragment { | ||||
| ||||
private static final String ARG_INCLUDE_TOPIC = "includeTopic"; | ||||
private static final String ARG_INCLUDE_DESC = "includeDescription"; | ||||
private static final String ARG_INCLUDE_TEMPLATE = "includeTemplate"; | ||||
private static final String ARG_ONLY_ARCHIVED = "onlyArchived"; | ||||
private FilterCallback callback; | ||||
| ||||
public static BottomSheetFilterFragment newInstance( | ||||
boolean includeTopic, | ||||
boolean includeDescription, | ||||
boolean includeTemplate, | ||||
boolean onlyArchived, | ||||
FilterCallback callback) { | ||||
BottomSheetFilterFragment fragment = new BottomSheetFilterFragment(); | ||||
Bundle args = new Bundle(); | ||||
args.putBoolean(ARG_INCLUDE_TOPIC, includeTopic); | ||||
args.putBoolean(ARG_INCLUDE_DESC, includeDescription); | ||||
args.putBoolean(ARG_INCLUDE_TEMPLATE, includeTemplate); | ||||
args.putBoolean(ARG_ONLY_ARCHIVED, onlyArchived); | ||||
fragment.setArguments(args); | ||||
fragment.callback = callback; | ||||
return fragment; | ||||
} | ||||
| ||||
@Override | ||||
public View onCreateView( | ||||
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { | ||||
BottomSheetExploreFiltersBinding binding = | ||||
BottomSheetExploreFiltersBinding.inflate(inflater, container, false); | ||||
| ||||
Bundle args = getArguments(); | ||||
boolean includeTopic = args != null && args.getBoolean(ARG_INCLUDE_TOPIC, false); | ||||
boolean includeDescription = args != null && args.getBoolean(ARG_INCLUDE_DESC, false); | ||||
boolean includeTemplate = args != null && args.getBoolean(ARG_INCLUDE_TEMPLATE, false); | ||||
boolean onlyArchived = args != null && args.getBoolean(ARG_ONLY_ARCHIVED, false); | ||||
| ||||
binding.includeTopicChip.setChecked(includeTopic); | ||||
binding.includeDescChip.setChecked(includeDescription); | ||||
binding.includeTemplateChip.setChecked(includeTemplate); | ||||
binding.onlyArchivedChip.setChecked(onlyArchived); | ||||
| ||||
binding.filterChipGroup.setOnCheckedStateChangeListener( | ||||
(group, checkedIds) -> { | ||||
boolean newIncludeTopic = checkedIds.contains(R.id.includeTopicChip); | ||||
boolean newIncludeDescription = checkedIds.contains(R.id.includeDescChip); | ||||
boolean newIncludeTemplate = checkedIds.contains(R.id.includeTemplateChip); | ||||
boolean newOnlyArchived = checkedIds.contains(R.id.onlyArchivedChip); | ||||
if (callback != null) { | ||||
callback.onFiltersApplied( | ||||
newIncludeTopic, | ||||
newIncludeDescription, | ||||
newIncludeTemplate, | ||||
newOnlyArchived); | ||||
} | ||||
}); | ||||
| ||||
return binding.getRoot(); | ||||
} | ||||
| ||||
public interface FilterCallback { | ||||
void onFiltersApplied( | ||||
boolean includeTopic, | ||||
boolean includeDescription, | ||||
boolean includeTemplate, | ||||
boolean onlyArchived); | ||||
} | ||||
} | ||||
} | ||||
| |
| @ -0,0 +1,134 @@ | |||
package org.mian.gitnex.fragments; | ||||
| ||||
import android.annotation.SuppressLint; | ||||
import android.content.Intent; | ||||
import android.os.Bundle; | ||||
import android.view.LayoutInflater; | ||||
import android.view.View; | ||||
import android.view.ViewGroup; | ||||
import androidx.annotation.NonNull; | ||||
import androidx.fragment.app.Fragment; | ||||
import androidx.navigation.NavController; | ||||
import androidx.navigation.Navigation; | ||||
import androidx.recyclerview.widget.LinearLayoutManager; | ||||
import java.util.ArrayList; | ||||
import java.util.List; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.activities.MainActivity; | ||||
import org.mian.gitnex.activities.ProfileActivity; | ||||
import org.mian.gitnex.adapters.HomeDashboardAdapter; | ||||
import org.mian.gitnex.adapters.UserAccountsNavAdapter; | ||||
import org.mian.gitnex.database.models.UserAccount; | ||||
import org.mian.gitnex.databinding.FragmentHomeDashboardBinding; | ||||
| ||||
/** | ||||
* @author mmarif | ||||
*/ | ||||
public class HomeDashboardFragment extends Fragment { | ||||
| ||||
private FragmentHomeDashboardBinding binding; | ||||
private String username; | ||||
private List<UserAccount> userAccountsList; | ||||
private UserAccountsNavAdapter accountsAdapter; | ||||
private HomeDashboardAdapter dashboardAdapter; | ||||
| ||||
@SuppressLint("NotifyDataSetChanged") | ||||
@Override | ||||
public View onCreateView( | ||||
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { | ||||
| ||||
binding = FragmentHomeDashboardBinding.inflate(inflater, container, false); | ||||
userAccountsList = new ArrayList<>(); | ||||
accountsAdapter = new UserAccountsNavAdapter(requireContext(), userAccountsList); | ||||
dashboardAdapter = new HomeDashboardAdapter(requireContext()); | ||||
| ||||
binding.mainScreensRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext())); | ||||
binding.mainScreensRecyclerView.setAdapter(dashboardAdapter); | ||||
| ||||
binding.userAccountsRecyclerView.setLayoutManager( | ||||
new LinearLayoutManager(requireContext())); | ||||
binding.userAccountsRecyclerView.setAdapter(accountsAdapter); | ||||
| ||||
NavController navController = | ||||
Navigation.findNavController(requireActivity(), R.id.nav_host_fragment); | ||||
binding.repoOrgCard.setOnClickListener( | ||||
v -> navController.navigate(R.id.action_to_organizations)); | ||||
binding.repoMyReposCard.setOnClickListener( | ||||
v -> navController.navigate(R.id.action_to_myRepositories)); | ||||
binding.repoStarredCard.setOnClickListener( | ||||
v -> navController.navigate(R.id.action_to_starredRepositories)); | ||||
binding.repoWatchedCard.setOnClickListener( | ||||
v -> navController.navigate(R.id.action_to_watchedRepositories)); | ||||
binding.repoActivitiesCard.setOnClickListener( | ||||
v -> navController.navigate(R.id.activitiesFragment)); | ||||
binding.repoMyIssuesCard.setOnClickListener( | ||||
v -> navController.navigate(R.id.action_to_myIssues)); | ||||
| ||||
// Load user info from MainActivity | ||||
if (requireActivity() instanceof MainActivity) { | ||||
((MainActivity) requireActivity()) | ||||
.loadUserInfo( | ||||
this, | ||||
binding, | ||||
dashboardAdapter, | ||||
userAccountsList, | ||||
accountsAdapter, | ||||
new MainActivity.UserInfoCallback() { | ||||
@Override | ||||
public void onUserInfoLoaded( | ||||
String username, boolean isAdmin, String serverVersion) { | ||||
HomeDashboardFragment.this.username = username; | ||||
} | ||||
| ||||
@Override | ||||
public void onUserAccountsLoaded() {} | ||||
}); | ||||
} | ||||
| ||||
binding.userAvatar.setOnClickListener( | ||||
v -> { | ||||
if (username != null) { | ||||
Intent intentProfile = new Intent(requireContext(), ProfileActivity.class); | ||||
intentProfile.putExtra("username", username); | ||||
startActivity(intentProfile); | ||||
} | ||||
}); | ||||
| ||||
binding.refreshButton.setOnClickListener( | ||||
v -> { | ||||
binding.userAvatar.setImageResource(R.drawable.loader_animated); | ||||
binding.userFullname.setText(""); | ||||
binding.userEmail.setText(""); | ||||
userAccountsList.clear(); | ||||
accountsAdapter.notifyDataSetChanged(); | ||||
| ||||
// Call MainActivity methods | ||||
if (requireActivity() instanceof MainActivity mainActivity) { | ||||
mainActivity.getNotificationsCount(); | ||||
mainActivity.giteaVersion(); | ||||
mainActivity.serverPageLimitSettings(); | ||||
mainActivity.updateGeneralAttachmentSettings(); | ||||
mainActivity.loadUserInfo( | ||||
this, | ||||
binding, | ||||
dashboardAdapter, | ||||
userAccountsList, | ||||
accountsAdapter, | ||||
new MainActivity.UserInfoCallback() { | ||||
@Override | ||||
public void onUserInfoLoaded( | ||||
String username, | ||||
boolean isAdmin, | ||||
String serverVersion) { | ||||
HomeDashboardFragment.this.username = username; | ||||
} | ||||
| ||||
@Override | ||||
public void onUserAccountsLoaded() {} | ||||
}); | ||||
} | ||||
}); | ||||
| ||||
return binding.getRoot(); | ||||
} | ||||
} |
| @ -22,7 +22,6 @@ import java.util.ArrayList; | |||
import java.util.List; | ||||
import java.util.Objects; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.activities.MainActivity; | ||||
import org.mian.gitnex.adapters.MostVisitedReposAdapter; | ||||
import org.mian.gitnex.database.api.BaseApi; | ||||
import org.mian.gitnex.database.api.RepositoriesApi; | ||||
| @ -51,9 +50,6 @@ public class MostVisitedReposFragment extends Fragment { | |||
| ||||
ctx = getContext(); | ||||
| ||||
((MainActivity) requireActivity()) | ||||
.setActionBarTitle(getResources().getString(R.string.navMostVisited)); | ||||
| ||||
TinyDB tinyDb = TinyDB.getInstance(ctx); | ||||
| ||||
mostVisitedReposList = new ArrayList<>(); | ||||
| |
| @ -17,21 +17,23 @@ import androidx.fragment.app.Fragment; | |||
import androidx.lifecycle.Lifecycle; | ||||
import androidx.lifecycle.ViewModelProvider; | ||||
import androidx.recyclerview.widget.LinearLayoutManager; | ||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.activities.MainActivity; | ||||
import org.mian.gitnex.adapters.ExploreIssuesAdapter; | ||||
import org.mian.gitnex.databinding.BottomSheetMyIssuesFilterBinding; | ||||
import org.mian.gitnex.databinding.FragmentIssuesBinding; | ||||
import org.mian.gitnex.helpers.TinyDB; | ||||
import org.mian.gitnex.viewmodels.IssuesViewModel; | ||||
| ||||
/** | ||||
* @author M M Arif | ||||
* @author mmarif | ||||
*/ | ||||
public class MyIssuesFragment extends Fragment { | ||||
| ||||
private FragmentIssuesBinding fragmentIssuesBinding; | ||||
private FragmentIssuesBinding binding; | ||||
private IssuesViewModel issuesViewModel; | ||||
private ExploreIssuesAdapter adapter; | ||||
private Menu menu; | ||||
private TinyDB tinyDB; | ||||
private String state = "open"; | ||||
private boolean assignedToMe = false; | ||||
private boolean createdByMe = true; | ||||
| @ -41,39 +43,34 @@ public class MyIssuesFragment extends Fragment { | |||
public View onCreateView( | ||||
@NonNull LayoutInflater inflater, | ||||
@Nullable ViewGroup container, | ||||
@Nullable Bundle savedInstanceState) { | ||||
| ||||
fragmentIssuesBinding = FragmentIssuesBinding.inflate(inflater, container, false); | ||||
Bundle savedInstanceState) { | ||||
binding = FragmentIssuesBinding.inflate(inflater, container, false); | ||||
tinyDB = TinyDB.getInstance(requireContext()); | ||||
issuesViewModel = new ViewModelProvider(this).get(IssuesViewModel.class); | ||||
| ||||
fragmentIssuesBinding.recyclerView.setHasFixedSize(true); | ||||
fragmentIssuesBinding.recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); | ||||
fragmentIssuesBinding.createNewIssue.setVisibility(View.GONE); | ||||
String savedFilter = tinyDB.getString("myIssuesFilter", "open_created_by_me"); | ||||
updateFilterState(savedFilter); | ||||
| ||||
binding.recyclerView.setHasFixedSize(true); | ||||
binding.recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); | ||||
binding.createNewIssue.setVisibility(View.GONE); | ||||
| ||||
setupMenu(); | ||||
| ||||
((MainActivity) requireActivity()) | ||||
.setFragmentRefreshListenerMyIssues( | ||||
myIssues -> { | ||||
updateFilterState(myIssues); | ||||
fetchDataAsync(null); | ||||
}); | ||||
| ||||
fragmentIssuesBinding.pullToRefresh.setOnRefreshListener( | ||||
binding.pullToRefresh.setOnRefreshListener( | ||||
() -> | ||||
new Handler(Looper.getMainLooper()) | ||||
.postDelayed( | ||||
() -> { | ||||
page = 1; | ||||
fragmentIssuesBinding.pullToRefresh.setRefreshing( | ||||
false); | ||||
binding.pullToRefresh.setRefreshing(false); | ||||
fetchDataAsync(null); | ||||
}, | ||||
50)); | ||||
| ||||
fetchDataAsync(null); | ||||
| ||||
return fragmentIssuesBinding.getRoot(); | ||||
return binding.getRoot(); | ||||
} | ||||
| ||||
private void setupMenu() { | ||||
| @ -82,13 +79,11 @@ public class MyIssuesFragment extends Fragment { | |||
new MenuProvider() { | ||||
@Override | ||||
public void onCreateMenu( | ||||
@NonNull Menu menu1, @NonNull MenuInflater menuInflater) { | ||||
@NonNull Menu menu, @NonNull MenuInflater menuInflater) { | ||||
menuInflater.inflate(R.menu.search_menu, menu); | ||||
menuInflater.inflate(R.menu.generic_nav_dotted_menu, menu); | ||||
| ||||
menu = menu1; | ||||
menuInflater.inflate(R.menu.search_menu, menu1); | ||||
menuInflater.inflate(R.menu.filter_menu, menu1); | ||||
| ||||
MenuItem searchItem = menu1.findItem(R.id.action_search); | ||||
MenuItem searchItem = menu.findItem(R.id.action_search); | ||||
androidx.appcompat.widget.SearchView searchView = | ||||
(androidx.appcompat.widget.SearchView) | ||||
searchItem.getActionView(); | ||||
| @ -98,7 +93,6 @@ public class MyIssuesFragment extends Fragment { | |||
searchView.setOnQueryTextListener( | ||||
new androidx.appcompat.widget.SearchView | ||||
.OnQueryTextListener() { | ||||
| ||||
@Override | ||||
public boolean onQueryTextSubmit(String query) { | ||||
fetchDataAsync(query); | ||||
| @ -116,20 +110,9 @@ public class MyIssuesFragment extends Fragment { | |||
| ||||
@Override | ||||
public boolean onMenuItemSelected(@NonNull MenuItem menuItem) { | ||||
| ||||
if (menuItem.getItemId() == R.id.filter) { | ||||
| ||||
String currentFilter = | ||||
state | ||||
+ "_" | ||||
+ (assignedToMe | ||||
? "assignedToMe" | ||||
: "created_by_me"); | ||||
BottomSheetMyIssuesFilterFragment bottomSheet = | ||||
BottomSheetMyIssuesFilterFragment.newInstance( | ||||
currentFilter); | ||||
bottomSheet.show(getParentFragmentManager(), "myIssuesFilter"); | ||||
| ||||
if (menuItem.getItemId() == R.id.genericMenu) { | ||||
new FilterBottomSheetDialogFragment() | ||||
.show(getChildFragmentManager(), "MyIssuesFilter"); | ||||
return true; | ||||
} | ||||
return false; | ||||
| @ -140,27 +123,15 @@ public class MyIssuesFragment extends Fragment { | |||
} | ||||
| ||||
public void updateFilterState(String filter) { | ||||
| ||||
String[] parts = filter.split("_", 2); | ||||
String stateValue = parts[0]; | ||||
String filterValue = parts[1]; | ||||
state = parts[0]; | ||||
String filterValue = parts.length > 1 ? parts[1] : "created_by_me"; | ||||
| ||||
state = stateValue; | ||||
menu.getItem(1) | ||||
.setIcon( | ||||
state.equals("closed") | ||||
? R.drawable.ic_filter_closed | ||||
: R.drawable.ic_filter); | ||||
createdByMe = filterValue.equals("created_by_me"); | ||||
assignedToMe = filterValue.equals("assignedToMe"); | ||||
| ||||
if (filterValue.equals("created_by_me")) { | ||||
createdByMe = true; | ||||
assignedToMe = false; | ||||
} else if (filterValue.equals("assignedToMe")) { | ||||
createdByMe = false; | ||||
assignedToMe = true; | ||||
} | ||||
| ||||
fetchDataAsync(null); | ||||
tinyDB.putString("myIssuesFilter", filter); | ||||
page = 1; | ||||
} | ||||
| ||||
public String getCurrentFilter() { | ||||
| @ -168,9 +139,8 @@ public class MyIssuesFragment extends Fragment { | |||
} | ||||
| ||||
private void fetchDataAsync(String query) { | ||||
| ||||
fragmentIssuesBinding.progressBar.setVisibility(View.VISIBLE); | ||||
fragmentIssuesBinding.noDataIssues.setVisibility(View.GONE); | ||||
binding.progressBar.setVisibility(View.VISIBLE); | ||||
binding.noDataIssues.setVisibility(View.GONE); | ||||
| ||||
issuesViewModel | ||||
.getIssuesList(query, "issues", createdByMe, state, assignedToMe, getContext()) | ||||
| @ -192,27 +162,81 @@ public class MyIssuesFragment extends Fragment { | |||
assignedToMe, | ||||
getContext(), | ||||
adapter); | ||||
fragmentIssuesBinding.progressBar.setVisibility( | ||||
View.VISIBLE); | ||||
binding.progressBar.setVisibility(View.VISIBLE); | ||||
} | ||||
| ||||
@Override | ||||
public void onLoadFinished() { | ||||
fragmentIssuesBinding.progressBar.setVisibility( | ||||
View.GONE); | ||||
binding.progressBar.setVisibility(View.GONE); | ||||
} | ||||
}); | ||||
| ||||
if (adapter.getItemCount() > 0) { | ||||
fragmentIssuesBinding.recyclerView.setAdapter(adapter); | ||||
fragmentIssuesBinding.noDataIssues.setVisibility(View.GONE); | ||||
binding.recyclerView.setAdapter(adapter); | ||||
binding.noDataIssues.setVisibility(View.GONE); | ||||
} else { | ||||
adapter.notifyDataChanged(); | ||||
fragmentIssuesBinding.recyclerView.setAdapter(adapter); | ||||
fragmentIssuesBinding.noDataIssues.setVisibility(View.VISIBLE); | ||||
binding.recyclerView.setAdapter(adapter); | ||||
binding.noDataIssues.setVisibility(View.VISIBLE); | ||||
} | ||||
| ||||
fragmentIssuesBinding.progressBar.setVisibility(View.GONE); | ||||
binding.progressBar.setVisibility(View.GONE); | ||||
}); | ||||
} | ||||
| ||||
public static class FilterBottomSheetDialogFragment extends BottomSheetDialogFragment { | ||||
| ||||
private String selectedState = "open"; | ||||
private String selectedFilter = "created_by_me"; | ||||
| ||||
@Override | ||||
public View onCreateView( | ||||
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { | ||||
BottomSheetMyIssuesFilterBinding binding = | ||||
BottomSheetMyIssuesFilterBinding.inflate(inflater, container, false); | ||||
| ||||
// Initialize based on parent fragment's current filter | ||||
MyIssuesFragment parent = (MyIssuesFragment) requireParentFragment(); | ||||
String currentFilter = parent.getCurrentFilter(); | ||||
String[] parts = currentFilter.split("_"); | ||||
selectedState = parts[0]; | ||||
selectedFilter = parts.length > 1 ? parts[1] : "created_by_me"; | ||||
| ||||
binding.chipOpen.setChecked(selectedState.equals("open")); | ||||
binding.chipClosed.setChecked(selectedState.equals("closed")); | ||||
binding.chipCreatedByMe.setChecked(selectedFilter.equals("created_by_me")); | ||||
binding.chipAssignedToMe.setChecked(selectedFilter.equals("assignedToMe")); | ||||
| ||||
binding.stateChipGroup.setOnCheckedStateChangeListener( | ||||
(group, checkedIds) -> { | ||||
if (!checkedIds.isEmpty()) { | ||||
int checkedId = checkedIds.get(0); | ||||
selectedState = | ||||
checkedId == binding.chipOpen.getId() ? "open" : "closed"; | ||||
applyFilter(parent); | ||||
} | ||||
}); | ||||
| ||||
binding.filterChipGroup.setOnCheckedStateChangeListener( | ||||
(group, checkedIds) -> { | ||||
if (!checkedIds.isEmpty()) { | ||||
int checkedId = checkedIds.get(0); | ||||
selectedFilter = | ||||
checkedId == binding.chipCreatedByMe.getId() | ||||
? "created_by_me" | ||||
: "assignedToMe"; | ||||
applyFilter(parent); | ||||
} | ||||
}); | ||||
| ||||
return binding.getRoot(); | ||||
} | ||||
| ||||
private void applyFilter(MyIssuesFragment parent) { | ||||
String result = selectedState + "_" + selectedFilter; | ||||
parent.updateFilterState(result); | ||||
parent.fetchDataAsync(null); | ||||
dismiss(); | ||||
} | ||||
} | ||||
} | ||||
| |
| @ -48,8 +48,6 @@ public class MyRepositoriesFragment extends Fragment { | |||
fragmentRepositoriesBinding = | ||||
FragmentRepositoriesBinding.inflate(inflater, container, false); | ||||
| ||||
((MainActivity) requireActivity()) | ||||
.setActionBarTitle(getResources().getString(R.string.navMyRepos)); | ||||
repositoriesViewModel = new ViewModelProvider(this).get(RepositoriesViewModel.class); | ||||
| ||||
final String userLogin = | ||||
| |
| @ -23,7 +23,6 @@ import java.util.ArrayList; | |||
import java.util.List; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.activities.CreateNoteActivity; | ||||
import org.mian.gitnex.activities.MainActivity; | ||||
import org.mian.gitnex.adapters.NotesAdapter; | ||||
import org.mian.gitnex.database.api.BaseApi; | ||||
import org.mian.gitnex.database.api.NotesApi; | ||||
| @ -113,9 +112,6 @@ public class NotesFragment extends Fragment { | |||
getViewLifecycleOwner(), | ||||
Lifecycle.State.RESUMED); | ||||
| ||||
((MainActivity) requireActivity()) | ||||
.setActionBarTitle(getResources().getString(R.string.navNotes)); | ||||
| ||||
noteIntent = new Intent(ctx, CreateNoteActivity.class); | ||||
| ||||
binding.newNote.setOnClickListener( | ||||
| |
| @ -5,16 +5,11 @@ import android.content.Context; | |||
import android.content.Intent; | ||||
import android.os.Bundle; | ||||
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 androidx.annotation.NonNull; | ||||
import androidx.annotation.Nullable; | ||||
import androidx.core.view.MenuProvider; | ||||
import androidx.fragment.app.Fragment; | ||||
import androidx.lifecycle.Lifecycle; | ||||
import androidx.recyclerview.widget.LinearLayoutManager; | ||||
import androidx.recyclerview.widget.RecyclerView; | ||||
import java.util.ArrayList; | ||||
| @ -53,13 +48,17 @@ public class NotificationsFragment extends Fragment | |||
private int pageResultLimit; | ||||
private String currentFilterMode = "unread"; | ||||
public static String emptyErrorResponse; | ||||
private NotificationCountListener notificationCountListener; | ||||
| ||||
@Override | ||||
public void onCreate(@Nullable Bundle savedInstanceState) { | ||||
| ||||
super.onCreate(savedInstanceState); | ||||
} | ||||
| ||||
public interface NotificationCountListener { | ||||
void onNotificationsMarkedRead(); | ||||
} | ||||
| ||||
@Nullable @Override | ||||
public View onCreateView( | ||||
@NonNull LayoutInflater inflater, | ||||
| @ -81,12 +80,26 @@ public class NotificationsFragment extends Fragment | |||
viewBinding.notifications.setLayoutManager(linearLayoutManager); | ||||
viewBinding.notifications.setAdapter(notificationsAdapter); | ||||
| ||||
viewBinding.filterChipGroup.setOnCheckedStateChangeListener( | ||||
(group, checkedIds) -> { | ||||
if (checkedIds.isEmpty()) return; | ||||
int checkedId = checkedIds.get(0); | ||||
String newFilterMode = checkedId == R.id.unreadChip ? "unread" : "read"; | ||||
if (!newFilterMode.equals(currentFilterMode)) { | ||||
currentFilterMode = newFilterMode; | ||||
pageCurrentIndex = 1; | ||||
loadNotifications(false); | ||||
viewBinding.markAllAsRead.setVisibility( | ||||
currentFilterMode.equals("unread") ? View.VISIBLE : View.GONE); | ||||
} | ||||
}); | ||||
| ||||
viewBinding.unreadChip.setChecked(true); | ||||
| ||||
viewBinding.notifications.addOnScrollListener( | ||||
new RecyclerView.OnScrollListener() { | ||||
| ||||
@Override | ||||
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { | ||||
| ||||
if (!recyclerView.canScrollVertically(1) && dy != 0) { | ||||
pageCurrentIndex++; | ||||
loadNotifications(true); | ||||
| @ -129,13 +142,15 @@ public class NotificationsFragment extends Fragment | |||
.markedNotificationsAsRead)); | ||||
pageCurrentIndex = 1; | ||||
loadNotifications(false); | ||||
if (notificationCountListener != null) { | ||||
notificationCountListener | ||||
.onNotificationsMarkedRead(); | ||||
} | ||||
} else { | ||||
| ||||
if (emptyErrorResponse != null) { | ||||
if (!emptyErrorResponse.isEmpty()) { | ||||
if (emptyErrorResponse.contains( | ||||
"205")) { | ||||
| ||||
SnackBar.success( | ||||
context, | ||||
requireActivity() | ||||
| @ -149,9 +164,13 @@ public class NotificationsFragment extends Fragment | |||
.markedNotificationsAsRead)); | ||||
pageCurrentIndex = 1; | ||||
loadNotifications(false); | ||||
if (notificationCountListener | ||||
!= null) { | ||||
notificationCountListener | ||||
.onNotificationsMarkedRead(); | ||||
} | ||||
} | ||||
} else { | ||||
| ||||
activity.runOnUiThread( | ||||
() -> | ||||
SnackBar.error( | ||||
| @ -180,72 +199,6 @@ public class NotificationsFragment extends Fragment | |||
| ||||
loadNotifications(true); | ||||
| ||||
requireActivity() | ||||
.addMenuProvider( | ||||
new MenuProvider() { | ||||
| ||||
Menu menu; | ||||
| ||||
@Override | ||||
public void onCreateMenu( | ||||
@NonNull Menu menu, @NonNull MenuInflater menuInflater) { | ||||
| ||||
this.menu = menu; | ||||
menuInflater.inflate(R.menu.filter_menu_notifications, menu); | ||||
| ||||
int filterIcon = | ||||
currentFilterMode.equalsIgnoreCase("read") | ||||
? R.drawable.ic_filter_closed | ||||
: R.drawable.ic_filter; | ||||
| ||||
menu.getItem(0).setIcon(filterIcon); | ||||
| ||||
if (currentFilterMode.equalsIgnoreCase("read")) { | ||||
viewBinding.markAllAsRead.setVisibility(View.GONE); | ||||
} else { | ||||
viewBinding.markAllAsRead.setVisibility(View.VISIBLE); | ||||
} | ||||
} | ||||
| ||||
@Override | ||||
public boolean onMenuItemSelected(@NonNull MenuItem menuItem) { | ||||
| ||||
if (menu.getItem(0).getItemId() == R.id.filterNotifications) { | ||||
| ||||
BottomSheetNotificationsFilterFragment | ||||
bottomSheetNotificationsFilterFragment = | ||||
new BottomSheetNotificationsFilterFragment(); | ||||
bottomSheetNotificationsFilterFragment.show( | ||||
getChildFragmentManager(), | ||||
"notificationsFilterBottomSheet"); | ||||
bottomSheetNotificationsFilterFragment.setOnClickListener( | ||||
(text) -> { | ||||
currentFilterMode = text; | ||||
pageCurrentIndex = 1; | ||||
loadNotifications(false); | ||||
| ||||
int filterIcon = | ||||
currentFilterMode.equalsIgnoreCase("read") | ||||
? R.drawable.ic_filter_closed | ||||
: R.drawable.ic_filter; | ||||
| ||||
menu.getItem(0).setIcon(filterIcon); | ||||
| ||||
if (currentFilterMode.equalsIgnoreCase("read")) { | ||||
viewBinding.markAllAsRead.setVisibility( | ||||
View.GONE); | ||||
} else { | ||||
viewBinding.markAllAsRead.setVisibility( | ||||
View.VISIBLE); | ||||
} | ||||
}); | ||||
} | ||||
return false; | ||||
} | ||||
}, | ||||
getViewLifecycleOwner(), | ||||
Lifecycle.State.RESUMED); | ||||
| ||||
return viewBinding.getRoot(); | ||||
} | ||||
| ||||
| @ -320,27 +273,21 @@ public class NotificationsFragment extends Fragment | |||
.enqueue( | ||||
(SimpleCallback<NotificationThread>) | ||||
(call, voidResponse) -> { | ||||
// reload without any checks, because Gitea returns a 205 | ||||
// and Java expects this to be empty | ||||
// but Gitea send a response -> results in a call of | ||||
// onFailure and no response is present | ||||
// if(voidResponse.isPresent() && | ||||
// voidResponse.get().isSuccessful()) { | ||||
pageCurrentIndex = 1; | ||||
loadNotifications(false); | ||||
// } | ||||
if (notificationCountListener != null) { | ||||
notificationCountListener.onNotificationsMarkedRead(); | ||||
} | ||||
}); | ||||
} | ||||
| ||||
if (StringUtils.containsAny( | ||||
notificationThread.getSubject().getType().toLowerCase(), "pull", "issue")) { | ||||
| ||||
RepositoryContext repo = | ||||
new RepositoryContext( | ||||
notificationThread.getRepository().getOwner().getLogin(), | ||||
notificationThread.getRepository().getName(), | ||||
context); // we can't use the repository object here directly because | ||||
// the permissions are missing | ||||
context); | ||||
String issueUrl = notificationThread.getSubject().getUrl(); | ||||
| ||||
repo.saveToDB(context); | ||||
| @ -371,8 +318,19 @@ public class NotificationsFragment extends Fragment | |||
() -> { | ||||
pageCurrentIndex = 1; | ||||
loadNotifications(false); | ||||
if (notificationCountListener != null) { | ||||
notificationCountListener.onNotificationsMarkedRead(); | ||||
} | ||||
}); | ||||
bottomSheetNotificationsFragment.show( | ||||
getChildFragmentManager(), "notificationsBottomSheet"); | ||||
} | ||||
| ||||
@Override | ||||
public void onAttach(@NonNull Context context) { | ||||
super.onAttach(context); | ||||
if (context instanceof NotificationCountListener) { | ||||
notificationCountListener = (NotificationCountListener) context; | ||||
} | ||||
} | ||||
} | ||||
| |
| @ -19,7 +19,6 @@ import androidx.lifecycle.ViewModelProvider; | |||
import androidx.recyclerview.widget.LinearLayoutManager; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.activities.CreateOrganizationActivity; | ||||
import org.mian.gitnex.activities.MainActivity; | ||||
import org.mian.gitnex.adapters.OrganizationsListAdapter; | ||||
import org.mian.gitnex.databinding.FragmentOrganizationsBinding; | ||||
import org.mian.gitnex.helpers.Constants; | ||||
| @ -88,8 +87,6 @@ public class OrganizationsFragment extends Fragment { | |||
getViewLifecycleOwner(), | ||||
Lifecycle.State.RESUMED); | ||||
| ||||
((MainActivity) requireActivity()) | ||||
.setActionBarTitle(getResources().getString(R.string.navOrg)); | ||||
organizationsViewModel = new ViewModelProvider(this).get(OrganizationsViewModel.class); | ||||
| ||||
resultLimit = Constants.getCurrentResultLimit(getContext()); | ||||
| |
| @ -4,7 +4,6 @@ import android.content.Context; | |||
import android.os.Bundle; | ||||
import android.os.Handler; | ||||
import android.os.Looper; | ||||
import android.util.Log; | ||||
import android.view.LayoutInflater; | ||||
import android.view.Menu; | ||||
import android.view.MenuInflater; | ||||
| @ -36,19 +35,17 @@ import retrofit2.Callback; | |||
import retrofit2.Response; | ||||
| ||||
/** | ||||
* @author M M Arif | ||||
* @author mmarif | ||||
*/ | ||||
public class PullRequestsFragment extends Fragment { | ||||
| ||||
public static boolean resumePullRequests = false; | ||||
private final String TAG = "PullRequestFragment"; | ||||
private FragmentPullRequestsBinding fragmentPullRequestsBinding; | ||||
private List<PullRequest> prList; | ||||
private PullRequestsAdapter adapter; | ||||
private Context context; | ||||
private int pageSize = Constants.prPageInit; | ||||
private int resultLimit; | ||||
| ||||
private RepositoryContext repository; | ||||
| ||||
public static PullRequestsFragment newInstance(RepositoryContext repository) { | ||||
| @ -72,6 +69,7 @@ public class PullRequestsFragment extends Fragment { | |||
resultLimit = Constants.getCurrentResultLimit(context); | ||||
prList = new ArrayList<>(); | ||||
repository = RepositoryContext.fromBundle(requireArguments()); | ||||
| ||||
boolean archived = repository.getRepository().isArchived(); | ||||
| ||||
swipeRefresh.setOnRefreshListener( | ||||
| @ -90,7 +88,7 @@ public class PullRequestsFragment extends Fragment { | |||
}, | ||||
200)); | ||||
| ||||
adapter = new PullRequestsAdapter(getContext(), prList); | ||||
adapter = new PullRequestsAdapter(context, prList); | ||||
adapter.setLoadMoreListener( | ||||
() -> | ||||
fragmentPullRequestsBinding.recyclerView.post( | ||||
| @ -114,7 +112,6 @@ public class PullRequestsFragment extends Fragment { | |||
.setFragmentRefreshListenerPr( | ||||
prState -> { | ||||
prList.clear(); | ||||
| ||||
adapter = new PullRequestsAdapter(context, prList); | ||||
adapter.setLoadMoreListener( | ||||
() -> | ||||
| @ -135,10 +132,8 @@ public class PullRequestsFragment extends Fragment { | |||
resultLimit); | ||||
} | ||||
})); | ||||
| ||||
fragmentPullRequestsBinding.progressBar.setVisibility(View.VISIBLE); | ||||
fragmentPullRequestsBinding.noData.setVisibility(View.GONE); | ||||
| ||||
loadInitial( | ||||
repository.getOwner(), | ||||
repository.getName(), | ||||
| @ -160,28 +155,23 @@ public class PullRequestsFragment extends Fragment { | |||
} | ||||
| ||||
if (repository.getRepository().isHasPullRequests() && !archived) { | ||||
| ||||
fragmentPullRequestsBinding.createPullRequest.setVisibility(View.VISIBLE); | ||||
fragmentPullRequestsBinding.createPullRequest.setOnClickListener( | ||||
v12 -> { | ||||
((RepoDetailActivity) requireActivity()) | ||||
.createPrLauncher.launch( | ||||
repository.getIntent( | ||||
getContext(), CreatePullRequestActivity.class)); | ||||
}); | ||||
v -> | ||||
((RepoDetailActivity) requireActivity()) | ||||
.createPrLauncher.launch( | ||||
repository.getIntent( | ||||
context, CreatePullRequestActivity.class))); | ||||
} else { | ||||
| ||||
fragmentPullRequestsBinding.createPullRequest.setVisibility(View.GONE); | ||||
} | ||||
| ||||
requireActivity() | ||||
.addMenuProvider( | ||||
new MenuProvider() { | ||||
| ||||
@Override | ||||
public void onCreateMenu( | ||||
@NonNull Menu menu, @NonNull MenuInflater menuInflater) { | ||||
| ||||
menuInflater.inflate(R.menu.search_menu, menu); | ||||
menuInflater.inflate(R.menu.filter_menu_pr, menu); | ||||
| ||||
| @ -201,7 +191,6 @@ public class PullRequestsFragment extends Fragment { | |||
searchView.setOnQueryTextListener( | ||||
new androidx.appcompat.widget.SearchView | ||||
.OnQueryTextListener() { | ||||
| ||||
@Override | ||||
public boolean onQueryTextSubmit(String query) { | ||||
return false; | ||||
| @ -228,9 +217,7 @@ public class PullRequestsFragment extends Fragment { | |||
| ||||
@Override | ||||
public void onResume() { | ||||
| ||||
super.onResume(); | ||||
| ||||
if (resumePullRequests) { | ||||
loadInitial( | ||||
repository.getOwner(), | ||||
| @ -244,12 +231,12 @@ public class PullRequestsFragment extends Fragment { | |||
| ||||
private void loadInitial( | ||||
String repoOwner, String repoName, int page, String prState, int resultLimit) { | ||||
| ||||
Call<List<PullRequest>> call = | ||||
RetrofitClient.getApiInterface(context) | ||||
.repoListPullRequests( | ||||
repoOwner, | ||||
repoName, | ||||
null, | ||||
prState, | ||||
null, | ||||
null, | ||||
| @ -260,14 +247,11 @@ public class PullRequestsFragment extends Fragment { | |||
| ||||
call.enqueue( | ||||
new Callback<>() { | ||||
| ||||
@Override | ||||
public void onResponse( | ||||
@NonNull Call<List<PullRequest>> call, | ||||
@NonNull Response<List<PullRequest>> response) { | ||||
| ||||
if (response.code() == 200) { | ||||
| ||||
assert response.body() != null; | ||||
if (!response.body().isEmpty()) { | ||||
prList.clear(); | ||||
| @ -283,10 +267,7 @@ public class PullRequestsFragment extends Fragment { | |||
} else if (response.code() == 404) { | ||||
fragmentPullRequestsBinding.noData.setVisibility(View.VISIBLE); | ||||
fragmentPullRequestsBinding.progressBar.setVisibility(View.GONE); | ||||
} else { | ||||
Log.i(TAG, String.valueOf(response.code())); | ||||
} | ||||
Log.i(TAG, String.valueOf(response.code())); | ||||
} | ||||
| ||||
@Override | ||||
| @ -297,14 +278,13 @@ public class PullRequestsFragment extends Fragment { | |||
| ||||
private void loadMore( | ||||
String repoOwner, String repoName, int page, String prState, int resultLimit) { | ||||
| ||||
fragmentPullRequestsBinding.progressBar.setVisibility(View.VISIBLE); | ||||
| ||||
Call<List<PullRequest>> call = | ||||
RetrofitClient.getApiInterface(context) | ||||
.repoListPullRequests( | ||||
repoOwner, | ||||
repoName, | ||||
null, | ||||
prState, | ||||
null, | ||||
null, | ||||
| @ -315,18 +295,13 @@ public class PullRequestsFragment extends Fragment { | |||
| ||||
call.enqueue( | ||||
new Callback<>() { | ||||
| ||||
@Override | ||||
public void onResponse( | ||||
@NonNull Call<List<PullRequest>> call, | ||||
@NonNull Response<List<PullRequest>> response) { | ||||
| ||||
if (response.code() == 200) { | ||||
| ||||
// remove loading view | ||||
prList.remove(prList.size() - 1); | ||||
List<PullRequest> result = response.body(); | ||||
| ||||
assert result != null; | ||||
if (!result.isEmpty()) { | ||||
pageSize = result.size(); | ||||
| @ -337,8 +312,6 @@ public class PullRequestsFragment extends Fragment { | |||
} | ||||
adapter.notifyDataChanged(); | ||||
fragmentPullRequestsBinding.progressBar.setVisibility(View.GONE); | ||||
} else { | ||||
Log.e(TAG, String.valueOf(response.code())); | ||||
} | ||||
} | ||||
| ||||
| @ -349,9 +322,7 @@ public class PullRequestsFragment extends Fragment { | |||
} | ||||
| ||||
private void filter(String text) { | ||||
| ||||
List<PullRequest> arr = new ArrayList<>(); | ||||
| ||||
for (PullRequest d : prList) { | ||||
if (d == null || d.getTitle() == null || d.getBody() == null) { | ||||
continue; | ||||
| |
| @ -105,8 +105,6 @@ public class RepositoriesFragment extends Fragment { | |||
getViewLifecycleOwner(), | ||||
Lifecycle.State.RESUMED); | ||||
| ||||
((MainActivity) requireActivity()) | ||||
.setActionBarTitle(getResources().getString(R.string.navRepos)); | ||||
repositoriesViewModel = new ViewModelProvider(this).get(RepositoriesViewModel.class); | ||||
| ||||
resultLimit = Constants.getCurrentResultLimit(getContext()); | ||||
| |
| @ -11,29 +11,16 @@ import android.view.ViewGroup; | |||
import androidx.annotation.NonNull; | ||||
import androidx.annotation.Nullable; | ||||
import androidx.fragment.app.Fragment; | ||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.activities.BaseActivity; | ||||
import org.mian.gitnex.activities.MainActivity; | ||||
import org.mian.gitnex.activities.SettingsAppearanceActivity; | ||||
import org.mian.gitnex.activities.SettingsBackupRestoreActivity; | ||||
import org.mian.gitnex.activities.SettingsCodeEditorActivity; | ||||
import org.mian.gitnex.activities.SettingsGeneralActivity; | ||||
import org.mian.gitnex.activities.SettingsNotificationsActivity; | ||||
import org.mian.gitnex.activities.SettingsSecurityActivity; | ||||
import org.mian.gitnex.databinding.CustomAboutDialogBinding; | ||||
import org.mian.gitnex.databinding.FragmentSettingsBinding; | ||||
import org.mian.gitnex.helpers.AppUtil; | ||||
| ||||
/** | ||||
* @author M M Arif | ||||
* @author mmarif | ||||
*/ | ||||
public class SettingsFragment extends Fragment { | ||||
| ||||
public static boolean refreshParent = false; | ||||
| ||||
private Context ctx; | ||||
private MaterialAlertDialogBuilder materialAlertDialogBuilder; | ||||
| ||||
@Nullable @Override | ||||
public View onCreateView( | ||||
| @ -46,28 +33,35 @@ public class SettingsFragment extends Fragment { | |||
| ||||
ctx = getContext(); | ||||
assert ctx != null; | ||||
materialAlertDialogBuilder = | ||||
new MaterialAlertDialogBuilder(ctx, R.style.ThemeOverlay_Material3_Dialog_Alert); | ||||
| ||||
((MainActivity) requireActivity()) | ||||
.setActionBarTitle(getResources().getString(R.string.navSettings)); | ||||
| ||||
fragmentSettingsBinding.notificationsFrame.setVisibility(View.VISIBLE); | ||||
| ||||
fragmentSettingsBinding.generalFrame.setOnClickListener( | ||||
generalFrameCall -> startActivity(new Intent(ctx, SettingsGeneralActivity.class))); | ||||
v1 -> | ||||
new BottomSheetSettingsGeneralFragment() | ||||
.show(getChildFragmentManager(), "BottomSheetSettingsGeneral")); | ||||
| ||||
fragmentSettingsBinding.appearanceFrame.setOnClickListener( | ||||
v1 -> startActivity(new Intent(ctx, SettingsAppearanceActivity.class))); | ||||
v1 -> | ||||
new BottomSheetSettingsAppearanceFragment() | ||||
.show(getChildFragmentManager(), "BottomSheetSettingsAppearance")); | ||||
| ||||
fragmentSettingsBinding.codeEditorFrame.setOnClickListener( | ||||
v1 -> startActivity(new Intent(ctx, SettingsCodeEditorActivity.class))); | ||||
v1 -> | ||||
new BottomSheetSettingsCodeEditorFragment() | ||||
.show(getChildFragmentManager(), "BottomSheetSettingsCodeEditor")); | ||||
| ||||
fragmentSettingsBinding.securityFrame.setOnClickListener( | ||||
v1 -> startActivity(new Intent(ctx, SettingsSecurityActivity.class))); | ||||
v1 -> | ||||
new BottomSheetSettingsSecurityFragment() | ||||
.show(getChildFragmentManager(), "BottomSheetSettingsSecurity")); | ||||
| ||||
fragmentSettingsBinding.notificationsFrame.setOnClickListener( | ||||
v1 -> startActivity(new Intent(ctx, SettingsNotificationsActivity.class))); | ||||
v1 -> | ||||
new BottomSheetSettingsNotificationsFragment() | ||||
.show( | ||||
getChildFragmentManager(), | ||||
"BottomSheetSettingsNotifications")); | ||||
| ||||
fragmentSettingsBinding.backupData.setText( | ||||
getString( | ||||
| @ -75,60 +69,22 @@ public class SettingsFragment extends Fragment { | |||
getString(R.string.backup), | ||||
getString(R.string.restore))); | ||||
fragmentSettingsBinding.backupFrame.setOnClickListener( | ||||
v1 -> startActivity(new Intent(ctx, SettingsBackupRestoreActivity.class))); | ||||
v1 -> | ||||
new BottomSheetSettingsBackupRestoreFragment() | ||||
.show( | ||||
getChildFragmentManager(), | ||||
"BottomSheetSettingsBackupRestore")); | ||||
| ||||
fragmentSettingsBinding.rateAppFrame.setOnClickListener(rateApp -> rateThisApp()); | ||||
| ||||
fragmentSettingsBinding.aboutAppFrame.setOnClickListener(aboutApp -> showAboutAppDialog()); | ||||
fragmentSettingsBinding.aboutAppFrame.setOnClickListener( | ||||
aboutApp -> | ||||
new BottomSheetSettingsAboutFragment() | ||||
.show(getChildFragmentManager(), "AboutBottomSheet")); | ||||
| ||||
return fragmentSettingsBinding.getRoot(); | ||||
} | ||||
| ||||
public void showAboutAppDialog() { | ||||
| ||||
CustomAboutDialogBinding aboutAppDialogBinding = | ||||
CustomAboutDialogBinding.inflate(LayoutInflater.from(ctx)); | ||||
View view = aboutAppDialogBinding.getRoot(); | ||||
| ||||
materialAlertDialogBuilder.setView(view); | ||||
| ||||
aboutAppDialogBinding.appVersionBuild.setText( | ||||
getString( | ||||
R.string.appVersionBuild, | ||||
AppUtil.getAppVersion(ctx), | ||||
AppUtil.getAppBuildNo(ctx))); | ||||
aboutAppDialogBinding.userServerVersion.setText( | ||||
((BaseActivity) requireActivity()).getAccount().getServerVersion().toString()); | ||||
| ||||
aboutAppDialogBinding.donationLinkPatreon.setOnClickListener( | ||||
v12 -> | ||||
AppUtil.openUrlInBrowser( | ||||
requireContext(), | ||||
getResources().getString(R.string.supportLinkPatreon))); | ||||
| ||||
aboutAppDialogBinding.translateLink.setOnClickListener( | ||||
v13 -> | ||||
AppUtil.openUrlInBrowser( | ||||
requireContext(), getResources().getString(R.string.crowdInLink))); | ||||
| ||||
aboutAppDialogBinding.appWebsite.setOnClickListener( | ||||
v14 -> | ||||
AppUtil.openUrlInBrowser( | ||||
requireContext(), | ||||
getResources().getString(R.string.appWebsiteLink))); | ||||
| ||||
aboutAppDialogBinding.feedback.setOnClickListener( | ||||
v14 -> | ||||
AppUtil.openUrlInBrowser( | ||||
requireContext(), getResources().getString(R.string.feedbackLink))); | ||||
| ||||
if (AppUtil.isPro(requireContext())) { | ||||
aboutAppDialogBinding.layoutFrame1.setVisibility(View.GONE); | ||||
} | ||||
| ||||
materialAlertDialogBuilder.show(); | ||||
} | ||||
| ||||
public void rateThisApp() { | ||||
| ||||
try { | ||||
| |
| @ -87,8 +87,6 @@ public class StarredRepositoriesFragment extends Fragment { | |||
getViewLifecycleOwner(), | ||||
Lifecycle.State.RESUMED); | ||||
| ||||
((MainActivity) requireActivity()) | ||||
.setActionBarTitle(getResources().getString(R.string.navStarredRepos)); | ||||
repositoriesViewModel = new ViewModelProvider(this).get(RepositoriesViewModel.class); | ||||
| ||||
resultLimit = Constants.getCurrentResultLimit(getContext()); | ||||
| |
| @ -87,8 +87,6 @@ public class WatchedRepositoriesFragment extends Fragment { | |||
getViewLifecycleOwner(), | ||||
Lifecycle.State.RESUMED); | ||||
| ||||
((MainActivity) requireActivity()) | ||||
.setActionBarTitle(getResources().getString(R.string.navWatchedRepositories)); | ||||
repositoriesViewModel = new ViewModelProvider(this).get(RepositoriesViewModel.class); | ||||
| ||||
resultLimit = Constants.getCurrentResultLimit(getContext()); | ||||
| |
| @ -33,7 +33,7 @@ public class AppDatabaseSettings { | |||
public static String APP_LINK_HANDLER_KEY = "app_link_handler"; | ||||
public static String APP_LINK_HANDLER_DEFAULT = "0"; | ||||
public static String APP_HOME_SCREEN_KEY = "app_home_screen"; | ||||
public static String APP_HOME_SCREEN_DEFAULT = "3"; | ||||
public static String APP_HOME_SCREEN_DEFAULT = "0"; | ||||
public static String APP_CUSTOM_BROWSER_KEY = "app_custom_browser_tab"; | ||||
public static String APP_CUSTOM_BROWSER_DEFAULT = "true"; | ||||
public static String APP_DRAFTS_DELETION_KEY = "app_drafts_deletion"; | ||||
| |
| @ -9,9 +9,9 @@ import androidx.lifecycle.ViewModel; | |||
import java.util.List; | ||||
import org.gitnex.tea4j.v2.models.Activity; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.adapters.DashboardAdapter; | ||||
import org.mian.gitnex.adapters.ActivitiesAdapter; | ||||
import org.mian.gitnex.clients.RetrofitClient; | ||||
import org.mian.gitnex.databinding.FragmentDashboardBinding; | ||||
import org.mian.gitnex.databinding.FragmentActivitiesBinding; | ||||
import org.mian.gitnex.helpers.Constants; | ||||
import org.mian.gitnex.helpers.Toasty; | ||||
import retrofit2.Call; | ||||
| @ -21,13 +21,13 @@ import retrofit2.Response; | |||
/** | ||||
* @author M M Arif | ||||
*/ | ||||
public class DashboardViewModel extends ViewModel { | ||||
public class ActivitiesViewModel extends ViewModel { | ||||
| ||||
private MutableLiveData<List<Activity>> activityList; | ||||
private int resultLimit; | ||||
| ||||
public LiveData<List<Activity>> getActivitiesList( | ||||
String username, Context ctx, FragmentDashboardBinding binding) { | ||||
String username, Context ctx, FragmentActivitiesBinding binding) { | ||||
| ||||
activityList = new MutableLiveData<>(); | ||||
resultLimit = Constants.getCurrentResultLimit(ctx); | ||||
| @ -35,7 +35,7 @@ public class DashboardViewModel extends ViewModel { | |||
return activityList; | ||||
} | ||||
| ||||
public void loadActivityList(String username, Context ctx, FragmentDashboardBinding binding) { | ||||
public void loadActivityList(String username, Context ctx, FragmentActivitiesBinding binding) { | ||||
| ||||
Call<List<Activity>> call = | ||||
RetrofitClient.getApiInterface(ctx) | ||||
| @ -70,8 +70,8 @@ public class DashboardViewModel extends ViewModel { | |||
String username, | ||||
int page, | ||||
Context ctx, | ||||
DashboardAdapter adapter, | ||||
FragmentDashboardBinding binding) { | ||||
ActivitiesAdapter adapter, | ||||
FragmentActivitiesBinding binding) { | ||||
| ||||
Call<List<Activity>> call = | ||||
RetrofitClient.getApiInterface(ctx) |
6 app/src/main/res/drawable/bottom_sheet_handle.xml Normal file
6
app/src/main/res/drawable/bottom_sheet_handle.xml Normal file | @ -0,0 +1,6 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | ||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" | ||||
android:shape="rectangle"> | ||||
<corners android:radius="@dimen/dimen12dp" /> | ||||
<solid android:color="?attr/iconsColor" /> | ||||
</shape> |
13 app/src/main/res/drawable/ic_activities.xml Normal file
13
app/src/main/res/drawable/ic_activities.xml Normal file | @ -0,0 +1,13 @@ | |||
<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="M3,12h4l3,8l4,-16l3,8h4" | ||||
android:strokeLineJoin="round" | ||||
android:strokeWidth="2" | ||||
android:fillColor="#00000000" | ||||
android:strokeColor="?attr/iconsColor" | ||||
android:strokeLineCap="round"/> | ||||
</vector> |
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue