mirror of
https://github.com/gitnex-org/gitnex.git
synced 2026-03-22 13:05:25 -05:00
add custom api for repo contents (files) and retrofit client
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -195,7 +195,5 @@ crowdin.yml
|
||||
|
||||
!/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# End of https://www.gitignore.io/api/android,androidstudio
|
||||
|
||||
# Crowdin Config
|
||||
crowdin.yml
|
||||
# Others
|
||||
local.md
|
||||
1008
APP_STRUCTURE.md
Normal file
1008
APP_STRUCTURE.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
||||
package org.mian.gitnex.api.clients;
|
||||
|
||||
import java.util.List;
|
||||
import org.mian.gitnex.api.models.contents.RepoGetContentsList;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
/**
|
||||
* @author mmarif
|
||||
*/
|
||||
public interface ApiInterface {
|
||||
|
||||
@GET("repos/{owner}/{repo}/contents")
|
||||
Call<List<RepoGetContentsList>> getRepoContents(
|
||||
@Path("owner") String owner, @Path("repo") String repo, @Query("ref") String ref);
|
||||
|
||||
@GET("repos/{owner}/{repo}/contents/{path}")
|
||||
Call<List<RepoGetContentsList>> getRepoContents(
|
||||
@Path("owner") String owner,
|
||||
@Path("repo") String repo,
|
||||
@Path("path") String path,
|
||||
@Query("ref") String ref);
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package org.mian.gitnex.api.clients;
|
||||
|
||||
import android.content.Context;
|
||||
import java.io.File;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Map;
|
||||
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.mian.gitnex.activities.BaseActivity;
|
||||
import org.mian.gitnex.helpers.AppDatabaseSettings;
|
||||
import org.mian.gitnex.helpers.AppUtil;
|
||||
import org.mian.gitnex.helpers.FilesData;
|
||||
import org.mian.gitnex.helpers.ssl.MemorizingTrustManager;
|
||||
import retrofit2.Retrofit;
|
||||
import retrofit2.converter.gson.GsonConverterFactory;
|
||||
import retrofit2.converter.scalars.ScalarsConverterFactory;
|
||||
|
||||
/**
|
||||
* @author mmarif
|
||||
*/
|
||||
public class ApiRetrofitClient {
|
||||
|
||||
private static final Map<String, ApiInterface> instances = new ConcurrentHashMap<>();
|
||||
private static final int CACHE_SIZE_MB = 50;
|
||||
private static final int MAX_STALE_SECONDS = 60 * 60 * 24 * 30;
|
||||
|
||||
public static ApiInterface getInstance(Context context) {
|
||||
if (!(context instanceof BaseActivity)) return null;
|
||||
var account = ((BaseActivity) context).getAccount().getAccount();
|
||||
String url = account.getInstanceUrl();
|
||||
String token = ((BaseActivity) context).getAccount().getAuthorization();
|
||||
File cacheFile = new File(context.getCacheDir(), "http-cache");
|
||||
|
||||
String key = token.hashCode() + "@" + url;
|
||||
return instances.computeIfAbsent(key, k -> createApi(context, url, token, cacheFile));
|
||||
}
|
||||
|
||||
private static ApiInterface createApi(
|
||||
Context context, String url, String token, File cacheFile) {
|
||||
OkHttpClient client = buildOkHttpClient(context, token, cacheFile);
|
||||
Retrofit retrofit =
|
||||
new Retrofit.Builder()
|
||||
.baseUrl(url)
|
||||
.client(client)
|
||||
.addConverterFactory(ScalarsConverterFactory.create())
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build();
|
||||
return retrofit.create(ApiInterface.class);
|
||||
}
|
||||
|
||||
private static OkHttpClient buildOkHttpClient(Context context, String token, File cacheFile) {
|
||||
try {
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
MemorizingTrustManager trustManager = new MemorizingTrustManager(context);
|
||||
sslContext.init(null, new X509TrustManager[] {trustManager}, new SecureRandom());
|
||||
|
||||
// HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
|
||||
// logging.setLevel(HttpLoggingInterceptor.Level.BODY);
|
||||
|
||||
OkHttpClient.Builder builder =
|
||||
new OkHttpClient.Builder()
|
||||
.sslSocketFactory(sslContext.getSocketFactory(), trustManager)
|
||||
// .addInterceptor(logging)
|
||||
.hostnameVerifier(
|
||||
trustManager.wrapHostnameVerifier(
|
||||
HttpsURLConnection.getDefaultHostnameVerifier()))
|
||||
.addInterceptor(userAgentInterceptor(context))
|
||||
.addInterceptor(authInterceptor(token));
|
||||
|
||||
if (cacheFile != null) {
|
||||
int cacheSize = getCacheSize(context);
|
||||
File cacheDir = new File(context.getCacheDir(), "http-cache");
|
||||
if (!cacheDir.exists()) cacheDir.mkdirs();
|
||||
builder.cache(new Cache(cacheDir, cacheSize))
|
||||
.addInterceptor(cacheInterceptor(context))
|
||||
.addNetworkInterceptor(networkInterceptor());
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Interceptor userAgentInterceptor(Context ctx) {
|
||||
return chain -> {
|
||||
Request req =
|
||||
chain.request()
|
||||
.newBuilder()
|
||||
.header(
|
||||
"User-Agent",
|
||||
"GitNex/"
|
||||
+ AppUtil.getAppVersion(ctx)
|
||||
+ " (Android "
|
||||
+ android.os.Build.VERSION.RELEASE
|
||||
+ ")")
|
||||
.build();
|
||||
return chain.proceed(req);
|
||||
};
|
||||
}
|
||||
|
||||
private static Interceptor authInterceptor(String token) {
|
||||
return chain ->
|
||||
chain.proceed(chain.request().newBuilder().header("Authorization", token).build());
|
||||
}
|
||||
|
||||
private static Interceptor cacheInterceptor(Context ctx) {
|
||||
return chain -> {
|
||||
Request req = chain.request();
|
||||
boolean hasNetwork = AppUtil.hasNetworkConnection(ctx);
|
||||
CacheControl control =
|
||||
hasNetwork
|
||||
? CacheControl.FORCE_NETWORK
|
||||
: new CacheControl.Builder()
|
||||
.onlyIfCached()
|
||||
.maxStale(MAX_STALE_SECONDS, TimeUnit.SECONDS)
|
||||
.build();
|
||||
return chain.proceed(req.newBuilder().cacheControl(control).build());
|
||||
};
|
||||
}
|
||||
|
||||
private static Interceptor networkInterceptor() {
|
||||
return chain -> {
|
||||
Response resp = chain.proceed(chain.request());
|
||||
if ("GET".equals(chain.request().method()) && resp.isSuccessful()) {
|
||||
return resp.newBuilder()
|
||||
.header(
|
||||
"Cache-Control",
|
||||
"public, only-if-cached, max-stale=" + MAX_STALE_SECONDS)
|
||||
.removeHeader("Pragma")
|
||||
.build();
|
||||
}
|
||||
return resp;
|
||||
};
|
||||
}
|
||||
|
||||
private static int getCacheSize(Context ctx) {
|
||||
try {
|
||||
return FilesData.returnOnlyNumberFileSize(
|
||||
AppDatabaseSettings.getSettingsValue(
|
||||
ctx, AppDatabaseSettings.APP_DATA_CACHE_SIZE_KEY))
|
||||
* 1024
|
||||
* 1024;
|
||||
} catch (Exception e) {
|
||||
return CACHE_SIZE_MB * 1024 * 1024;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.mian.gitnex.api.models.contents;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* @author mmarif
|
||||
*/
|
||||
public class Links {
|
||||
|
||||
@SerializedName("git")
|
||||
private String git;
|
||||
|
||||
@SerializedName("self")
|
||||
private String self;
|
||||
|
||||
@SerializedName("html")
|
||||
private String html;
|
||||
|
||||
public String getGit() {
|
||||
return git;
|
||||
}
|
||||
|
||||
public String getSelf() {
|
||||
return self;
|
||||
}
|
||||
|
||||
public String getHtml() {
|
||||
return html;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
package org.mian.gitnex.api.models.contents;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* @author mmarif
|
||||
*/
|
||||
public class RepoGetContentsList {
|
||||
|
||||
@SerializedName("last_commit_when")
|
||||
private String lastCommitWhen;
|
||||
|
||||
@SerializedName("last_committer_date")
|
||||
private Date lastCommitterDate;
|
||||
|
||||
@SerializedName("last_author_date")
|
||||
private Date lastAuthorDate;
|
||||
|
||||
@SerializedName("submodule_git_url")
|
||||
private String submoduleGitUrl;
|
||||
|
||||
@SerializedName("_links")
|
||||
private Links links;
|
||||
|
||||
@SerializedName("last_commit_sha")
|
||||
private String lastCommitSha;
|
||||
|
||||
@SerializedName("type")
|
||||
private String type;
|
||||
|
||||
@SerializedName("encoding")
|
||||
private String encoding;
|
||||
|
||||
@SerializedName("sha")
|
||||
private String sha;
|
||||
|
||||
@SerializedName("content")
|
||||
private String content;
|
||||
|
||||
@SerializedName("url")
|
||||
private String url;
|
||||
|
||||
@SerializedName("target")
|
||||
private String target;
|
||||
|
||||
@SerializedName("path")
|
||||
private String path;
|
||||
|
||||
@SerializedName("size")
|
||||
private int size;
|
||||
|
||||
@SerializedName("html_url")
|
||||
private String htmlUrl;
|
||||
|
||||
@SerializedName("name")
|
||||
private String name;
|
||||
|
||||
@SerializedName("download_url")
|
||||
private String downloadUrl;
|
||||
|
||||
@SerializedName("git_url")
|
||||
private String gitUrl;
|
||||
|
||||
public String getLastCommitWhen() {
|
||||
return lastCommitWhen;
|
||||
}
|
||||
|
||||
public Date getLastCommiterDate() {
|
||||
return lastCommitterDate;
|
||||
}
|
||||
|
||||
public Date getLastAuthorDate() {
|
||||
return lastAuthorDate;
|
||||
}
|
||||
|
||||
public String getSubmoduleGitUrl() {
|
||||
return submoduleGitUrl;
|
||||
}
|
||||
|
||||
public Links getLinks() {
|
||||
return links;
|
||||
}
|
||||
|
||||
public String getLastCommitSha() {
|
||||
return lastCommitSha;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getEncoding() {
|
||||
return encoding;
|
||||
}
|
||||
|
||||
public String getSha() {
|
||||
return sha;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public String getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public String getHtmlUrl() {
|
||||
return htmlUrl;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getDownloadUrl() {
|
||||
return downloadUrl;
|
||||
}
|
||||
|
||||
public String getGitUrl() {
|
||||
return gitUrl;
|
||||
}
|
||||
|
||||
public Date getCompatibleCommitDate() {
|
||||
// First try Gitea field
|
||||
if (lastCommitterDate != null) {
|
||||
return lastCommitterDate;
|
||||
}
|
||||
|
||||
// Then try Forgejo field
|
||||
if (lastCommitWhen != null) {
|
||||
return parseForgejoDateString(lastCommitWhen);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Date parseForgejoDateString(String dateString) {
|
||||
if (dateString == null) return null;
|
||||
|
||||
String[] dateFormats = {
|
||||
"yyyy-MM-dd'T'HH:mm:ssXXX",
|
||||
"yyyy-MM-dd'T'HH:mm:ss'Z'",
|
||||
"yyyy-MM-dd'T'HH:mm:ss.SSSXXX",
|
||||
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
|
||||
};
|
||||
|
||||
for (String format : dateFormats) {
|
||||
try {
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat(format, Locale.getDefault());
|
||||
dateFormat.setLenient(false);
|
||||
return dateFormat.parse(dateString);
|
||||
} catch (ParseException e) {
|
||||
// Try next format
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user