Initial commit

This commit is contained in:
inorichi
2015-09-24 17:27:43 +02:00
commit b69510e972
57 changed files with 1965 additions and 0 deletions

View File

@ -0,0 +1,34 @@
package eu.kanade.mangafeed;
import android.app.Application;
import android.content.Context;
import timber.log.Timber;
public class App extends Application {
AppComponent mApplicationComponent;
@Override
public void onCreate() {
super.onCreate();
if (BuildConfig.DEBUG) Timber.plant(new Timber.DebugTree());
mApplicationComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this))
.build();
}
public static App get(Context context) {
return (App) context.getApplicationContext();
}
public AppComponent getComponent() {
return mApplicationComponent;
}
// Needed to replace the component with a test specific one
public void setComponent(AppComponent applicationComponent) {
mApplicationComponent = applicationComponent;
}
}

View File

@ -0,0 +1,23 @@
package eu.kanade.mangafeed;
import android.app.Application;
import javax.inject.Singleton;
import dagger.Component;
import eu.kanade.mangafeed.data.DataModule;
import eu.kanade.mangafeed.ui.activity.MainActivity;
@Singleton
@Component(
modules = {
AppModule.class,
DataModule.class
}
)
public interface AppComponent {
void inject(MainActivity mainActivity);
Application application();
}

View File

@ -0,0 +1,28 @@
package eu.kanade.mangafeed;
import android.app.Application;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
/**
* Provide application-level dependencies. Mainly singleton object that can be injected from
* anywhere in the app.
*/
@Module
public class AppModule {
protected final Application mApplication;
public AppModule(Application application) {
mApplication = application;
}
@Provides
@Singleton
Application provideApplication() {
return mApplication;
}
}

View File

@ -0,0 +1,38 @@
package eu.kanade.mangafeed.data;
import android.app.Application;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import eu.kanade.mangafeed.data.helpers.PreferencesHelper;
import eu.kanade.mangafeed.data.helpers.DatabaseHelper;
import rx.Scheduler;
import rx.schedulers.Schedulers;
/**
* Provide dependencies to the DataManager, mainly Helper classes and Retrofit services.
*/
@Module
public class DataModule {
@Provides
@Singleton
PreferencesHelper providePreferencesHelper(Application app) {
return new PreferencesHelper(app);
}
@Provides
@Singleton
DatabaseHelper provideDatabaseHelper(Application app) {
return new DatabaseHelper(app);
}
@Provides
@Singleton
Scheduler provideSubscribeScheduler() {
return Schedulers.io();
}
}

View File

@ -0,0 +1,198 @@
package eu.kanade.mangafeed.data.entities;
/**
* Created by len on 23/09/2015.
*/
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.pushtorefresh.storio.sqlite.annotations.StorIOSQLiteColumn;
import com.pushtorefresh.storio.sqlite.annotations.StorIOSQLiteType;
import eu.kanade.mangafeed.data.tables.MangasTable;
@StorIOSQLiteType(table = MangasTable.TABLE)
public class Manga {
@Nullable
@StorIOSQLiteColumn(name = MangasTable.COLUMN_ID, key = true)
Long id;
@NonNull
@StorIOSQLiteColumn(name = MangasTable.COLUMN_SOURCE)
int source;
@NonNull
@StorIOSQLiteColumn(name = MangasTable.COLUMN_URL)
String url;
@NonNull
@StorIOSQLiteColumn(name = MangasTable.COLUMN_ARTIST)
String artist;
@NonNull
@StorIOSQLiteColumn(name = MangasTable.COLUMN_AUTHOR)
String author;
@NonNull
@StorIOSQLiteColumn(name = MangasTable.COLUMN_DESCRIPTION)
String description;
@NonNull
@StorIOSQLiteColumn(name = MangasTable.COLUMN_GENRE)
String genre;
@NonNull
@StorIOSQLiteColumn(name = MangasTable.COLUMN_TITLE)
String title;
@NonNull
@StorIOSQLiteColumn(name = MangasTable.COLUMN_STATUS)
String status;
@NonNull
@StorIOSQLiteColumn(name = MangasTable.COLUMN_THUMBNAIL_URL)
String thumbnail_url;
@NonNull
@StorIOSQLiteColumn(name = MangasTable.COLUMN_RANK)
int rank;
@NonNull
@StorIOSQLiteColumn(name = MangasTable.COLUMN_LAST_UPDATE)
long last_update;
@NonNull
@StorIOSQLiteColumn(name = MangasTable.COLUMN_INITIALIZED)
boolean initialized;
@NonNull
@StorIOSQLiteColumn(name = MangasTable.COLUMN_VIEWER)
int viewer;
@NonNull
@StorIOSQLiteColumn(name = MangasTable.COLUMN_CHAPTER_ORDER)
int chapter_order;
Manga() {}
@Nullable
public Long id() {
return id;
}
@NonNull
public int source() {
return source;
}
@NonNull
public String url() {
return url;
}
@NonNull
public String artist() {
return artist;
}
@NonNull
public String author() {
return author;
}
@NonNull
public String description() {
return description;
}
@NonNull
public String genre() {
return genre;
}
@NonNull
public String title() {
return title;
}
@NonNull
public String status() {
return status;
}
@NonNull
public String thumbnail_url() {
return thumbnail_url;
}
@NonNull
public int rank() {
return rank;
}
@NonNull
public long last_update() {
return last_update;
}
@NonNull
public boolean nitialized() {
return initialized;
}
@NonNull
public int viewer() {
return viewer;
}
@NonNull
public int chapter_order() {
return chapter_order;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Manga manga = (Manga) o;
if (source != manga.source) return false;
if (rank != manga.rank) return false;
if (last_update != manga.last_update) return false;
if (initialized != manga.initialized) return false;
if (viewer != manga.viewer) return false;
if (chapter_order != manga.chapter_order) return false;
if (id != null ? !id.equals(manga.id) : manga.id != null) return false;
if (!url.equals(manga.url)) return false;
if (!artist.equals(manga.artist)) return false;
if (!author.equals(manga.author)) return false;
if (!description.equals(manga.description)) return false;
if (!genre.equals(manga.genre)) return false;
if (!title.equals(manga.title)) return false;
if (!status.equals(manga.status)) return false;
return thumbnail_url.equals(manga.thumbnail_url);
}
@Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + source;
result = 31 * result + url.hashCode();
result = 31 * result + artist.hashCode();
result = 31 * result + author.hashCode();
result = 31 * result + description.hashCode();
result = 31 * result + genre.hashCode();
result = 31 * result + title.hashCode();
result = 31 * result + status.hashCode();
result = 31 * result + thumbnail_url.hashCode();
result = 31 * result + rank;
result = 31 * result + (int) (last_update ^ (last_update >>> 32));
result = 31 * result + (initialized ? 1 : 0);
result = 31 * result + viewer;
result = 31 * result + chapter_order;
return result;
}
}

View File

@ -0,0 +1,43 @@
package eu.kanade.mangafeed.data.helpers;
import android.content.Context;
import com.pushtorefresh.storio.sqlite.StorIOSQLite;
import com.pushtorefresh.storio.sqlite.impl.DefaultStorIOSQLite;
import com.pushtorefresh.storio.sqlite.queries.Query;
import java.util.List;
import eu.kanade.mangafeed.data.entities.Manga;
import eu.kanade.mangafeed.data.tables.MangasTable;
import rx.Observable;
/**
* Created by len on 23/09/2015.
*/
public class DatabaseHelper {
private StorIOSQLite db;
public DatabaseHelper(Context context) {
db = DefaultStorIOSQLite.builder()
.sqliteOpenHelper(new DbOpenHelper(context))
.build();
}
public StorIOSQLite getStorIODb() {
return db;
}
public Observable<List<Manga>> getMangas() {
return db.get()
.listOfObjects(Manga.class)
.withQuery(Query.builder()
.table(MangasTable.TABLE)
.build())
.prepare()
.createObservable();
}
}

View File

@ -0,0 +1,31 @@
package eu.kanade.mangafeed.data.helpers;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.support.annotation.NonNull;
import eu.kanade.mangafeed.data.tables.MangasTable;
/**
* Created by len on 23/09/2015.
*/
public class DbOpenHelper extends SQLiteOpenHelper {
public static final String DATABASE_NAME = "mangafeed.db";
public static final int DATABASE_VERSION = 1;
public DbOpenHelper(@NonNull Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(@NonNull SQLiteDatabase db) {
db.execSQL(MangasTable.getCreateTableQuery());
}
@Override
public void onUpgrade(@NonNull SQLiteDatabase db, int oldVersion, int newVersion) {
// no impl
}
}

View File

@ -0,0 +1,21 @@
package eu.kanade.mangafeed.data.helpers;
import android.content.Context;
import android.content.SharedPreferences;
public class PreferencesHelper {
private static SharedPreferences mPref;
public static final String PREF_FILE_NAME = "android_boilerplate_pref_file";
public PreferencesHelper(Context context) {
mPref = context.getSharedPreferences(PREF_FILE_NAME, Context.MODE_PRIVATE);
}
public void clear() {
mPref.edit().clear().apply();
}
}

View File

@ -0,0 +1,18 @@
package eu.kanade.mangafeed.data.tables;
import android.support.annotation.NonNull;
/**
* Created by len on 23/09/2015.
*/
public class CategoriesTable {
@NonNull
public static final String TABLE = "categories";
@NonNull
public static final String COLUMN_ID = "_id";
@NonNull
public static final String COLUMN_NAME = "name";
}

View File

@ -0,0 +1,30 @@
package eu.kanade.mangafeed.data.tables;
import android.support.annotation.NonNull;
/**
* Created by len on 23/09/2015.
*/
public class ChaptersTable {
@NonNull
public static final String TABLE = "chapters";
@NonNull
public static final String COLUMN_ID = "_id";
@NonNull
public static final String COLUMN_MANGA_ID = "manga_id";
@NonNull
public static final String COLUMN_URL = "url";
@NonNull
public static final String COLUMN_NAME = "name";
@NonNull
public static final String COLUMN_READ = "read";
@NonNull
public static final String COLUMN_DATE_FETCH = "date_fetch";
}

View File

@ -0,0 +1,18 @@
package eu.kanade.mangafeed.data.tables;
import android.support.annotation.NonNull;
/**
* Created by len on 23/09/2015.
*/
public class MangasCategoriesTable {
@NonNull
public static final String TABLE = "mangas_categories";
@NonNull
public static final String COLUMN_MANGA_ID = "_manga_id";
@NonNull
public static final String COLUMN_CATEGORY_ID = "_category_id";
}

View File

@ -0,0 +1,85 @@
package eu.kanade.mangafeed.data.tables;
import android.support.annotation.NonNull;
/**
* Created by len on 23/09/2015.
*/
public class MangasTable {
@NonNull
public static final String TABLE = "mangas";
@NonNull
public static final String COLUMN_ID = "_id";
@NonNull
public static final String COLUMN_SOURCE = "source";
@NonNull
public static final String COLUMN_URL = "url";
@NonNull
public static final String COLUMN_ARTIST = "artist";
@NonNull
public static final String COLUMN_AUTHOR = "author" ;
@NonNull
public static final String COLUMN_DESCRIPTION = "description";
@NonNull
public static final String COLUMN_GENRE = "genre";
@NonNull
public static final String COLUMN_TITLE = "title";
@NonNull
public static final String COLUMN_STATUS = "status";
@NonNull
public static final String COLUMN_THUMBNAIL_URL = "thumbnail_url";
@NonNull
public static final String COLUMN_RANK = "rank";
@NonNull
public static final String COLUMN_LAST_UPDATE = "last_update";
@NonNull
public static final String COLUMN_INITIALIZED = "initialized";
@NonNull
public static final String COLUMN_VIEWER = "viewer";
@NonNull
public static final String COLUMN_CHAPTER_ORDER = "chapter_order";
// This is just class with Meta Data, we don't need instances
private MangasTable() {
throw new IllegalStateException("No instances please");
}
// Better than static final field -> allows VM to unload useless String
// Because you need this string only once per application life on the device
@NonNull
public static String getCreateTableQuery() {
return "CREATE TABLE " + TABLE + "("
+ COLUMN_ID + " INTEGER NOT NULL PRIMARY KEY, "
+ COLUMN_SOURCE + " INTEGER NOT NULL, "
+ COLUMN_URL + " TEXT NOT NULL, "
+ COLUMN_ARTIST + " TEXT NOT NULL, "
+ COLUMN_AUTHOR + " TEXT NOT NULL, "
+ COLUMN_DESCRIPTION + " TEXT NOT NULL, "
+ COLUMN_GENRE + " TEXT NOT NULL, "
+ COLUMN_TITLE + " TEXT NOT NULL, "
+ COLUMN_STATUS + " TEXT NOT NULL, "
+ COLUMN_THUMBNAIL_URL + " TEXT NOT NULL, "
+ COLUMN_RANK + " INTEGER NOT NULL, "
+ COLUMN_LAST_UPDATE + " LONG NOT NULL, "
+ COLUMN_INITIALIZED + " BOOLEAN NOT NULL, "
+ COLUMN_VIEWER + " INTEGER NOT NULL, "
+ COLUMN_CHAPTER_ORDER + " INTEGER NOT NULL"
+ ");";
}
}

View File

@ -0,0 +1,32 @@
package eu.kanade.mangafeed.ui.activity;
import android.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;
import eu.kanade.mangafeed.App;
import eu.kanade.mangafeed.AppComponent;
public class BaseActivity extends AppCompatActivity {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
FragmentManager fm = getFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
} else {
finish();
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
protected AppComponent applicationComponent() {
return App.get(this).getComponent();
}
}

View File

@ -0,0 +1,85 @@
package eu.kanade.mangafeed.ui.activity;
import android.os.Bundle;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ProgressBar;
import javax.inject.Inject;
import eu.kanade.mangafeed.R;
import butterknife.Bind;
import butterknife.ButterKnife;
import eu.kanade.mangafeed.data.helpers.DatabaseHelper;
import rx.subscriptions.CompositeSubscription;
import timber.log.Timber;
import uk.co.ribot.easyadapter.EasyRecyclerAdapter;
public class MainActivity extends BaseActivity {
@Bind(R.id.recycler_characters)
RecyclerView mCharactersRecycler;
@Bind(R.id.toolbar)
Toolbar mToolbar;
@Bind(R.id.progress_indicator)
ProgressBar mProgressBar;
@Bind(R.id.swipe_container)
SwipeRefreshLayout mSwipeRefresh;
@Inject DatabaseHelper mDb;
private CompositeSubscription mSubscriptions;
private EasyRecyclerAdapter<Character> mEasyRecycleAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
applicationComponent().inject(this);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
mSubscriptions = new CompositeSubscription();
//mDataManager = App.get(this).getComponent().dataManager();
setupToolbar();
setupRecyclerView();
}
@Override
protected void onDestroy() {
super.onDestroy();
mSubscriptions.unsubscribe();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_github:
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void setupToolbar() {
setSupportActionBar(mToolbar);
}
private void setupRecyclerView() {
mCharactersRecycler.setLayoutManager(new LinearLayoutManager(this));
mCharactersRecycler.setAdapter(mEasyRecycleAdapter);
mSwipeRefresh.setColorSchemeResources(R.color.primary);
}
}

View File

@ -0,0 +1,37 @@
package eu.kanade.mangafeed.ui.adapter;
import android.content.Intent;
import android.net.Uri;
import android.view.View;
import android.widget.TextView;
import uk.co.ribot.easyadapter.ItemViewHolder;
import uk.co.ribot.easyadapter.PositionInfo;
import uk.co.ribot.easyadapter.annotations.LayoutId;
import uk.co.ribot.easyadapter.annotations.ViewId;
@LayoutId(eu.kanade.mangafeed.R.layout.item_detail)
public class DetailHolder extends ItemViewHolder<String> {
@ViewId(eu.kanade.mangafeed.R.id.text_detail)
TextView mDetailText;
public DetailHolder(View view) {
super(view);
}
@Override
public void onSetValues(String item, PositionInfo positionInfo) {
mDetailText.setText(item);
}
@Override
public void onSetListeners() {
mDetailText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(getItem())));
}
});
}
}

View File

@ -0,0 +1,33 @@
package eu.kanade.mangafeed.util;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import timber.log.Timber;
public class AndroidComponentUtil {
public static void toggleComponent(Context context, Class componentClass, boolean enable) {
Timber.i((enable ? "Enabling " : "Disabling ") + componentClass.getSimpleName());
ComponentName componentName = new ComponentName(context, componentClass);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(componentName,
enable ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
}
public static boolean isServiceRunning(Context context, Class serviceClass) {
ActivityManager manager =
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,13 @@
package eu.kanade.mangafeed.util;
import android.content.Context;
import android.net.ConnectivityManager;
public class DataUtils {
public static boolean isNetworkAvailable(Context context) {
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
return connectivityManager.getActiveNetworkInfo() != null;
}
}

View File

@ -0,0 +1,39 @@
package eu.kanade.mangafeed.util;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.support.annotation.StringRes;
import android.support.v7.app.AlertDialog;
import eu.kanade.mangafeed.R;
public class DialogFactory {
public static Dialog createSimpleOkErrorDialog(Context context, String title, String message) {
AlertDialog.Builder alertDialog = new AlertDialog.Builder(context)
.setTitle(title)
.setMessage(message)
.setNeutralButton(R.string.dialog_action_ok, null);
return alertDialog.create();
}
public static Dialog createSimpleErrorDialog(Context context) {
AlertDialog.Builder alertDialog = new AlertDialog.Builder(context)
.setTitle(context.getString(R.string.dialog_error_title))
.setMessage(context.getString(R.string.dialog_general_error_Message))
.setNeutralButton(R.string.dialog_action_ok, null);
return alertDialog.create();
}
public static ProgressDialog createProgressDialog(Context context, String message) {
ProgressDialog progressDialog = new ProgressDialog(context);
progressDialog.setMessage(message);
return progressDialog;
}
public static ProgressDialog createProgressDialog(Context context, @StringRes int messageResoruce) {
return createProgressDialog(context, context.getString(messageResoruce));
}
}

View File

@ -0,0 +1,27 @@
package eu.kanade.mangafeed.util;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import retrofit.HttpException;
public class NetworkUtil {
/**
* Returns true if the Throwable is an instance of RetrofitError with an
* http status code equals to the given one.
*/
public static boolean isHttpStatusCode(Throwable throwable, int statusCode) {
return throwable instanceof HttpException
&& ((HttpException) throwable).code() == statusCode;
}
public static boolean isNetworkConnected(Context context) {
ConnectivityManager cm =
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
}
}

View File

@ -0,0 +1,18 @@
package eu.kanade.mangafeed.util;
import android.content.Context;
import android.support.design.widget.Snackbar;
import android.view.View;
import android.view.ViewGroup;
import eu.kanade.mangafeed.R;
public class SnackbarFactory {
public static Snackbar createSnackbar(Context context, View view, String message) {
Snackbar snackbar = Snackbar.make(view, message, Snackbar.LENGTH_SHORT);
ViewGroup group = (ViewGroup) snackbar.getView();
group.setBackgroundColor(context.getResources().getColor(R.color.primary));
return snackbar;
}
}

View File

@ -0,0 +1,13 @@
package eu.kanade.mangafeed.util;
import android.content.Context;
import android.util.DisplayMetrics;
public class ViewUtils {
public static float convertPixelsToDp(float px, Context context){
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return px / (metrics.densityDpi / 160f);
}
}