diff --git a/app/build.gradle b/app/build.gradle index 7c8d8e959..2f6ce5b93 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,6 +4,10 @@ apply plugin: 'com.zeroturnaround.jrebel.android' apply plugin: 'com.neenbedankt.android-apt' apply plugin: 'me.tatarka.retrolambda' +retrolambda { + jvmArgs '-noverify' +} + android { compileSdkVersion 23 buildToolsVersion "23.0.1" @@ -45,7 +49,7 @@ android { } dependencies { - final SUPPORT_LIBRARY_VERSION = '23.0.1' + final SUPPORT_LIBRARY_VERSION = '23.1.0' final DAGGER_VERSION = '2.0.1' final HAMCREST_VERSION = '1.3' final MOCKITO_VERSION = '1.10.19' diff --git a/app/src/main/java/eu/kanade/mangafeed/data/helpers/PreferencesHelper.java b/app/src/main/java/eu/kanade/mangafeed/data/helpers/PreferencesHelper.java index d2944441e..7006ea780 100644 --- a/app/src/main/java/eu/kanade/mangafeed/data/helpers/PreferencesHelper.java +++ b/app/src/main/java/eu/kanade/mangafeed/data/helpers/PreferencesHelper.java @@ -5,30 +5,52 @@ import android.content.SharedPreferences; import android.preference.PreferenceManager; import eu.kanade.mangafeed.R; +import eu.kanade.mangafeed.sources.Source; public class PreferencesHelper { private static SharedPreferences mPref; + private Context context; - private static final String PREF_HIDE_STATUS_BAR = "hide_status_bar"; - private static final String PREF_DEFAULT_VIEWER = "default_viewer"; + private static final String SOURCE_ACCOUNT_USERNAME = "pref_source_username_"; + private static final String SOURCE_ACCOUNT_PASSWORD = "pref_source_password_"; public PreferencesHelper(Context context) { - PreferenceManager.setDefaultValues(context, R.xml.preferences, false); + this.context = context; + PreferenceManager.setDefaultValues(context, R.xml.pref_reader, false); mPref = PreferenceManager.getDefaultSharedPreferences(context); } + private String getKey(int keyResource) { + return context.getString(keyResource); + } + public void clear() { mPref.edit().clear().apply(); } public boolean hideStatusBarSet() { - return mPref.getBoolean(PREF_HIDE_STATUS_BAR, false); + return mPref.getBoolean(getKey(R.string.pref_hide_status_bar_key), false); } public int getDefaultViewer() { - return Integer.parseInt(mPref.getString(PREF_DEFAULT_VIEWER, "1")); + return Integer.parseInt(mPref.getString(getKey(R.string.pref_default_viewer_key), "1")); + } + + public String getSourceUsername(Source source) { + return mPref.getString(SOURCE_ACCOUNT_USERNAME + source.getSourceId(), ""); + } + + public String getSourcePassword(Source source) { + return mPref.getString(SOURCE_ACCOUNT_PASSWORD + source.getSourceId(), ""); + } + + public void setSourceCredentials(Source source, String username, String password) { + mPref.edit() + .putString(SOURCE_ACCOUNT_USERNAME + source.getSourceId(), username) + .putString(SOURCE_ACCOUNT_PASSWORD + source.getSourceId(), password) + .apply(); } } diff --git a/app/src/main/java/eu/kanade/mangafeed/injection/component/AppComponent.java b/app/src/main/java/eu/kanade/mangafeed/injection/component/AppComponent.java index b6f48e1b2..259798c58 100644 --- a/app/src/main/java/eu/kanade/mangafeed/injection/component/AppComponent.java +++ b/app/src/main/java/eu/kanade/mangafeed/injection/component/AppComponent.java @@ -15,6 +15,7 @@ import eu.kanade.mangafeed.presenter.MangaInfoPresenter; import eu.kanade.mangafeed.presenter.ReaderPresenter; import eu.kanade.mangafeed.presenter.SourcePresenter; import eu.kanade.mangafeed.ui.activity.ReaderActivity; +import eu.kanade.mangafeed.ui.fragment.SettingsAccountsFragment; @Singleton @Component( @@ -34,6 +35,7 @@ public interface AppComponent { void inject(ReaderPresenter readerPresenter); void inject(ReaderActivity readerActivity); + void inject(SettingsAccountsFragment settingsAccountsFragment); Application application(); diff --git a/app/src/main/java/eu/kanade/mangafeed/sources/Batoto.java b/app/src/main/java/eu/kanade/mangafeed/sources/Batoto.java index 3c591f5e0..98752c12e 100644 --- a/app/src/main/java/eu/kanade/mangafeed/sources/Batoto.java +++ b/app/src/main/java/eu/kanade/mangafeed/sources/Batoto.java @@ -98,6 +98,11 @@ public class Batoto extends Source { return SourceManager.BATOTO; } + @Override + public boolean isLoginRequired() { + return true; + } + @Override protected String getUrlFromPageNumber(int page) { return INITIAL_UPDATE_URL + page; diff --git a/app/src/main/java/eu/kanade/mangafeed/sources/Source.java b/app/src/main/java/eu/kanade/mangafeed/sources/Source.java index 6e4876654..2204eaf11 100644 --- a/app/src/main/java/eu/kanade/mangafeed/sources/Source.java +++ b/app/src/main/java/eu/kanade/mangafeed/sources/Source.java @@ -33,6 +33,11 @@ public abstract class Source { protected abstract List parseHtmlToPageUrls(String unparsedHtml); protected abstract String parseHtmlToImageUrl(String unparsedHtml); + // True if the source requires a login + public boolean isLoginRequired() { + return false; + } + // Get the URL to the details of a manga, useful if the source provides some kind of API or fast calls protected String getMangaUrl(String defaultMangaUrl) { return defaultMangaUrl; diff --git a/app/src/main/java/eu/kanade/mangafeed/ui/activity/SettingsActivity.java b/app/src/main/java/eu/kanade/mangafeed/ui/activity/SettingsActivity.java index 90da9a262..5e7e3c751 100644 --- a/app/src/main/java/eu/kanade/mangafeed/ui/activity/SettingsActivity.java +++ b/app/src/main/java/eu/kanade/mangafeed/ui/activity/SettingsActivity.java @@ -1,13 +1,14 @@ package eu.kanade.mangafeed.ui.activity; import android.os.Bundle; -import android.preference.PreferenceFragment; import android.support.v7.widget.Toolbar; +import android.view.MenuItem; import butterknife.Bind; import butterknife.ButterKnife; import eu.kanade.mangafeed.R; import eu.kanade.mangafeed.ui.activity.base.BaseActivity; +import eu.kanade.mangafeed.ui.fragment.SettingsMainFragment; public class SettingsActivity extends BaseActivity { @@ -21,16 +22,25 @@ public class SettingsActivity extends BaseActivity { setupToolbar(toolbar); - getFragmentManager().beginTransaction().replace(R.id.settings_content, - new MyPreferenceFragment()).commit(); + if (savedInstanceState == null) + getFragmentManager().beginTransaction().replace(R.id.settings_content, + new SettingsMainFragment()) + .commit(); } - public static class MyPreferenceFragment extends PreferenceFragment { - @Override - public void onCreate(final Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.preferences); + @Override + public void onBackPressed() { + if( !getFragmentManager().popBackStackImmediate() ) super.onBackPressed(); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + onBackPressed(); + return true; } + return super.onOptionsItemSelected(item); } } diff --git a/app/src/main/java/eu/kanade/mangafeed/ui/fragment/SettingsAccountsFragment.java b/app/src/main/java/eu/kanade/mangafeed/ui/fragment/SettingsAccountsFragment.java new file mode 100644 index 000000000..bf0739ac5 --- /dev/null +++ b/app/src/main/java/eu/kanade/mangafeed/ui/fragment/SettingsAccountsFragment.java @@ -0,0 +1,120 @@ +package eu.kanade.mangafeed.ui.fragment; + +import android.content.Context; +import android.os.Bundle; +import android.preference.DialogPreference; +import android.preference.PreferenceFragment; +import android.preference.PreferenceScreen; +import android.text.method.PasswordTransformationMethod; +import android.util.AttributeSet; +import android.view.View; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.TextView; + +import java.util.List; + +import javax.inject.Inject; + +import butterknife.Bind; +import butterknife.ButterKnife; +import eu.kanade.mangafeed.App; +import eu.kanade.mangafeed.R; +import eu.kanade.mangafeed.data.helpers.PreferencesHelper; +import eu.kanade.mangafeed.data.helpers.SourceManager; +import eu.kanade.mangafeed.sources.Source; +import eu.kanade.mangafeed.ui.activity.base.BaseActivity; +import rx.Observable; + +public class SettingsAccountsFragment extends PreferenceFragment { + + @Inject SourceManager sourceManager; + @Inject PreferencesHelper preferences; + + public static SettingsAccountsFragment newInstance() { + return new SettingsAccountsFragment(); + } + + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + App.get(getActivity()).getComponent().inject(this); + + addPreferencesFromResource(R.xml.pref_accounts); + + PreferenceScreen screen = getPreferenceScreen(); + + List sourceAccounts = getSourcesWithLogin(); + + for (Source source : sourceAccounts) { + LoginDialogPreference dialog = new LoginDialogPreference( + screen.getContext(), null, source); + dialog.setTitle(source.getName()); + + screen.addPreference(dialog); + } + } + + @Override + public void onResume() { + super.onResume(); + ((BaseActivity)getActivity()) + .setToolbarTitle(getString(R.string.pref_category_accounts)); + } + + private List getSourcesWithLogin() { + return Observable.from(sourceManager.getSources()) + .filter(Source::isLoginRequired) + .toList() + .toBlocking() + .single(); + } + + public class LoginDialogPreference extends DialogPreference { + + @Bind(R.id.accounts_login) TextView title; + @Bind(R.id.username) EditText username; + @Bind(R.id.password) EditText password; + @Bind(R.id.show_password) CheckBox showPassword; + + private Source source; + + public LoginDialogPreference(Context context, AttributeSet attrs, Source source) { + super(context, attrs); + this.source = source; + + setDialogLayoutResource(R.layout.pref_account_login); + } + + @Override + protected void onBindDialogView(View view) { + ButterKnife.bind(this, view); + + title.setText(getString(R.string.accounts_login_title, source.getName())); + + username.setText(preferences.getSourceUsername(source)); + password.setText(preferences.getSourcePassword(source)); + showPassword.setOnCheckedChangeListener((buttonView, isChecked) -> { + if (isChecked) + password.setTransformationMethod(null); + else + password.setTransformationMethod(new PasswordTransformationMethod()); + }); + + super.onBindDialogView(view); + } + + @Override + protected void onDialogClosed(boolean positiveResult) { + if(!positiveResult) + return; + + preferences.setSourceCredentials(source, + username.getText().toString(), + password.getText().toString()); + + super.onDialogClosed(true); + } + + } + +} diff --git a/app/src/main/java/eu/kanade/mangafeed/ui/fragment/SettingsMainFragment.java b/app/src/main/java/eu/kanade/mangafeed/ui/fragment/SettingsMainFragment.java new file mode 100644 index 000000000..381d33687 --- /dev/null +++ b/app/src/main/java/eu/kanade/mangafeed/ui/fragment/SettingsMainFragment.java @@ -0,0 +1,41 @@ +package eu.kanade.mangafeed.ui.fragment; + +import android.os.Bundle; +import android.preference.PreferenceFragment; + +import eu.kanade.mangafeed.R; +import eu.kanade.mangafeed.ui.activity.base.BaseActivity; + +public class SettingsMainFragment extends PreferenceFragment { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.pref_main); + + registerSubpreference(R.string.pref_category_reader_key, + SettingsNestedFragment.newInstance( + R.xml.pref_reader, R.string.pref_category_reader)); + + registerSubpreference(R.string.pref_category_accounts_key, + SettingsAccountsFragment.newInstance()); + } + + @Override + public void onResume() { + super.onResume(); + ((BaseActivity)getActivity()) + .setToolbarTitle(getString(R.string.settings_title)); + } + + private void registerSubpreference(int preferenceResource, PreferenceFragment fragment) { + findPreference(getString(preferenceResource)) + .setOnPreferenceClickListener(preference -> { + getFragmentManager().beginTransaction() + .replace(R.id.settings_content, fragment) + .addToBackStack(fragment.getClass().getSimpleName()).commit(); + return true; + }); + } + +} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/mangafeed/ui/fragment/SettingsNestedFragment.java b/app/src/main/java/eu/kanade/mangafeed/ui/fragment/SettingsNestedFragment.java new file mode 100644 index 000000000..cb4642993 --- /dev/null +++ b/app/src/main/java/eu/kanade/mangafeed/ui/fragment/SettingsNestedFragment.java @@ -0,0 +1,36 @@ +package eu.kanade.mangafeed.ui.fragment; + +import android.os.Bundle; +import android.preference.PreferenceFragment; + +import eu.kanade.mangafeed.ui.activity.base.BaseActivity; + +public class SettingsNestedFragment extends PreferenceFragment { + + private static final String RESOURCE_FILE = "resource_file"; + private static final String TOOLBAR_TITLE = "toolbar_title"; + + public static SettingsNestedFragment newInstance(int resourcePreference, int resourceTitle) { + SettingsNestedFragment fragment = new SettingsNestedFragment(); + Bundle args = new Bundle(); + args.putInt(RESOURCE_FILE, resourcePreference); + args.putInt(TOOLBAR_TITLE, resourceTitle); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(getArguments().getInt(RESOURCE_FILE)); + + } + + @Override + public void onResume() { + super.onResume(); + ((BaseActivity)getActivity()) + .setToolbarTitle(getString(getArguments().getInt(TOOLBAR_TITLE))); + } + +} \ No newline at end of file diff --git a/app/src/main/res/layout/pref_account_login.xml b/app/src/main/res/layout/pref_account_login.xml new file mode 100644 index 000000000..9409d80ef --- /dev/null +++ b/app/src/main/res/layout/pref_account_login.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/keys.xml b/app/src/main/res/values/keys.xml new file mode 100644 index 000000000..00eab40e4 --- /dev/null +++ b/app/src/main/res/values/keys.xml @@ -0,0 +1,7 @@ + + + pref_category_reader_key + pref_category_accounts_key + pref_hide_status_bar_key + pref_default_viewer_key + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e3e4c59d6..68c48a4ed 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -46,13 +46,25 @@ Info Chapters ViewerActivity + + + Settings + Reader + Accounts + Hide status bar This option will hide the status bar while reading + Default viewer Left to right Right to left Vertical Webtoon (experimental) + Login for %1$s + Username + Password + Show password + diff --git a/app/src/main/res/xml/pref_accounts.xml b/app/src/main/res/xml/pref_accounts.xml new file mode 100644 index 000000000..70056be23 --- /dev/null +++ b/app/src/main/res/xml/pref_accounts.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/pref_main.xml b/app/src/main/res/xml/pref_main.xml new file mode 100644 index 000000000..ef8d2e490 --- /dev/null +++ b/app/src/main/res/xml/pref_main.xml @@ -0,0 +1,14 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/pref_reader.xml similarity index 69% rename from app/src/main/res/xml/preferences.xml rename to app/src/main/res/xml/pref_reader.xml index fd93c50f3..84787446b 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/pref_reader.xml @@ -2,12 +2,12 @@ + android:summary="@string/pref_hide_status_bar_summary" />