1
0
mirror of https://codeberg.org/gitnex/GitNex synced 2025-02-03 04:37:53 +01:00

Proper URL parsing, label redesign and other improvements. (#564)

Final improvements.

Fixing reply mention.

Do NOT use "instanceUrlRaw" as of now.

Minor fixes

Merge remote-tracking branch 'remotes/main/master' into login-fix

URL parsing, label and other improvements.

Co-authored-by: opyale <opyale@noreply.gitea.io>
Reviewed-on: https://codeberg.org/gitnex/GitNex/pulls/564
Reviewed-by: M M Arif <mmarif@noreply.codeberg.org>
This commit is contained in:
opyale 2020-06-30 16:43:27 +02:00 committed by M M Arif
parent 5672208fd0
commit e872069093
16 changed files with 284 additions and 176 deletions

View File

@ -66,7 +66,7 @@ We use [Crowdin](https://crowdin.com/project/gitnex) for translation. If your la
## Thanks ## Thanks
Thanks to all the open source libraries, contributors and donators. Thanks to all the open source libraries, contributors and donators.
Open source libraries #### Open source libraries
- Retrofit - Retrofit
- Gson - Gson
- Okhttp - Okhttp
@ -88,5 +88,6 @@ Open source libraries
- Barteksc/AndroidPdfViewer - Barteksc/AndroidPdfViewer
- Ge0rg/MemorizingTrustManager - Ge0rg/MemorizingTrustManager
- Dimezis/BlurView - Dimezis/BlurView
- mikaelhg/urlbuilder
[Follow me on Fediverse - mastodon.social/@mmarif](https://mastodon.social/@mmarif) [Follow me on Fediverse - mastodon.social/@mmarif](https://mastodon.social/@mmarif)

View File

@ -86,6 +86,7 @@ dependencies {
implementation "ch.acra:acra-mail:$acra" implementation "ch.acra:acra-mail:$acra"
implementation "ch.acra:acra-limiter:$acra" implementation "ch.acra:acra-limiter:$acra"
implementation "ch.acra:acra-notification:$acra" implementation "ch.acra:acra-notification:$acra"
implementation 'com.eightbitlab:blurview:1.6.3' implementation "com.eightbitlab:blurview:1.6.3"
implementation "io.mikael:urlbuilder:2.0.9"
} }

View File

@ -22,6 +22,7 @@ import com.tooltip.Tooltip;
import org.mian.gitnex.R; import org.mian.gitnex.R;
import org.mian.gitnex.clients.RetrofitClient; import org.mian.gitnex.clients.RetrofitClient;
import org.mian.gitnex.helpers.NetworkObserver; import org.mian.gitnex.helpers.NetworkObserver;
import org.mian.gitnex.helpers.PathsHelper;
import org.mian.gitnex.helpers.SnackBar; import org.mian.gitnex.helpers.SnackBar;
import org.mian.gitnex.helpers.UrlHelper; import org.mian.gitnex.helpers.UrlHelper;
import org.mian.gitnex.helpers.Version; import org.mian.gitnex.helpers.Version;
@ -30,10 +31,11 @@ import org.mian.gitnex.models.UserInfo;
import org.mian.gitnex.models.UserTokens; import org.mian.gitnex.models.UserTokens;
import org.mian.gitnex.util.AppUtil; import org.mian.gitnex.util.AppUtil;
import org.mian.gitnex.util.TinyDB; import org.mian.gitnex.util.TinyDB;
import java.net.URL; import java.net.URI;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import io.mikael.urlbuilder.UrlBuilder;
import okhttp3.Credentials; import okhttp3.Credentials;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.Callback; import retrofit2.Callback;
@ -166,17 +168,23 @@ public class LoginActivity extends BaseActivity {
String loginToken = loginTokenCode.getText().toString().trim(); String loginToken = loginTokenCode.getText().toString().trim();
Protocol protocol = (Protocol) protocolSpinner.getSelectedItem(); Protocol protocol = (Protocol) protocolSpinner.getSelectedItem();
URL rawInstanceUrl = new URL(UrlHelper.fixScheme(instanceUrlET.getText().toString(), protocol.name().toLowerCase()));
LoginType loginType = (loginMethod.getCheckedRadioButtonId() == R.id.loginUsernamePassword) ? LoginType.BASIC : LoginType.TOKEN; LoginType loginType = (loginMethod.getCheckedRadioButtonId() == R.id.loginUsernamePassword) ? LoginType.BASIC : LoginType.TOKEN;
String portAppendix = (rawInstanceUrl.getPort() > 0) ? ":" + rawInstanceUrl.getPort() : ""; URI rawInstanceUrl = UrlBuilder.fromString(UrlHelper.fixScheme(instanceUrlET.getText().toString(), "http"))
String instanceUrlWithProtocol = protocol.name().toLowerCase() + "://" + rawInstanceUrl.getHost() + portAppendix; .toUri();
String instanceUrl = instanceUrlWithProtocol + "/api/v1/";
URI instanceUrlWithProtocol = UrlBuilder.fromUri(rawInstanceUrl)
.withScheme(protocol.name().toLowerCase())
.toUri();
URI instanceUrl = UrlBuilder.fromUri(instanceUrlWithProtocol)
.withPath(PathsHelper.join(instanceUrlWithProtocol.getPath(), "/api/v1/"))
.toUri();
tinyDB.putString("loginType", loginType.name().toLowerCase()); tinyDB.putString("loginType", loginType.name().toLowerCase());
tinyDB.putString("instanceUrlRaw", rawInstanceUrl.getHost()); tinyDB.putString("instanceUrlRaw", instanceUrlET.getText().toString());
tinyDB.putString("instanceUrl", instanceUrl); tinyDB.putString("instanceUrl", instanceUrl.toString());
tinyDB.putString("instanceUrlWithProtocol", instanceUrlWithProtocol); tinyDB.putString("instanceUrlWithProtocol", instanceUrlWithProtocol.toString());
if(instanceUrlET.getText().toString().equals("")) { if(instanceUrlET.getText().toString().equals("")) {
@ -188,7 +196,13 @@ public class LoginActivity extends BaseActivity {
if(loginType == LoginType.BASIC) { if(loginType == LoginType.BASIC) {
int loginOTP = (otpCode.getText().toString().length() == 6) ? Integer.parseInt(otpCode.getText().toString().trim()) : 0; if(otpCode.length() != 0 && otpCode.length() != 6) {
SnackBar.warning(ctx, layoutView, getResources().getString(R.string.loginOTPTypeError));
enableProcessButton();
return;
}
if(rawInstanceUrl.getUserInfo() != null) { if(rawInstanceUrl.getUserInfo() != null) {
@ -197,8 +211,6 @@ public class LoginActivity extends BaseActivity {
} }
tinyDB.putString("loginUid", loginUid);
if(loginUid.equals("")) { if(loginUid.equals("")) {
SnackBar.warning(ctx, layoutView, getResources().getString(R.string.emptyFieldUsername)); SnackBar.warning(ctx, layoutView, getResources().getString(R.string.emptyFieldUsername));
@ -215,7 +227,10 @@ public class LoginActivity extends BaseActivity {
} }
versionCheck(instanceUrl, loginUid, loginPass, loginOTP, loginToken, 1); int loginOTP = (otpCode.length() > 0) ? Integer.parseInt(otpCode.getText().toString().trim()) : 0;
tinyDB.putString("loginUid", loginUid);
versionCheck(instanceUrl.toString(), loginUid, loginPass, loginOTP, loginToken, 1);
} }
else { else {
@ -228,7 +243,7 @@ public class LoginActivity extends BaseActivity {
} }
versionCheck(instanceUrl, loginUid, loginPass, 123, loginToken, 2); versionCheck(instanceUrl.toString(), loginUid, loginPass, 123, loginToken, 2);
} }
@ -358,8 +373,8 @@ public class LoginActivity extends BaseActivity {
switch(response.code()) { switch(response.code()) {
case 200: case 200:
tinyDB.putBoolean("loggedInMode", true);
assert userDetails != null; assert userDetails != null;
tinyDB.putBoolean("loggedInMode", true);
tinyDB.putString(userDetails.getLogin() + "-token", loginToken); tinyDB.putString(userDetails.getLogin() + "-token", loginToken);
tinyDB.putString("loginUid", userDetails.getLogin()); tinyDB.putString("loginUid", userDetails.getLogin());
tinyDB.putString("userLogin", userDetails.getUsername()); tinyDB.putString("userLogin", userDetails.getUsername());
@ -423,8 +438,8 @@ public class LoginActivity extends BaseActivity {
if(response.code() == 200) { if(response.code() == 200) {
boolean setTokenFlag = false;
assert userTokens != null; assert userTokens != null;
boolean setTokenFlag = false;
if(userTokens.size() > 0) { // FIXME This is in need of a refactor, but i don't understand what the code is used for. if(userTokens.size() > 0) { // FIXME This is in need of a refactor, but i don't understand what the code is used for.
@ -494,9 +509,9 @@ public class LoginActivity extends BaseActivity {
switch(response.code()) { switch(response.code()) {
case 200: case 200:
assert userDetails != null;
tinyDB.remove("loginPass"); tinyDB.remove("loginPass");
tinyDB.putBoolean("loggedInMode", true); tinyDB.putBoolean("loggedInMode", true);
assert userDetails != null;
tinyDB.putString("userLogin", userDetails.getUsername()); tinyDB.putString("userLogin", userDetails.getUsername());
tinyDB.putString(loginUid + "-token", newToken.getSha1()); tinyDB.putString(loginUid + "-token", newToken.getSha1());
tinyDB.putString(loginUid + "-token-last-eight", appUtil.getLastCharactersOfWord(newToken.getSha1(), 8)); tinyDB.putString(loginUid + "-token-last-eight", appUtil.getLastCharactersOfWord(newToken.getSha1(), 8));

View File

@ -515,7 +515,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig
tinyDb.putString("userLang", userDetails.getLang()); tinyDb.putString("userLang", userDetails.getLang());
} }
else { else {
tinyDb.putString("userLang", "..."); tinyDb.putString("userLang", "");
} }
} }
} }
@ -526,7 +526,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig
} }
else { else {
String toastError = getResources().getString(R.string.genericApiStatusError) + String.valueOf(response.code()); String toastError = getResources().getString(R.string.genericApiStatusError) + response.code();
Toasty.info(ctx, toastError); Toasty.info(ctx, toastError);
} }

View File

@ -5,7 +5,13 @@ import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import org.mian.gitnex.R;
import org.mian.gitnex.helpers.PathsHelper;
import org.mian.gitnex.helpers.Toasty;
import org.mian.gitnex.util.TinyDB; import org.mian.gitnex.util.TinyDB;
import java.net.URI;
import java.net.URISyntaxException;
import io.mikael.urlbuilder.UrlBuilder;
/** /**
* Author M M Arif * Author M M Arif
@ -18,20 +24,28 @@ public class OpenRepoInBrowserActivity extends AppCompatActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
appCtx = getApplicationContext(); appCtx = getApplicationContext();
TinyDB tinyDb = new TinyDB(appCtx);
TinyDB tinyDb = new TinyDB(appCtx); try {
String instanceUrlWithProtocol = "https://" + tinyDb.getString("instanceUrlRaw");
if (!tinyDb.getString("instanceUrlWithProtocol").isEmpty()) {
instanceUrlWithProtocol = tinyDb.getString("instanceUrlWithProtocol");
}
String repoFullNameBrowser = getIntent().getStringExtra("repoFullNameBrowser"); URI instanceUrl = new URI(tinyDb.getString("instanceUrlWithProtocol"));
Uri url = Uri.parse(instanceUrlWithProtocol + "/" + repoFullNameBrowser);
Intent i = new Intent(Intent.ACTION_VIEW, url); String browserPath = PathsHelper.join(instanceUrl.getPath(), getIntent().getStringExtra("repoFullNameBrowser"));
startActivity(i);
finish(); String browserUrl = UrlBuilder.fromUri(instanceUrl)
.withPath(browserPath)
.toString();
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(browserUrl));
startActivity(i);
finish();
}
catch(URISyntaxException e) {
Toasty.error(appCtx, getString(R.string.genericError));
}
} }

View File

@ -154,7 +154,12 @@ public class IssueCommentsAdapter extends RecyclerView.Adapter<IssueCommentsAdap
commentMenuQuote.setOnClickListener(v1 -> { commentMenuQuote.setOnClickListener(v1 -> {
StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("@").append(commenterUsername.getText().toString()).append("\n\n"); String commenterName = commenterUsername.getText().toString();
if(!commenterName.equals(tinyDb.getString("userLogin"))) {
stringBuilder.append("@").append(commenterName).append("\n\n");
}
String[] lines = commendBodyRaw.getText().toString().split("\\R"); String[] lines = commendBodyRaw.getText().toString().split("\\R");

View File

@ -3,26 +3,25 @@ package org.mian.gitnex.adapters;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Typeface;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import com.amulyakhare.textdrawable.TextDrawable; import androidx.annotation.NonNull;
import androidx.cardview.widget.CardView;
import androidx.core.widget.ImageViewCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.bottomsheet.BottomSheetDialog; import com.google.android.material.bottomsheet.BottomSheetDialog;
import org.mian.gitnex.R; import org.mian.gitnex.R;
import org.mian.gitnex.activities.CreateLabelActivity; import org.mian.gitnex.activities.CreateLabelActivity;
import org.mian.gitnex.helpers.AlertDialogs; import org.mian.gitnex.helpers.AlertDialogs;
import org.mian.gitnex.helpers.ColorInverter; import org.mian.gitnex.helpers.ColorInverter;
import org.mian.gitnex.helpers.LabelWidthCalculator;
import org.mian.gitnex.models.Labels; import org.mian.gitnex.models.Labels;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
/** /**
* Author M M Arif * Author M M Arif
@ -39,12 +38,17 @@ public class LabelsAdapter extends RecyclerView.Adapter<LabelsAdapter.LabelsView
private TextView labelTitle; private TextView labelTitle;
private TextView labelId; private TextView labelId;
private TextView labelColor; private TextView labelColor;
private ImageView labelsView;
private CardView labelView;
private ImageView labelIcon;
private TextView labelName;
private LabelsViewHolder(View itemView) { private LabelsViewHolder(View itemView) {
super(itemView); super(itemView);
labelsView = itemView.findViewById(R.id.labelsView); labelView = itemView.findViewById(R.id.labelView);
labelIcon = itemView.findViewById(R.id.labelIcon);
labelName = itemView.findViewById(R.id.labelName);
ImageView labelsOptionsMenu = itemView.findViewById(R.id.labelsOptionsMenu); ImageView labelsOptionsMenu = itemView.findViewById(R.id.labelsOptionsMenu);
labelTitle = itemView.findViewById(R.id.labelTitle); labelTitle = itemView.findViewById(R.id.labelTitle);
labelId = itemView.findViewById(R.id.labelId); labelId = itemView.findViewById(R.id.labelId);
@ -119,19 +123,13 @@ public class LabelsAdapter extends RecyclerView.Adapter<LabelsAdapter.LabelsView
String labelName = currentItem.getName(); String labelName = currentItem.getName();
int color = Color.parseColor("#" + labelColor); int color = Color.parseColor("#" + labelColor);
int contrastColor = new ColorInverter().getContrastColor(color);
TextDrawable drawable = TextDrawable.builder() ImageViewCompat.setImageTintList(holder.labelIcon, ColorStateList.valueOf(contrastColor));
.beginConfig()
.useFont(Typeface.DEFAULT)
.bold()
.textColor(new ColorInverter().getContrastColor(color))
.fontSize(35)
.width(LabelWidthCalculator.calculateLabelWidth(labelName, Typeface.DEFAULT, 40, 20))
.height(55)
.endConfig()
.buildRoundRect(labelName, color, 10);
holder.labelsView.setImageDrawable(drawable); holder.labelName.setTextColor(contrastColor);
holder.labelName.setText(labelName);
holder.labelView.setCardBackgroundColor(color);
} }
@ -140,4 +138,4 @@ public class LabelsAdapter extends RecyclerView.Adapter<LabelsAdapter.LabelsView
return labelsList.size(); return labelsList.size();
} }
} }

View File

@ -19,10 +19,14 @@ import org.mian.gitnex.activities.AddRemoveLabelsActivity;
import org.mian.gitnex.activities.EditIssueActivity; import org.mian.gitnex.activities.EditIssueActivity;
import org.mian.gitnex.activities.FileDiffActivity; import org.mian.gitnex.activities.FileDiffActivity;
import org.mian.gitnex.activities.MergePullRequestActivity; import org.mian.gitnex.activities.MergePullRequestActivity;
import org.mian.gitnex.helpers.PathsHelper;
import org.mian.gitnex.helpers.Toasty; import org.mian.gitnex.helpers.Toasty;
import org.mian.gitnex.helpers.Version; import org.mian.gitnex.helpers.Version;
import org.mian.gitnex.util.TinyDB; import org.mian.gitnex.util.TinyDB;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Objects; import java.util.Objects;
import io.mikael.urlbuilder.UrlBuilder;
/** /**
* Author M M Arif * Author M M Arif
@ -138,53 +142,61 @@ public class BottomSheetSingleIssueFragment extends BottomSheetDialogFragment {
shareIssue.setOnClickListener(v1 -> { shareIssue.setOnClickListener(v1 -> {
// get url of repo try {
String repoFullName = tinyDB.getString("repoFullName");
String instanceUrlWithProtocol = "https://" + tinyDB.getString("instanceUrlRaw"); URI instanceUrl = new URI(tinyDB.getString("instanceUrlWithProtocol"));
if(!tinyDB.getString("instanceUrlWithProtocol").isEmpty()) {
instanceUrlWithProtocol = tinyDB.getString("instanceUrlWithProtocol"); String issuePath = PathsHelper.join(instanceUrl.getPath(), tinyDB.getString("repoFullName"), "/issues/", tinyDB.getString("issueNumber"));
String issueUrl = UrlBuilder.fromUri(instanceUrl)
.withPath(issuePath)
.toString();
// share issue
Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
sharingIntent.setType("text/plain");
sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, getResources().getString(R.string.hash) + tinyDB.getString("issueNumber") + " " + tinyDB.getString("issueTitle"));
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, issueUrl);
startActivity(Intent.createChooser(sharingIntent, getResources().getString(R.string.hash) + tinyDB.getString("issueNumber") + " " + tinyDB.getString("issueTitle")));
}
catch(URISyntaxException e) {
Toasty.error(ctx, getString(R.string.genericError));
}
finally {
dismiss();
} }
// get issue Url
String issueUrl = instanceUrlWithProtocol + "/" + repoFullName + "/issues/" + tinyDB.getString("issueNumber");
// share issue
Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
sharingIntent.setType("text/plain");
sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, getResources().getString(R.string.hash) + tinyDB.getString("issueNumber") + " " + tinyDB.getString("issueTitle"));
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, issueUrl);
startActivity(Intent.createChooser(sharingIntent, getResources().getString(R.string.hash) + tinyDB.getString("issueNumber") + " " + tinyDB.getString("issueTitle")));
dismiss();
}); });
copyIssueUrl.setOnClickListener(new View.OnClickListener() { copyIssueUrl.setOnClickListener(v12 -> {
@Override try {
public void onClick(View v) {
// get url of repo URI instanceUrl = new URI(tinyDB.getString("instanceUrlWithProtocol"));
String repoFullName = tinyDB.getString("repoFullName");
String instanceUrlWithProtocol = "https://" + tinyDB.getString("instanceUrlRaw");
if(!tinyDB.getString("instanceUrlWithProtocol").isEmpty()) {
instanceUrlWithProtocol = tinyDB.getString("instanceUrlWithProtocol");
}
// get issue Url String issuePath = PathsHelper.join(instanceUrl.getPath(), tinyDB.getString("repoFullName"), "/issues/", tinyDB.getString("issueNumber"));
String issueUrl = instanceUrlWithProtocol + "/" + repoFullName + "/issues/" + tinyDB.getString("issueNumber");
String issueUrl = UrlBuilder.fromUri(instanceUrl)
.withPath(issuePath)
.toString();
// copy to clipboard // copy to clipboard
ClipboardManager clipboard = (ClipboardManager) Objects.requireNonNull(ctx).getSystemService(android.content.Context.CLIPBOARD_SERVICE); ClipboardManager clipboard = (ClipboardManager) Objects.requireNonNull(ctx).getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("issueUrl", issueUrl); ClipData clip = ClipData.newPlainText("issueUrl", issueUrl);
assert clipboard != null; assert clipboard != null;
clipboard.setPrimaryClip(clip); clipboard.setPrimaryClip(clip);
dismiss();
Toasty.info(ctx, ctx.getString(R.string.copyIssueUrlToastMsg)); Toasty.info(ctx, ctx.getString(R.string.copyIssueUrlToastMsg));
} }
catch(URISyntaxException e) {
Toasty.error(ctx, getString(R.string.genericError));
}
finally {
dismiss();
}
}); });
if(tinyDB.getString("issueType").equals("issue")) { if(tinyDB.getString("issueType").equals("issue")) {

View File

@ -63,11 +63,19 @@ public class ProfileFragment extends Fragment {
ViewGroup aboutFrame = v.findViewById(R.id.aboutFrame); ViewGroup aboutFrame = v.findViewById(R.id.aboutFrame);
String[] userLanguageCodes = tinyDb.getString("userLang").split("-"); String[] userLanguageCodes = tinyDb.getString("userLang").split("-");
Locale locale = new Locale(userLanguageCodes[0], userLanguageCodes[1]);
if(userLanguageCodes.length >= 2) {
Locale locale = new Locale(userLanguageCodes[0], userLanguageCodes[1]);
userLanguage.setText(locale.getDisplayCountry());
}
else {
userLanguage.setText(R.string.notSupported);
}
userFullName.setText(tinyDb.getString("userFullname")); userFullName.setText(tinyDb.getString("userFullname"));
userLogin.setText(getString(R.string.usernameWithAt, tinyDb.getString("userLogin"))); userLogin.setText(getString(R.string.usernameWithAt, tinyDb.getString("userLogin")));
userLanguage.setText(locale.getDisplayCountry());
PicassoService.getInstance(ctx).get() PicassoService.getInstance(ctx).get()
.load(tinyDb.getString("userAvatar")) .load(tinyDb.getString("userAvatar"))

View File

@ -0,0 +1,37 @@
package org.mian.gitnex.helpers;
/**
* Author opyale
*/
public class PathsHelper {
public static String join(String... paths) {
StringBuilder stringBuilder = new StringBuilder();
for(String path : paths) {
if(path != null && !path.isEmpty()) {
if(!path.startsWith("/")) {
stringBuilder.append("/");
}
if(path.endsWith("/")) {
path = path.substring(0, path.lastIndexOf("/"));
}
stringBuilder.append(path);
}
}
return stringBuilder.append("/").toString();
}
}

View File

@ -9,39 +9,39 @@ import java.net.URISyntaxException;
public class UrlHelper { public class UrlHelper {
public static String cleanUrl(String url) { public static String cleanUrl(String url) {
URI uri = null; URI uri = null;
try { try {
uri = new URI(url); uri = new URI(url);
} catch (URISyntaxException e) { }
e.printStackTrace(); catch(URISyntaxException e) {
} e.printStackTrace();
}
assert uri != null; assert uri != null;
String urlProtocol = uri.getScheme(); String urlProtocol = uri.getScheme();
String urlHost = uri.getHost(); String urlHost = uri.getHost();
int urlPort = uri.getPort(); int urlPort = uri.getPort();
String urlFinal = null; String urlFinal = null;
if(urlPort > 0) { if(urlPort > 0) {
urlFinal = urlProtocol + "://" + urlHost + ":" + urlPort; urlFinal = urlProtocol + "://" + urlHost + ":" + urlPort;
} }
else if(urlProtocol != null) { else if(urlProtocol != null) {
urlFinal = urlProtocol + "://" + urlHost; urlFinal = urlProtocol + "://" + urlHost;
} }
else { else {
urlFinal = urlHost; urlFinal = urlHost;
} }
return urlFinal; return urlFinal;
} }
public static String fixScheme(String url, String scheme) { public static String fixScheme(String url, String scheme) {
return !url.matches("^(http|https)://.+$") ? scheme + "://" + url : url; return !url.matches("^(http|https)://.+$") ? scheme + "://" + url : url;
} }
} }

View File

@ -95,7 +95,7 @@ public class AppUtil {
return str.matches("^[\\w-]+$"); return str.matches("^[\\w-]+$");
} }
public Boolean checkIntegers(String str) { public static Boolean checkIntegers(String str) {
return str.matches("\\d+"); return str.matches("\\d+");
} }

View File

@ -1,61 +1,86 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/linearLayoutMainFrame"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:background="?attr/primaryBackgroundColor"
android:id="@+id/relativeLayoutMainFrame" android:orientation="horizontal"
android:background="?attr/primaryBackgroundColor"> android:padding="10dp">
<TextView <TextView
android:id="@+id/labelTitle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:visibility="gone" android:layout_weight="1"
android:id="@+id/labelTitle"/> android:visibility="gone" />
<TextView <TextView
android:id="@+id/labelId"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:visibility="gone" android:layout_weight="1"
android:id="@+id/labelId"/> android:visibility="gone" />
<TextView <TextView
android:id="@+id/labelColor"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:visibility="gone" android:layout_weight="1"
android:id="@+id/labelColor"/> android:visibility="gone" />
<LinearLayout <androidx.cardview.widget.CardView
android:layout_width="match_parent" android:id="@+id/labelView"
android:layout_height="match_parent" android:layout_width="wrap_content"
android:layout_margin="15dp" android:layout_height="wrap_content"
android:id="@+id/labelsFrame" app:cardCornerRadius="5dp"
android:orientation="horizontal"> app:cardElevation="0dp"
app:contentPaddingBottom="3dp"
app:contentPaddingLeft="10dp"
app:contentPaddingRight="10dp"
app:contentPaddingTop="3dp">
<ImageView <LinearLayout
android:id="@+id/labelsView" android:layout_width="match_parent"
android:layout_width="0dp" android:layout_height="match_parent"
android:layout_height="wrap_content" android:gravity="center_vertical"
android:layout_gravity="start" android:orientation="horizontal">
android:paddingTop="5dp"
android:contentDescription="@string/labelMenuContentDesc"
android:gravity="start"
android:layout_weight="1"
android:scaleType="fitStart"
android:src="@drawable/ic_label" />
<ImageView <ImageView
android:id="@+id/labelsOptionsMenu" android:id="@+id/labelIcon"
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="end" android:layout_weight="0"
android:layout_weight="0.15" android:contentDescription="@string/generalImgContentText"
android:contentDescription="@string/labelMenuContentDesc" android:tint="@color/colorWhite"
android:gravity="end" app:srcCompat="@drawable/ic_label" />
android:scaleType="fitEnd"
android:src="@drawable/ic_dotted_menu_horizontal" />
</LinearLayout> <TextView
android:id="@+id/labelName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_weight="1"
android:textColor="@color/colorWhite"
android:textSize="16sp"
android:textStyle="bold" />
</LinearLayout>
</RelativeLayout> </androidx.cardview.widget.CardView>
<Space
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<ImageView
android:id="@+id/labelsOptionsMenu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:contentDescription="@string/labelMenuContentDesc"
android:paddingLeft="10dp"
android:src="@drawable/ic_dotted_menu_horizontal" />
</LinearLayout>

View File

@ -27,8 +27,7 @@
android:singleLine="true" android:singleLine="true"
android:textColor="?attr/primaryTextColor" android:textColor="?attr/primaryTextColor"
android:textSize="18sp" android:textSize="18sp"
android:textStyle="bold" android:textStyle="bold" />
tools:text="3.0.0-rc1" />
<TextView <TextView
android:id="@+id/releaseType" android:id="@+id/releaseType"
@ -68,8 +67,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="5dp" android:layout_marginStart="5dp"
android:textColor="?attr/primaryTextColor" android:textColor="?attr/primaryTextColor"
android:textSize="14sp" android:textSize="14sp" />
tools:text="Published by @mmarif" />
</LinearLayout> </LinearLayout>
@ -99,8 +97,7 @@
android:layout_marginStart="5dp" android:layout_marginStart="5dp"
android:singleLine="true" android:singleLine="true"
android:textColor="?attr/primaryTextColor" android:textColor="?attr/primaryTextColor"
android:textSize="14sp" android:textSize="14sp" />
tools:text="3.0.0-rc1" />
</LinearLayout> </LinearLayout>
@ -125,8 +122,7 @@
android:layout_marginStart="5dp" android:layout_marginStart="5dp"
android:singleLine="true" android:singleLine="true"
android:textColor="?attr/primaryTextColor" android:textColor="?attr/primaryTextColor"
android:textSize="14sp" android:textSize="14sp" />
tools:text="8b1c79c0c3" />
</LinearLayout> </LinearLayout>
@ -150,8 +146,7 @@
android:layout_marginStart="5dp" android:layout_marginStart="5dp"
android:singleLine="true" android:singleLine="true"
android:textColor="?attr/primaryTextColor" android:textColor="?attr/primaryTextColor"
android:textSize="14sp" android:textSize="14sp" />
tools:text="1 day ago" />
</LinearLayout> </LinearLayout>
@ -172,9 +167,10 @@
android:id="@+id/releaseBodyContent" android:id="@+id/releaseBodyContent"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:autoLink="web|email"
android:textColorLink="@color/lightBlue"
android:textColor="?attr/primaryTextColor" android:textColor="?attr/primaryTextColor"
android:textSize="14sp" android:textSize="14sp" />
tools:text="Put your release body here" />
</LinearLayout> </LinearLayout>
@ -207,8 +203,7 @@
android:layout_marginStart="3dp" android:layout_marginStart="3dp"
android:textColor="?attr/primaryTextColor" android:textColor="?attr/primaryTextColor"
android:textSize="14sp" android:textSize="14sp"
android:text="@string/releaseDownloadText" android:text="@string/releaseDownloadText" />
tools:text="Downloads" />
</LinearLayout> </LinearLayout>
@ -229,8 +224,7 @@
android:drawableStart="@drawable/ic_file_download_24dp" android:drawableStart="@drawable/ic_file_download_24dp"
android:drawablePadding="8dp" android:drawablePadding="8dp"
android:textColor="?attr/primaryTextColor" android:textColor="?attr/primaryTextColor"
android:textSize="14sp" android:textSize="14sp" />
tools:text="Source code (ZIP)" />
<TextView <TextView
android:id="@+id/releaseTarDownload" android:id="@+id/releaseTarDownload"
@ -241,8 +235,7 @@
android:drawableStart="@drawable/ic_file_download_24dp" android:drawableStart="@drawable/ic_file_download_24dp"
android:drawablePadding="8dp" android:drawablePadding="8dp"
android:textColor="?attr/primaryTextColor" android:textColor="?attr/primaryTextColor"
android:textSize="14sp" android:textSize="14sp" />
tools:text="Source code (TAR)" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/downloadList" android:id="@+id/downloadList"

View File

@ -29,19 +29,17 @@
android:id="@+id/userAvatar" android:id="@+id/userAvatar"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:src="@drawable/ic_android"
android:maxHeight="24dp" android:maxHeight="24dp"
android:maxWidth="24dp" android:maxWidth="24dp"
android:paddingStart="20dp" android:paddingStart="20dp"
android:paddingEnd="5dp" android:paddingEnd="5dp"
android:contentDescription="@string/app_name"/> android:contentDescription="@string/generalImgContentText"/>
<TextView <TextView
android:id="@+id/userFullname" android:id="@+id/userFullname"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingTop="10dp" android:paddingTop="10dp"
android:text="@string/app_name"
android:textSize="18sp" android:textSize="18sp"
android:textIsSelectable="true" android:textIsSelectable="true"
android:textColor="@color/white" android:textColor="@color/white"
@ -59,7 +57,6 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_weight="0.85" android:layout_weight="0.85"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/appEmail"
android:textSize="14sp" android:textSize="14sp"
android:textIsSelectable="true" android:textIsSelectable="true"
android:textColor="@color/white" android:textColor="@color/white"

View File

@ -527,6 +527,8 @@
<string name="filesGenericError">Sorry this file cannot be viewed as API returned an error</string> <string name="filesGenericError">Sorry this file cannot be viewed as API returned an error</string>
<string name="filesBreadcrumb">Root</string> <string name="filesBreadcrumb">Root</string>
<string name="notSupported">Not supported</string>
<!-- generic copy --> <!-- generic copy -->
<string name="okButton">OK</string> <string name="okButton">OK</string>
<string name="doneButton">Done</string> <string name="doneButton">Done</string>