Frontport and updates (#1429)
All checks were successful
ci/woodpecker/push/locale Pipeline was successful
ci/woodpecker/push/build Pipeline was successful
ci/woodpecker/push/check 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

Reviewed-on: #1429 Co-authored-by: M M Arif <mmarif@swatian.com> Co-committed-by: M M Arif <mmarif@swatian.com>
This commit is contained in:
M M Arif 2025-04-01 16:36:28 +00:00 committed by M M Arif
commit 93e8075fe7

View file

@ -14,6 +14,7 @@ GitNex is licensed under the GPLv3 License. Please refer to the LICENSE file for
[<img alt='Get it on Google Play' src='https://codeberg.org/gitnex/GitNex/raw/branch/main/assets/google-play.png' height="80"/>](https://play.google.com/store/apps/details?id=org.mian.gitnex.pro)
[<img alt='Download builds and releases' src='https://codeberg.org/gitnex/GitNex/raw/branch/main/assets/apk-badge.png' height="82"/>](https://cloud.swatian.com/s/WS4k3seXnmfQppo)
[<img alt='Get it on OpenAPK' src='https://codeberg.org/gitnex/GitNex/raw/branch/main/assets/openapk.png' height="82"/>](https://www.openapk.net/gitnex-for-forgejo-and-gitea/org.mian.gitnex/)
[<img alt='Get it on IzzyOnDroid' src='https://codeberg.org/gitnex/GitNex/raw/branch/main/assets/IzzyOnDroid.png' height="82"/>](https://apt.izzysoft.de/fdroid/index/apk/org.mian.gitnex)
## Note about Forgejo and Gitea version
@ -53,7 +54,7 @@ We use [Crowdin](https://crowdin.com/project/gitnex) for translations. If your l
**Link: https://crowdin.com/project/GitNex**
## Screenshots:
## Screenshots
[<img src="https://codeberg.org/gitnex/GitNex/raw/branch/main/fastlane/metadata/android/en-US/images/phoneScreenshots/001.png" alt="001.png" width="200"/>](https://codeberg.org/gitnex/GitNex/raw/branch/main/fastlane/metadata/android/en-US/images/phoneScreenshots/001.png) | [<img src="https://codeberg.org/gitnex/GitNex/raw/branch/main/fastlane/metadata/android/en-US/images/phoneScreenshots/002.png" alt="002.png" width="200"/>](https://codeberg.org/gitnex/GitNex/raw/branch/main/fastlane/metadata/android/en-US/images/phoneScreenshots/002.png) | [<img src="https://codeberg.org/gitnex/GitNex/raw/branch/main/fastlane/metadata/android/en-US/images/phoneScreenshots/003.png" alt="003.png" width="200"/>](https://codeberg.org/gitnex/GitNex/raw/branch/main/fastlane/metadata/android/en-US/images/phoneScreenshots/003.png) | [<img src="https://codeberg.org/gitnex/GitNex/raw/branch/main/fastlane/metadata/android/en-US/images/phoneScreenshots/004.png" alt="004.png" width="200"/>](https://codeberg.org/gitnex/GitNex/raw/branch/main/fastlane/metadata/android/en-US/images/phoneScreenshots/004.png)
---|---|---|---
@ -86,7 +87,6 @@ Thanks to all the open source libraries, contributors, and donors.
- [ramseth001/TextDrawable](https://github.com/ramseth001/TextDrawable)
- [vdurmont/emoji-java](https://github.com/vdurmont/emoji-java)
- [skydoves/ColorPickerView](https://github.com/skydoves/ColorPickerView)
- [HamidrezaAmz/BreadcrumbsView](https://github.com/HamidrezaAmz/BreadcrumbsView)
- [Baseflow/PhotoView](https://github.com/Baseflow/PhotoView)
- [apache/commons](https://github.com/apache/commons-io)
- [ge0rg/MemorizingTrustManager](https://github.com/ge0rg/MemorizingTrustManager)
@ -102,6 +102,10 @@ Thanks to all the open source libraries, contributors, and donors.
- [google/material-design-icons](https://github.com/google/material-design-icons)
- [tabler/tabler-icons](https://github.com/tabler/tabler-icons)
## Social
[Follow me on Fediverse - mastodon.social/@mmarif](https://mastodon.social/@mmarif)
[Follow me on Bluesky - mmarif.bsky.social](https://bsky.app/profile/mmarif.bsky.social)
*All trademarks and logos are the properties of their respective owners.*

View file

@ -8,8 +8,8 @@ android {
applicationId "org.mian.gitnex"
minSdkVersion 23
targetSdkVersion 35
versionCode 800
versionName "8.0.0"
versionCode 895
versionName "9.0.0-dev"
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
compileSdk 35
@ -34,6 +34,9 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
dependenciesInfo {
includeInApk = false
}
compileOptions {
coreLibraryDesugaringEnabled true
@ -96,7 +99,6 @@ dependencies {
implementation "com.caverock:androidsvg-aar:1.4"
implementation "pl.droidsonroids.gif:android-gif-drawable:1.2.29"
implementation 'com.google.guava:guava:32.1.3-jre'
implementation "com.github.HamidrezaAmz:BreadcrumbsView:0.2.9"
//noinspection GradleDependency
implementation 'commons-io:commons-io:2.5'
implementation 'org.apache.commons:commons-lang3:3.13.0'

View file

@ -169,6 +169,7 @@ public class IssueDetailActivity extends BaseActivity
private final float buttonAlphaStatEnabled = 1F;
private int loadingFinished = 0;
private MentionHelper mentionHelper;
private boolean pullRequestFetchAttempted = false;
private enum Mode {
EDIT,
@ -394,6 +395,7 @@ public class IssueDetailActivity extends BaseActivity
getSingleIssue(repoOwner, repoName, issueIndex);
getAttachments();
fetchDataAsync(repoOwner, repoName, issueIndex);
getPullRequest();
viewBinding.statuses.setOnClickListener(
view -> {
@ -1016,7 +1018,7 @@ public class IssueDetailActivity extends BaseActivity
if (issue.hasIssue()) {
viewBinding.progressBar.setVisibility(View.GONE);
getSubscribed();
initWithIssue();
checkAndInitWithIssue();
return;
}
@ -1037,9 +1039,9 @@ public class IssueDetailActivity extends BaseActivity
Issue singleIssue = response.body();
assert singleIssue != null;
issue.setIssue(singleIssue);
initWithIssue();
loadingFinishedIssue = true;
checkAndInitWithIssue();
} else if (response.code() == 401) {
AlertDialogs.authorizationTokenRevokedDialog(ctx);
@ -1054,6 +1056,8 @@ public class IssueDetailActivity extends BaseActivity
public void onFailure(@NonNull Call<Issue> call, @NonNull Throwable t) {
viewBinding.progressBar.setVisibility(View.GONE);
loadingFinishedIssue = true;
checkAndInitWithIssue();
}
});
@ -1100,26 +1104,34 @@ public class IssueDetailActivity extends BaseActivity
viewBinding.issuePrState.setVisibility(View.VISIBLE);
if (issue.getIssue() == null) {
return;
}
if (issue.getIssue().getPullRequest() != null) {
viewBinding.statusesLvMain.setVisibility(View.VISIBLE);
getStatuses();
getPullRequest();
viewBinding.prInfoLayout.setVisibility(View.VISIBLE);
String displayName;
if (!issue.getPullRequest().getUser().getFullName().isEmpty()) {
displayName = issue.getPullRequest().getUser().getFullName();
User user = issue.getIssue().getUser();
if (user != null && user.getFullName() != null && !user.getFullName().isEmpty()) {
displayName = user.getFullName();
} else {
displayName = issue.getPullRequest().getUser().getLogin();
displayName = user != null && user.getLogin() != null ? user.getLogin() : "Unknown";
}
PullRequest pr = issue.getPullRequest();
if (pr != null && pr.getHead() != null && pr.getBase() != null) {
viewBinding.prInfo.setText(
getString(
R.string.pr_info,
displayName,
pr.getHead().getRef(),
pr.getBase().getRef()));
}
viewBinding.prInfo.setText(
getString(
R.string.pr_info,
displayName,
issue.getPullRequest().getHead().getRef(),
issue.getPullRequest().getBase().getRef()));
if (issue.getIssue().getPullRequest().isMerged()) { // merged
@ -1530,19 +1542,33 @@ public class IssueDetailActivity extends BaseActivity
public void onResponse(
@NonNull Call<PullRequest> call,
@NonNull Response<PullRequest> response) {
pullRequestFetchAttempted = true;
if (response.isSuccessful() && response.body() != null) {
issue.setPullRequest(response.body());
loadingFinishedPr = true;
updateMenuState();
} else {
loadingFinishedPr = true;
}
checkAndInitWithIssue();
}
@Override
public void onFailure(
@NonNull Call<PullRequest> call, @NonNull Throwable t) {}
@NonNull Call<PullRequest> call, @NonNull Throwable t) {
pullRequestFetchAttempted = true;
loadingFinishedPr = true;
checkAndInitWithIssue();
}
});
}
private void checkAndInitWithIssue() {
if (loadingFinishedIssue || pullRequestFetchAttempted) {
initWithIssue();
}
}
private void getRepoInfo() {
Call<Repository> call =
RetrofitClient.getApiInterface(ctx)
@ -1800,15 +1826,16 @@ public class IssueDetailActivity extends BaseActivity
private void getStatuses() {
PullRequest pr = issue.getPullRequest();
if (pr == null || pr.getHead() == null || pr.getHead().getRef() == null) {
viewBinding.statusesLvMain.setVisibility(View.GONE);
return;
}
String headRef = pr.getHead().getSha();
RetrofitClient.getApiInterface(ctx)
.repoListStatuses(
repoOwner,
repoName,
issue.getRepository().getBranchRef(),
null,
null,
null,
null)
.repoListStatuses(repoOwner, repoName, headRef, null, null, null, null)
.enqueue(
new Callback<>() {
@ -1862,6 +1889,7 @@ public class IssueDetailActivity extends BaseActivity
public void onFailure(
@NonNull Call<List<CommitStatus>> call, @NonNull Throwable t) {
viewBinding.statusesLvMain.setVisibility(View.GONE);
checkLoading();
if (ctx != null) {
Toasty.error(ctx, getString(R.string.genericError));

View file

@ -61,7 +61,6 @@ import org.mian.gitnex.helpers.AppUtil;
import org.mian.gitnex.helpers.ChangeLog;
import org.mian.gitnex.helpers.Toasty;
import org.mian.gitnex.structs.BottomSheetListener;
import org.mian.gitnex.structs.FragmentRefreshListener;
import retrofit2.Call;
import retrofit2.Callback;
@ -120,6 +119,8 @@ public class MainActivity extends BaseActivity
noConnection = false;
loadUserInfo();
Toolbar toolbar = activityMainBinding.toolbar;
toolbarTitle = activityMainBinding.toolbarTitle;
@ -501,24 +502,6 @@ public class MainActivity extends BaseActivity
}
}
handler.postDelayed(
() -> {
boolean connToInternet = AppUtil.hasNetworkConnection(appCtx);
if (!connToInternet) {
if (!noConnection) {
Toasty.error(
ctx, getResources().getString(R.string.checkNetConnection));
}
noConnection = true;
} else {
loadUserInfo();
noConnection = false;
}
},
750);
handler.postDelayed(
() -> {
boolean connToInternet = AppUtil.hasNetworkConnection(appCtx);

View file

@ -11,6 +11,7 @@ import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.module.AppGlideModule;
import java.io.InputStream;
import okhttp3.OkHttpClient;
import org.mian.gitnex.activities.BaseActivity;
/**
* @author mmarif
@ -26,7 +27,11 @@ public class GlideService extends AppGlideModule {
@Override
public void registerComponents(
@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
OkHttpClient okHttpClient = GlideHttpClient.getUnsafeOkHttpClient();
String token = "";
if (context instanceof BaseActivity) {
token = ((BaseActivity) context).getAccount().getAuthorization();
}
OkHttpClient okHttpClient = RetrofitClient.getOkHttpClient(context, token);
registry.replace(
GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(okHttpClient));
}

View file

@ -1,7 +1,6 @@
package org.mian.gitnex.clients;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import com.google.gson.GsonBuilder;
import java.io.File;
@ -15,12 +14,16 @@ import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.X509TrustManager;
import okhttp3.Cache;
import okhttp3.CacheControl;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.gitnex.tea4j.v2.apis.AdminApi;
import org.gitnex.tea4j.v2.apis.IssueApi;
import org.gitnex.tea4j.v2.apis.MiscellaneousApi;
@ -52,89 +55,120 @@ public class RetrofitClient {
private static final Map<String, ApiInterface> apiInterfaces = new ConcurrentHashMap<>();
private static final Map<String, WebApi> webInterfaces = new ConcurrentHashMap<>();
private static final int CACHE_SIZE_MB = 50;
private static final int MAX_AGE_SECONDS = 60 * 5;
private static final int MAX_STALE_SECONDS = 60 * 60 * 24 * 30;
private static Retrofit createRetrofit(
Context context,
String instanceUrl,
boolean cacheEnabled,
String token,
File cacheFile) {
private static OkHttpClient buildOkHttpClient(Context context, String token, File cacheFile) {
// HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
// logging.setLevel(HttpLoggingInterceptor.Level.BODY);
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
MemorizingTrustManager memorizingTrustManager = new MemorizingTrustManager(context);
sslContext.init(
null, new X509TrustManager[] {memorizingTrustManager}, new SecureRandom());
ApiKeyAuth auth = new ApiKeyAuth("header", "Authorization");
auth.setApiKey(token);
OkHttpClient.Builder okHttpClient =
new OkHttpClient.Builder()
// .addInterceptor(logging)
.addInterceptor(auth)
.sslSocketFactory(sslContext.getSocketFactory(), memorizingTrustManager)
.hostnameVerifier(
memorizingTrustManager.wrapHostnameVerifier(
HttpsURLConnection.getDefaultHostnameVerifier()));
if (cacheEnabled && cacheFile != null) {
if (cacheFile != null) {
int cacheSize = CACHE_SIZE_MB;
try {
cacheSize =
FilesData.returnOnlyNumberFileSize(
AppDatabaseSettings.getSettingsValue(
context, AppDatabaseSettings.APP_DATA_CACHE_SIZE_KEY));
} catch (Exception ignored) {
}
cacheSize = cacheSize * 1024 * 1024;
int cacheSize =
FilesData.returnOnlyNumberFileSize(
AppDatabaseSettings.getSettingsValue(
context,
AppDatabaseSettings.APP_DATA_CACHE_SIZE_KEY))
* 1024
* 1024;
Cache cache = new Cache(cacheFile, cacheSize);
File cacheDir = new File(context.getCacheDir(), "http-cache");
if (!cacheDir.exists()) {
if (!cacheDir.mkdirs()) {
throw new RuntimeException(
"Failed to create cache directory: " + cacheDir.getAbsolutePath());
}
}
Cache cache = new Cache(cacheDir, cacheSize);
okHttpClient.cache(cache);
Interceptor cacheInterceptor =
chain -> {
Request originalRequest = chain.request();
boolean hasNetwork = AppUtil.hasNetworkConnection(context);
CacheControl.Builder cacheControlBuilder = new CacheControl.Builder();
if (hasNetwork) {
cacheControlBuilder.maxAge(MAX_AGE_SECONDS, TimeUnit.SECONDS);
} else {
cacheControlBuilder
.onlyIfCached()
.maxStale(MAX_STALE_SECONDS, TimeUnit.SECONDS);
}
CacheControl cacheControl = cacheControlBuilder.build();
Request modifiedRequest =
originalRequest.newBuilder().cacheControl(cacheControl).build();
return chain.proceed(modifiedRequest);
};
Interceptor forceCacheInterceptor =
chain -> {
Request request = chain.request();
Response response = chain.proceed(request);
if (request.method().equals("GET") && response.isSuccessful()) {
return response.newBuilder()
.header(
"Cache-Control",
"public, max-age=" + MAX_AGE_SECONDS)
.removeHeader("Pragma")
.build();
}
return response;
};
okHttpClient
.cache(cache)
.addInterceptor(
chain -> {
Request request = chain.request();
request =
AppUtil.hasNetworkConnection(context)
? request.newBuilder()
.header(
"Cache-Control",
"public, max-age=" + 60)
.build()
: request.newBuilder()
.header(
"Cache-Control",
"public, only-if-cached, max-stale="
+ 60 * 60 * 24 * 30)
.build();
return chain.proceed(request);
});
.addInterceptor(auth)
.addInterceptor(cacheInterceptor)
.addNetworkInterceptor(forceCacheInterceptor);
}
return new Retrofit.Builder()
.baseUrl(instanceUrl)
.client(okHttpClient.build())
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(
GsonConverterFactory.create(
new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
.create()))
.addConverterFactory(DateQueryConverterFactory.create())
.build();
return okHttpClient.build();
} catch (Exception e) {
Log.e("onFailureRetrofit", e.toString());
throw new RuntimeException(e);
}
}
return null;
private static Retrofit createRetrofit(
Context context, String instanceUrl, String token, File cacheFile) {
OkHttpClient okHttpClient = buildOkHttpClient(context, token, cacheFile);
return new Retrofit.Builder()
.baseUrl(instanceUrl)
.client(okHttpClient)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(
GsonConverterFactory.create(
new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
.create()))
.addConverterFactory(DateQueryConverterFactory.create())
.build();
}
public static OkHttpClient getOkHttpClient(Context context, String token) {
File cacheFile = new File(context.getCacheDir(), "http-cache");
return buildOkHttpClient(context, token, cacheFile);
}
public static ApiInterface getApiInterface(Context context) {
@ -146,10 +180,8 @@ public class RetrofitClient {
}
public static WebApi getWebInterface(Context context) {
String instanceUrl = ((BaseActivity) context).getAccount().getAccount().getInstanceUrl();
instanceUrl = instanceUrl.substring(0, instanceUrl.lastIndexOf("api/v1/"));
return getWebInterface(
context,
instanceUrl,
@ -158,7 +190,6 @@ public class RetrofitClient {
}
public static WebApi getWebInterface(Context context, String url) {
return getWebInterface(
context,
url,
@ -168,45 +199,35 @@ public class RetrofitClient {
public static ApiInterface getApiInterface(
Context context, String url, String token, File cacheFile) {
String key = token.hashCode() + "@" + url;
if (!apiInterfaces.containsKey(key)) {
synchronized (RetrofitClient.class) {
if (!apiInterfaces.containsKey(key)) {
ApiInterface apiInterface =
Objects.requireNonNull(
createRetrofit(context, url, true, token, cacheFile))
Objects.requireNonNull(createRetrofit(context, url, token, cacheFile))
.create(ApiInterface.class);
apiInterfaces.put(key, apiInterface);
return apiInterface;
}
}
}
return apiInterfaces.get(key);
}
public static WebApi getWebInterface(
Context context, String url, String token, File cacheFile) {
String key = token.hashCode() + "@" + url;
if (!webInterfaces.containsKey(key)) {
synchronized (RetrofitClient.class) {
if (!webInterfaces.containsKey(key)) {
WebApi webInterface =
Objects.requireNonNull(
createRetrofit(context, url, false, token, cacheFile))
Objects.requireNonNull(createRetrofit(context, url, token, cacheFile))
.create(WebApi.class);
webInterfaces.put(key, webInterface);
return webInterface;
}
}
}
return webInterfaces.get(key);
}
@ -224,7 +245,6 @@ public class RetrofitClient {
PackageApi {}
private static class DateQueryConverterFactory extends Converter.Factory {
public static DateQueryConverterFactory create() {
return new DateQueryConverterFactory();
}
@ -240,16 +260,13 @@ public class RetrofitClient {
return null;
}
private static final class DateQueryConverter implements Converter<Date, String> {
private static class DateQueryConverter implements Converter<Date, String> {
static final DateQueryConverter INSTANCE = new DateQueryConverter();
private static final ThreadLocal<DateFormat> DF =
new ThreadLocal<>() {
@Override
public DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
}
};

View file

@ -1,6 +1,5 @@
package org.mian.gitnex.fragments;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.Intent;
import android.graphics.Rect;
@ -24,12 +23,8 @@ import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import moe.feng.common.view.breadcrumbs.DefaultBreadcrumbsCallback;
import moe.feng.common.view.breadcrumbs.model.BreadcrumbItem;
import org.gitnex.tea4j.v2.models.Branch;
import org.gitnex.tea4j.v2.models.ContentsResponse;
import org.mian.gitnex.R;
@ -95,31 +90,6 @@ public class FilesFragment extends Fragment implements FilesAdapter.FilesAdapter
binding.branchTitle.setText(repository.getBranchRef());
binding.breadcrumbsView.setItems(
new ArrayList<>(
Collections.singletonList(
BreadcrumbItem.createSimpleItem(repository.getBranchRef()))));
// noinspection unchecked
binding.breadcrumbsView.setCallback(
new DefaultBreadcrumbsCallback<BreadcrumbItem>() {
@SuppressLint("SetTextI18n")
@Override
public void onNavigateBack(BreadcrumbItem item, int position) {
if (position == 0) {
path.clear();
} else {
path.pop(path.size() - position);
}
refresh();
}
@Override
public void onNavigateNewLocation(
BreadcrumbItem newItem, int changedPosition) {}
});
requireActivity()
.getOnBackPressedDispatcher()
.addCallback(
@ -136,7 +106,6 @@ public class FilesFragment extends Fragment implements FilesAdapter.FilesAdapter
return;
}
path.remove(path.size() - 1);
binding.breadcrumbsView.removeLastItem();
if (path.size() == 0) {
fetchDataAsync(
repository.getOwner(),
@ -163,19 +132,12 @@ public class FilesFragment extends Fragment implements FilesAdapter.FilesAdapter
repoBranch -> {
repository.setBranchRef(repoBranch);
path.clear();
binding.breadcrumbsView.setItems(
new ArrayList<>(
Collections.singletonList(
BreadcrumbItem.createSimpleItem(
repository.getBranchRef()))));
refresh();
});
String dir = requireActivity().getIntent().getStringExtra("dir");
if (dir != null) {
for (String segment : dir.split("/")) {
binding.breadcrumbsView.addItem(
new BreadcrumbItem(Collections.singletonList(segment)));
path.add(segment);
}
}
@ -254,8 +216,6 @@ public class FilesFragment extends Fragment implements FilesAdapter.FilesAdapter
switch (file.getType()) {
case "dir":
path.addWithoutEncoding(file.getName());
binding.breadcrumbsView.addItem(
new BreadcrumbItem(Collections.singletonList(file.getName())));
refresh();
break;

View file

@ -492,13 +492,6 @@ public class RepoInfoFragment extends Fragment {
requireActivity()
.runOnUiThread(
() -> {
Toasty.error(
ctx,
ctx
.getString(
R
.string
.genericError));
binding
.fileContentsFrameHeader
.setVisibility(

View file

@ -15,37 +15,6 @@
android:visibility="gone"
tools:visibility="visible">
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="?attr/materialCardViewElevatedStyle"
android:layout_marginStart="@dimen/dimen8dp"
android:layout_marginEnd="@dimen/dimen8dp"
android:layout_marginTop="@dimen/dimen8dp"
android:visibility="gone"
app:cardElevation="@dimen/dimen0dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:foreground="?android:attr/selectableItemBackground"
android:background="?attr/materialCardBackgroundColor"
android:orientation="horizontal">
<moe.feng.common.view.breadcrumbs.BreadcrumbsView
android:id="@+id/breadcrumbs_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:saveEnabled="false"
android:text="@string/filesBreadcrumbRoot"
app:CustomTextSize="@dimen/dimen16sp"
app:SelectedTextColor="?attr/primaryTextColor"
app:UnSelectedTextColor="?attr/inputSelectedColor" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<!-- branch section -->
<com.google.android.material.card.MaterialCardView
android:id="@+id/branch_section"

View file

@ -1,27 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<changelog>
<release version="8.0.0" versioncode="800">
<change>New: Create a branch when creating, editing, or deleting a file</change>
<change>New: Search within files</change>
<change>New: Show tags if there are no releases</change>
<change>New: Display a user activity heatmap on the profile</change>
<change>New: Add pinch-and-zoom support for files</change>
<change>New: Mention users in issues, pull requests, and comments</change>
<change>New: Filter issues by labels</change>
<change>New: Filter issues where I am mentioned</change>
<change>New: Manage dependencies for issues and pull requests (view, add, remove)</change>
<change>New: Manage tracked time (view, add, remove)</change>
<change>New: Add a Created by Me filter to My Issues</change>
<change>Improvement: UI enhancements</change>
<change>Improvement: Paginate repository stargazers and watchers</change>
<change>Improvement: Paginate organization members and repository collaborators</change>
<change>Improvement: Add pagination for repository branches</change>
<change>Improvement: Improve the loading of large files in the file viewer</change>
<change>Improvement: Update translations</change>
<change>Bugfix: Fix the commits UI</change>
<change>Bugfix: Fix the issues progress bar</change>
<change>Removal: HTTP Basic Authentication (username/password login) has been removed</change>
<release version="9.0.0-dev" versioncode="895">
<change>Under development</change>
</release>
</changelog>

BIN
assets/IzzyOnDroid.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -7,7 +7,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:8.9.0'
classpath 'com.android.tools.build:gradle:8.9.1'
}
}