Skip to content
Permalink
Browse files
Fixes #116: Reuse OkHttpClient Instance (#123)
* Provide OkHttpClient, Retrofit and Gson instances from Dagger

* Separate APIs and response types into their own classes

* Minor refactor missed in #100
  • Loading branch information
bobheadxi committed Aug 12, 2017
1 parent 187ac00 commit 7f5d41f6d16fb445b139cad034ca4d312c7ab320
Showing with 307 additions and 271 deletions.
  1. +4 −2 app/src/main/java/subreddit/android/appstore/AppComponent.java
  2. +35 −0 app/src/main/java/subreddit/android/appstore/backend/HttpModule.java
  3. +38 −0 app/src/main/java/subreddit/android/appstore/backend/github/GithubApi.java
  4. +2 −24 app/src/main/java/subreddit/android/appstore/backend/github/GithubRepository.java
  5. +6 −38 app/src/main/java/subreddit/android/appstore/backend/github/LiveGithubRepository.java
  6. +0 −19 app/src/main/java/subreddit/android/appstore/backend/reddit/Token.java
  7. +35 −0 app/src/main/java/subreddit/android/appstore/backend/reddit/TokenApi.java
  8. +14 −54 app/src/main/java/subreddit/android/appstore/backend/reddit/TokenRepository.java
  9. +3 −85 app/src/main/java/subreddit/android/appstore/backend/reddit/wiki/LiveWikiRepository.java
  10. +66 −0 app/src/main/java/subreddit/android/appstore/backend/reddit/wiki/WikiApi.java
  11. +4 −3 app/src/main/java/subreddit/android/appstore/backend/scrapers/LiveMediaScraper.java
  12. +6 −6 app/src/main/java/subreddit/android/appstore/backend/scrapers/gplay/GPlayScraper.java
  13. +6 −6 app/src/main/java/subreddit/android/appstore/screens/navigation/NavigationContract.java
  14. +4 −4 app/src/main/java/subreddit/android/appstore/screens/navigation/NavigationFragment.java
  15. +6 −5 app/src/main/java/subreddit/android/appstore/screens/navigation/NavigationPresenter.java
  16. +3 −2 app/src/main/java/subreddit/android/appstore/screens/settings/AboutActivity.java
  17. +1 −2 app/src/main/java/subreddit/android/appstore/util/ui/glide/IconRequestModelLoader.java
  18. +7 −7 app/src/mock/java/subreddit/android/appstore/backend/github/FakeGithubRepository.java
  19. +22 −4 app/src/prod/java/subreddit/android/appstore/backend/github/GithubRepositoryModule.java
  20. +42 −8 app/src/prod/java/subreddit/android/appstore/backend/reddit/wiki/WikiRepositoryModule.java
  21. +3 −2 app/src/prod/java/subreddit/android/appstore/backend/scrapers/ScraperModule.java
@@ -9,6 +9,7 @@
import subreddit.android.appstore.backend.reddit.wiki.WikiRepositoryModule;
import subreddit.android.appstore.backend.scrapers.MediaScraper;
import subreddit.android.appstore.backend.scrapers.ScraperModule;
import subreddit.android.appstore.backend.HttpModule;
import subreddit.android.appstore.util.dagger.ApplicationScope;


@@ -17,7 +18,8 @@
AndroidModule.class,
WikiRepositoryModule.class,
ScraperModule.class,
GithubRepositoryModule.class
GithubRepositoryModule.class,
HttpModule.class
})
public interface AppComponent {
SharedPreferences providePreferences();
@@ -26,5 +28,5 @@

MediaScraper mediaScraper();

GithubRepository selfUpdater();
GithubRepository githubRepository();
}
@@ -0,0 +1,35 @@
package subreddit.android.appstore.backend;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import dagger.Module;
import dagger.Provides;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import subreddit.android.appstore.BuildConfig;
import subreddit.android.appstore.util.dagger.ApplicationScope;

@Module
public class HttpModule {

@Provides
@ApplicationScope
Gson provideGson() {
return new GsonBuilder().create();
}

@Provides
@ApplicationScope
OkHttpClient provideOkHttpClient(UserAgentInterceptor userAgent) {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
builder.addInterceptor(interceptor);
}
builder.addInterceptor(userAgent);
return builder.build();
}

}
@@ -0,0 +1,38 @@
package subreddit.android.appstore.backend.github;

import com.google.gson.annotations.SerializedName;

import java.util.Date;
import java.util.List;

import io.reactivex.Observable;
import retrofit2.http.GET;

public interface GithubApi {
String BASEURL = "https://api.github.com/";

@GET("repos/d4rken/reddit-android-appstore/releases/latest")
Observable<Release> getLatestRelease();

@GET("repos/d4rken/reddit-android-appstore/contributors")
Observable<List<Contributor>> getContributors();

class Release {
@SerializedName("url") public String releaseUrl;
@SerializedName("tag_name") public String tagName;
@SerializedName("name") public String releaseName;
@SerializedName("body") public String releaseDescription;
public boolean prerelease;
@SerializedName("published_at") public Date publishDate;
public List<Assets> assets;

public static class Assets {
@SerializedName("browser_download_url") public String downloadUrl;
public long size;
}
}

class Contributor {
@SerializedName("login") public String username;
}
}
@@ -1,34 +1,12 @@
package subreddit.android.appstore.backend.github;

import com.google.gson.annotations.SerializedName;

import java.util.Date;
import java.util.List;

import io.reactivex.Observable;

public interface GithubRepository {

Observable<Release> getLatestRelease();
Observable<List<Contributor>> getContributors();

class Release {
@SerializedName("url") public String releaseUrl;
@SerializedName("tag_name") public String tagName;
@SerializedName("name") public String releaseName;
@SerializedName("body") public String releaseDescription;
public boolean prerelease;
@SerializedName("published_at") public Date publishDate;
public List<Assets> assets;

public static class Assets {
@SerializedName("browser_download_url") public String downloadUrl;
public long size;
}
}

class Contributor {
@SerializedName("login") public String username;
}
Observable<GithubApi.Release> getLatestRelease();
Observable<List<GithubApi.Contributor>> getContributors();

}
@@ -1,58 +1,26 @@
package subreddit.android.appstore.backend.github;

import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;

import java.util.List;

import io.reactivex.Observable;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.http.GET;
import subreddit.android.appstore.BuildConfig;
import subreddit.android.appstore.backend.UserAgentInterceptor;

public class LiveGithubRepository implements GithubRepository {
private static final String BASEURL = "https://api.github.com/";
private final GithubApi githubApi;
private Observable<Release> latestReleaseCache;
private Observable<List<Contributor>> latestContributorsCache;

public LiveGithubRepository(UserAgentInterceptor userAgent) {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
builder.addInterceptor(interceptor);
}
builder.addInterceptor(userAgent);
OkHttpClient client = builder.build();
Retrofit retrofit = new Retrofit.Builder()
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(BASEURL)
.build();
githubApi = retrofit.create(GithubApi.class);
}

interface GithubApi {
@GET("repos/d4rken/reddit-android-appstore/releases/latest")
Observable<Release> getLatestRelease();
private Observable<GithubApi.Release> latestReleaseCache;
private Observable<List<GithubApi.Contributor>> latestContributorsCache;

@GET("repos/d4rken/reddit-android-appstore/contributors")
Observable<List<Contributor>> getContributors();
public LiveGithubRepository(GithubApi githubApi) {
this.githubApi = githubApi;
}

@Override
public Observable<Release> getLatestRelease() {
public Observable<GithubApi.Release> getLatestRelease() {
if (latestReleaseCache == null) latestReleaseCache = githubApi.getLatestRelease().cache();
return latestReleaseCache;
}

@Override
public Observable<List<Contributor>> getContributors() {
public Observable<List<GithubApi.Contributor>> getContributors() {
if (latestContributorsCache == null) latestContributorsCache = githubApi.getContributors().cache();
return latestContributorsCache;
}

This file was deleted.

@@ -0,0 +1,35 @@
package subreddit.android.appstore.backend.reddit;


import io.reactivex.Observable;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.Header;
import retrofit2.http.POST;

public interface TokenApi {
String BASEURL = "https://www.reddit.com/";

@FormUrlEncoded
@POST("api/v1/access_token")
Observable<TokenApi.Token> getUserlessAuthToken(@Header("Authorization") String authentication,
@Field("device_id") String deviceId,
@Field("grant_type") String grant_type,
@Field("scope") String scope);

class Token {
String access_token;
String token_type;
long expires_in;
String scope;
final long issuedTime = System.currentTimeMillis();

public boolean isExpired() {
return System.currentTimeMillis() > issuedTime + expires_in * 1000;
}

public String getAuthorizationString() {
return token_type + " " + access_token;
}
}
}
@@ -8,30 +8,14 @@
import android.util.Base64;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.Header;
import retrofit2.http.POST;
import subreddit.android.appstore.BuildConfig;
import subreddit.android.appstore.backend.DeviceIdentifier;
import subreddit.android.appstore.backend.UserAgentInterceptor;
import timber.log.Timber;

public class TokenRepository {
private static final String BASEURL = "https://www.reddit.com/";
private static final String CLIENT_ID = "8i-tKlCSV9P_fQ";
private static final String PREF_KEY = "reddit.token.userlessauth";
final Context context;
@@ -41,38 +25,25 @@
private final Gson gson;
private final SharedPreferences preferences;

public TokenRepository(Context context, DeviceIdentifier deviceIdentifier, UserAgentInterceptor userAgent) {
public TokenRepository(Context context,
DeviceIdentifier deviceIdentifier,
TokenApi tokenApi,
Gson gson) {
this.context = context;
this.deviceIdentifier = deviceIdentifier;

OkHttpClient.Builder builder = new OkHttpClient.Builder();
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
builder.addInterceptor(interceptor);
}
builder.addInterceptor(userAgent);
OkHttpClient client = builder.build();
Retrofit retrofit = new Retrofit.Builder()
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl(BASEURL)
.build();
tokenApi = retrofit.create(TokenApi.class);
this.gson = gson;
this.tokenApi = tokenApi;

String credentials = CLIENT_ID + ":";
encodedCredentials = "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);

gson = new GsonBuilder().create();

preferences = PreferenceManager.getDefaultSharedPreferences(context);
}

public Observable<Token> getUserlessAuthToken() {
return Observable.<Token>create(
public Observable<TokenApi.Token> getUserlessAuthToken() {
return Observable.<TokenApi.Token>create(
emitter -> {
Token token = getToken();
TokenApi.Token token = getToken();
if (token != null) emitter.onNext(token);
emitter.onComplete();
})
@@ -88,31 +59,20 @@ public TokenRepository(Context context, DeviceIdentifier deviceIdentifier, UserA
}

@Nullable
Token getToken() {
Token token = null;
TokenApi.Token getToken() {
TokenApi.Token token = null;
try {
token = gson.fromJson(preferences.getString(PREF_KEY, null), Token.class);
token = gson.fromJson(preferences.getString(PREF_KEY, null), TokenApi.Token.class);
} catch (JsonSyntaxException ignore) {}
if (token != null && token.isExpired()) {
Timber.d("Token expired!");
Timber.d("TokenApi expired!");
return null;
}
return token;
}

void storeToken(@NonNull Token token) {
void storeToken(@NonNull TokenApi.Token token) {
preferences.edit().putString(PREF_KEY, gson.toJson(token)).apply();
}

interface TokenApi {
@FormUrlEncoded
@POST("api/v1/access_token")
Observable<Token> getUserlessAuthToken(
@Header("Authorization") String authentication,
@Field("device_id") String deviceId,
@Field("grant_type") String grant_type,
@Field("scope") String scope
);
}

}

0 comments on commit 7f5d41f

Please sign in to comment.