Improve changelog UI, add where to get access token

This commit is contained in:
M M Arif
2025-04-07 20:35:56 +05:00
parent 2391b79b46
commit 23bd6f3201
8 changed files with 258 additions and 51 deletions

View File

@@ -4,8 +4,12 @@ import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.util.TypedValue;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.core.text.HtmlCompat;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import io.mikael.urlbuilder.UrlBuilder;
import java.net.URI;
@@ -61,6 +65,8 @@ public class AddNewAccountActivity extends BaseActivity {
spinnerSelectedValue = Protocol.HTTPS.toString();
}
viewBinding.tokenHelper.setOnClickListener(token -> showTokenHelpDialog());
ArrayAdapter<Protocol> adapterProtocols =
new ArrayAdapter<>(ctx, R.layout.list_spinner_items, Protocol.values());
@@ -84,6 +90,37 @@ public class AddNewAccountActivity extends BaseActivity {
});
}
private void showTokenHelpDialog() {
MaterialAlertDialogBuilder dialogBuilder =
new MaterialAlertDialogBuilder(this)
.setMessage(
HtmlCompat.fromHtml(
getString(R.string.where_to_get_token_message),
HtmlCompat.FROM_HTML_MODE_LEGACY))
.setPositiveButton(R.string.close, null)
.setCancelable(true);
AlertDialog dialog = dialogBuilder.create();
dialog.show();
TextView messageView = dialog.findViewById(android.R.id.message);
if (messageView != null) {
messageView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
int paddingTop =
(int)
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
16,
getResources().getDisplayMetrics());
messageView.setPadding(
messageView.getPaddingLeft(),
paddingTop,
messageView.getPaddingRight(),
messageView.getPaddingBottom());
}
}
private void processLogin() {
try {

View File

@@ -10,10 +10,14 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.TypedValue;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.core.text.HtmlCompat;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import io.mikael.urlbuilder.UrlBuilder;
import java.io.File;
@@ -93,6 +97,8 @@ public class LoginActivity extends BaseActivity {
disableProcessButton();
}
activityLoginBinding.tokenHelper.setOnClickListener(token -> showTokenHelpDialog());
networkStatusObserver.registerNetworkStatusListener(
hasNetworkConnection ->
runOnUiThread(
@@ -138,6 +144,37 @@ public class LoginActivity extends BaseActivity {
});
}
private void showTokenHelpDialog() {
MaterialAlertDialogBuilder dialogBuilder =
new MaterialAlertDialogBuilder(this)
.setMessage(
HtmlCompat.fromHtml(
getString(R.string.where_to_get_token_message),
HtmlCompat.FROM_HTML_MODE_LEGACY))
.setPositiveButton(R.string.close, null)
.setCancelable(true);
AlertDialog dialog = dialogBuilder.create();
dialog.show();
TextView messageView = dialog.findViewById(android.R.id.message);
if (messageView != null) {
messageView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
int paddingTop =
(int)
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
16,
getResources().getDisplayMetrics());
messageView.setPadding(
messageView.getPaddingLeft(),
paddingTop,
messageView.getPaddingRight(),
messageView.getPaddingBottom());
}
}
private void login() {
try {

View File

@@ -526,9 +526,7 @@ public class MainActivity extends BaseActivity
if (versionCode > tinyDB.getInt("versionCode")) {
tinyDB.putInt("versionCode", versionCode);
ChangeLog changelogDialog = new ChangeLog(this);
changelogDialog.showDialog();
new ChangeLog(this).showDialog();
}
OnBackPressedCallback onBackPressedCallback =

View File

@@ -1,14 +1,21 @@
package org.mian.gitnex.helpers;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.util.Log;
import android.util.TypedValue;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.core.text.HtmlCompat;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.vdurmont.emoji.EmojiParser;
import java.io.IOException;
import java.util.Objects;
import java.util.ArrayList;
import java.util.List;
import org.mian.gitnex.R;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -18,82 +25,185 @@ import org.xmlpull.v1.XmlPullParserException;
*/
public class ChangeLog {
private static final String TAG = "ChangeLog";
private static final String CHANGELOG_XML_NODE = "changelog";
private final Activity changelogActivity;
public ChangeLog(Activity context) {
changelogActivity = context;
this.changelogActivity = context;
}
private String ParseReleaseTag(XmlResourceParser aXml)
throws XmlPullParserException, IOException {
private static class Release {
String version;
int versioncode;
List<Type> types;
StringBuilder strBuilder =
new StringBuilder(aXml.getAttributeValue(null, "version") + "<br>");
int eventType = aXml.getEventType();
while ((eventType != XmlPullParser.END_TAG) || (aXml.getName().equals("change"))) {
if ((eventType == XmlPullParser.START_TAG) && (aXml.getName().equals("change"))) {
eventType = aXml.next();
strBuilder.append(aXml.getText()).append("<br>");
}
eventType = aXml.next();
Release(String version, int versioncode) {
this.version = version;
this.versioncode = versioncode;
this.types = new ArrayList<>();
}
strBuilder.append("<br>");
return strBuilder.toString();
}
private String getChangelog(int resId, Resources res) {
private static class Type {
String name;
List<String> changes;
StringBuilder strBuilder = new StringBuilder();
try (XmlResourceParser xml = res.getXml(resId)) {
Type(String name) {
this.name = name;
this.changes = new ArrayList<>();
}
}
private Release parseLatestRelease(@NonNull XmlResourceParser xml) {
List<Release> releases = new ArrayList<>();
Release currentRelease = null;
Type currentType = null;
try {
int eventType = xml.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if ((eventType == XmlPullParser.START_TAG) && (xml.getName().equals("release"))) {
strBuilder.append(ParseReleaseTag(xml));
if (eventType == XmlPullParser.START_TAG) {
String tagName = xml.getName();
if (tagName.equals("release")) {
String version = xml.getAttributeValue(null, "version");
int versioncode = xml.getAttributeIntValue(null, "versioncode", -1);
currentRelease = new Release(version, versioncode);
releases.add(currentRelease);
} else if (tagName.equals("type") && currentRelease != null) {
String typeName = xml.getAttributeValue(null, "name");
currentType = new Type(typeName);
currentRelease.types.add(currentType);
} else if (tagName.equals("change") && currentType != null) {
eventType = xml.next();
if (eventType == XmlPullParser.TEXT) {
String changeText = xml.getText().trim();
if (!changeText.isEmpty()) {
currentType.changes.add(changeText);
}
}
}
}
eventType = xml.next();
}
} catch (XmlPullParserException | IOException e) {
Log.e(TAG, Objects.requireNonNull(e.getMessage()));
return new Release("Error", -1);
}
return strBuilder.toString();
Release latest = null;
for (Release r : releases) {
if (latest == null
|| (r.versioncode > latest.versioncode && r.versioncode != -1)
|| (r.versioncode == -1 && latest.versioncode == -1)) {
latest = r;
}
}
return latest != null ? latest : new Release("No releases found", -1);
}
private LinearLayout buildChangelogView(Release release) {
LinearLayout layout = new LinearLayout(changelogActivity);
layout.setOrientation(LinearLayout.VERTICAL);
int paddingHorizontal =
(int)
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
24,
changelogActivity.getResources().getDisplayMetrics());
int paddingVertical =
(int)
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
16,
changelogActivity.getResources().getDisplayMetrics());
layout.setPadding(paddingHorizontal, paddingVertical, paddingHorizontal, paddingVertical);
TextView versionView = new TextView(changelogActivity);
versionView.setText(
HtmlCompat.fromHtml(
"<b>" + release.version + "</b>", HtmlCompat.FROM_HTML_MODE_LEGACY));
versionView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
LinearLayout.LayoutParams versionParams =
new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
versionParams.setMargins(0, 0, 0, 16);
versionView.setLayoutParams(versionParams);
layout.addView(versionView);
for (Type type : release.types) {
TextView typeView = new TextView(changelogActivity);
typeView.setText(
HtmlCompat.fromHtml(
"<b>" + EmojiParser.parseToUnicode(type.name) + "</b>",
HtmlCompat.FROM_HTML_MODE_LEGACY));
typeView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
LinearLayout.LayoutParams typeParams =
new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
typeParams.setMargins(0, 48, 0, 16);
typeView.setLayoutParams(typeParams);
layout.addView(typeView);
for (String change : type.changes) {
if (!change.isEmpty()) {
TextView changeView = new TextView(changelogActivity);
changeView.setText(
HtmlCompat.fromHtml("" + change, HtmlCompat.FROM_HTML_MODE_LEGACY));
changeView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
LinearLayout.LayoutParams changeParams =
new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
changeParams.setMargins(32, 0, 0, 8);
changeView.setLayoutParams(changeParams);
layout.addView(changeView);
}
}
}
return layout;
}
public void showDialog() {
String packageName = changelogActivity.getPackageName();
Resources res = null;
Resources res = changelogActivity.getResources();
Release latestRelease;
try {
res = changelogActivity.getPackageManager().getResourcesForApplication(packageName);
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, Objects.requireNonNull(e.getMessage()));
try (XmlResourceParser xml = res.getXml(R.xml.changelog)) {
latestRelease = parseLatestRelease(xml);
} catch (Exception e) {
latestRelease = new Release("Error loading changelog", -1);
}
assert res != null;
int resId = res.getIdentifier(CHANGELOG_XML_NODE, "xml", packageName);
ScrollView scrollView = new ScrollView(changelogActivity);
scrollView.addView(buildChangelogView(latestRelease));
String changelogMessage = getChangelog(resId, res);
MaterialAlertDialogBuilder materialAlertDialogBuilder =
MaterialAlertDialogBuilder dialogBuilder =
new MaterialAlertDialogBuilder(changelogActivity)
.setTitle(R.string.changelogTitle)
.setMessage(
HtmlCompat.fromHtml(
"<small>" + changelogMessage + "</small>",
HtmlCompat.FROM_HTML_MODE_LEGACY))
.setView(scrollView)
.setCancelable(false)
.setNeutralButton(R.string.close, null);
materialAlertDialogBuilder.create().show();
AlertDialog dialog = dialogBuilder.create();
dialog.show();
TextView titleView = dialog.findViewById(com.google.android.material.R.id.alertTitle);
if (titleView != null) {
int extraTopPadding =
(int)
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
16,
changelogActivity.getResources().getDisplayMetrics());
titleView.setPadding(
titleView.getPaddingLeft(),
titleView.getPaddingTop() + extraTopPadding,
titleView.getPaddingRight(),
titleView.getPaddingBottom());
}
}
}

View File

@@ -126,6 +126,16 @@
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textview.MaterialTextView
android:id="@+id/token_helper"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen16dp"
android:layout_gravity="center_horizontal"
android:textSize="@dimen/dimen14sp"
android:autoLink="web"
android:text="@string/where_to_get_token_text" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>

View File

@@ -148,6 +148,17 @@
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textview.MaterialTextView
android:id="@+id/token_helper"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dimen16dp"
android:layout_marginBottom="@dimen/dimen16dp"
android:layout_gravity="center_horizontal"
android:textSize="@dimen/dimen14sp"
android:autoLink="web"
android:text="@string/where_to_get_token_text" />
<com.google.android.material.button.MaterialButton
android:id="@+id/login_button"
android:layout_width="match_parent"

View File

@@ -17,6 +17,8 @@
<string name="supportText" translatable="false">Support</string>
<string name="supportTextPatreon" translatable="false">Patreon</string>
<string name="feedbackText" translatable="false">Feedback</string>
<string name="where_to_get_token_text" translatable="false"><u>Where can I get an access token?</u></string>
<string name="where_to_get_token_message" translatable="false"><![CDATA[<br>• Log in to your account on your instance (Codeberg, etc.).<br>• Click on your user profile picture, then go to Settings.<br>• Under Applications, create a token with a name of your choice.<br>• Choose All (public, private, and limited).<br>• Under Select permissions, select Read and write for each dropdown.<br><br> Once created, copy and paste or type the token here along with the instance URL.]]></string>
<!-- menu items -->
<string name="navMyRepos">My Repositories</string>

View File

@@ -2,7 +2,9 @@
<changelog>
<release version="9.0.0-dev" versioncode="895">
<change>Under development</change>
<type name="Dev">
<change>Under development</change>
</type>
</release>
</changelog>