diff --git a/README.md b/README.md index 05b9a4e2..862bd4a2 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ GitNex is licensed under the GPLv3 License. Please refer to the LICENSE file for [Get it on Google Play](https://play.google.com/store/apps/details?id=org.mian.gitnex.pro) [Download builds and releases](https://cloud.swatian.com/s/WS4k3seXnmfQppo) [Get it on OpenAPK](https://www.openapk.net/gitnex-for-forgejo-and-gitea/org.mian.gitnex/) +[Get it on IzzyOnDroid](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 [001.png](https://codeberg.org/gitnex/GitNex/raw/branch/main/fastlane/metadata/android/en-US/images/phoneScreenshots/001.png) | [002.png](https://codeberg.org/gitnex/GitNex/raw/branch/main/fastlane/metadata/android/en-US/images/phoneScreenshots/002.png) | [003.png](https://codeberg.org/gitnex/GitNex/raw/branch/main/fastlane/metadata/android/en-US/images/phoneScreenshots/003.png) | [004.png](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.* diff --git a/app/build.gradle b/app/build.gradle index c29218da..f54834d1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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' diff --git a/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java b/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java index 825bec06..8dd1deeb 100644 --- a/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java @@ -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 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 call, @NonNull Response 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 call, @NonNull Throwable t) {} + @NonNull Call call, @NonNull Throwable t) { + pullRequestFetchAttempted = true; + loadingFinishedPr = true; + checkAndInitWithIssue(); + } }); } + private void checkAndInitWithIssue() { + if (loadingFinishedIssue || pullRequestFetchAttempted) { + initWithIssue(); + } + } + private void getRepoInfo() { Call 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> call, @NonNull Throwable t) { + viewBinding.statusesLvMain.setVisibility(View.GONE); checkLoading(); if (ctx != null) { Toasty.error(ctx, getString(R.string.genericError)); diff --git a/app/src/main/java/org/mian/gitnex/activities/MainActivity.java b/app/src/main/java/org/mian/gitnex/activities/MainActivity.java index 9b14dd51..b650cb02 100644 --- a/app/src/main/java/org/mian/gitnex/activities/MainActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/MainActivity.java @@ -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); diff --git a/app/src/main/java/org/mian/gitnex/clients/GlideService.java b/app/src/main/java/org/mian/gitnex/clients/GlideService.java index 86ebf37a..8db4e382 100644 --- a/app/src/main/java/org/mian/gitnex/clients/GlideService.java +++ b/app/src/main/java/org/mian/gitnex/clients/GlideService.java @@ -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)); } diff --git a/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java b/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java index 7d8b614e..0f0fbc88 100644 --- a/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java +++ b/app/src/main/java/org/mian/gitnex/clients/RetrofitClient.java @@ -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 apiInterfaces = new ConcurrentHashMap<>(); private static final Map 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 { - + private static class DateQueryConverter implements Converter { static final DateQueryConverter INSTANCE = new DateQueryConverter(); private static final ThreadLocal DF = new ThreadLocal<>() { - @Override public DateFormat initialValue() { - return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US); } }; diff --git a/app/src/main/java/org/mian/gitnex/fragments/FilesFragment.java b/app/src/main/java/org/mian/gitnex/fragments/FilesFragment.java index 23ffd41a..67613abe 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/FilesFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/FilesFragment.java @@ -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() { - - @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; diff --git a/app/src/main/java/org/mian/gitnex/fragments/RepoInfoFragment.java b/app/src/main/java/org/mian/gitnex/fragments/RepoInfoFragment.java index ec230d3a..3c6799ca 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/RepoInfoFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/RepoInfoFragment.java @@ -492,13 +492,6 @@ public class RepoInfoFragment extends Fragment { requireActivity() .runOnUiThread( () -> { - Toasty.error( - ctx, - ctx - .getString( - R - .string - .genericError)); binding .fileContentsFrameHeader .setVisibility( diff --git a/app/src/main/res/layout/fragment_files.xml b/app/src/main/res/layout/fragment_files.xml index bb131c87..577623b0 100644 --- a/app/src/main/res/layout/fragment_files.xml +++ b/app/src/main/res/layout/fragment_files.xml @@ -15,37 +15,6 @@ android:visibility="gone" tools:visibility="visible"> - - - - - - - - - - - - New: Create a branch when creating, editing, or deleting a file - New: Search within files - New: Show tags if there are no releases - New: Display a user activity heatmap on the profile - New: Add pinch-and-zoom support for files - New: Mention users in issues, pull requests, and comments - New: Filter issues by labels - New: Filter issues where I am mentioned - New: Manage dependencies for issues and pull requests (view, add, remove) - New: Manage tracked time (view, add, remove) - New: Add a Created by Me filter to My Issues - Improvement: UI enhancements - Improvement: Paginate repository stargazers and watchers - Improvement: Paginate organization members and repository collaborators - Improvement: Add pagination for repository branches - Improvement: Improve the loading of large files in the file viewer - Improvement: Update translations - Bugfix: Fix the commits UI - Bugfix: Fix the issues progress bar - Removal: HTTP Basic Authentication (username/password login) has been removed + + Under development diff --git a/assets/IzzyOnDroid.png b/assets/IzzyOnDroid.png new file mode 100644 index 00000000..af5bf5bd Binary files /dev/null and b/assets/IzzyOnDroid.png differ diff --git a/build.gradle b/build.gradle index 83486707..95681bb0 100644 --- a/build.gradle +++ b/build.gradle @@ -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' } }