diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..15b02d1 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,18 @@ +language: android +android: + components: + # Uncomment the lines below if you want to + # use the latest revision of Android SDK Tools + - platform-tools + - tools + + # The BuildTools version used by your project + - build-tools-23.0.2 + + # The SDK version used to compile your project + - android-23 + + # Additional components + - extra-google-google_play_services + - extra-google-m2repository + - extra-android-m2repository \ No newline at end of file diff --git a/README.md b/README.md index 2ae6ebf..b94b158 100644 --- a/README.md +++ b/README.md @@ -48,10 +48,12 @@ http://bookdash.org/ /parse_data_example/ If you want to download the actual books, you need to individually download each book and import the files into the parse manually. See the information here: https://www.parse.com/questions/exporting-file-objects-and-importing-to-another-application -4. Change the Parse API Key and Client Key in the file to your unique parse key: /appconfig-sample.properties . +4. Change the Parse API Key and Client Key in the file to your unique parse key: /gradle.properties . 5. Setup a Fabric Account. https://fabric.io/dashboard -6. Get your Fabric API Key and Client key, change it in the file: /app/fabric-sample.properties -7. If you wish to build a release version you will need to create your own keystore file and edit the password values in the following file - (create a version of the file without the .sample extension): release-keystore.properties.sample +6. Get your Fabric API Key and Client key, change it in the file: /app/fabric-sample.properties and rename the file to fabric.properties +7. Go generate a google-services.json file by following the instructions here: https://developers.google.com/mobile/add?platform=android +You will need to select App Invites API and Google Analytics and enter your SHA-1 of your debug keystore. +8. If you wish to build a release version you will need to create your own keystore file and edit the password values in the following file - (create a version of the file without the .sample extension): release-keystore.properties.sample # License Copyright 2015 Book Dash. diff --git a/app/build.gradle b/app/build.gradle index d9f0250..71601ba 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -17,34 +17,40 @@ repositories { } -def appConfig = new Properties() - -appConfig.load(new FileInputStream(rootProject.file("appconfig.properties"))) android { compileSdkVersion 23 - buildToolsVersion "23.0.1" + buildToolsVersion "23.0.2" defaultConfig { applicationId "org.bookdash.android" minSdkVersion 16 targetSdkVersion 23 - versionCode 11 - versionName "1.0.11" + versionCode 12 + versionName "1.0.12" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + buildConfigField "String", "PARSE_APPLICATION_ID", "\"${BOOKDASH_PARSE_APP_ID}\"" + buildConfigField "String", "PARSE_CLIENT_KEY", "\"${BOOKDASH_PARSE_CLIENT_ID}\"" + } - signingConfigs { - release { - def props = new Properties() + File signFile = rootProject.file('release-keystore.properties') + if (signFile.exists()) { + signingConfigs { + release { + def props = new Properties() - props.load(new FileInputStream(rootProject.file("release-keystore.properties"))) + props.load(new FileInputStream(signFile)) - storeFile rootProject.file(props.keyStore) - storePassword props.keyStorePassword - keyAlias props.keyAlias - keyPassword props.keyAliasPassword + storeFile rootProject.file(props.keyStore) + storePassword props.keyStorePassword + keyAlias props.keyAlias + keyPassword props.keyAliasPassword + } } + buildTypes.release.signingConfig signingConfigs.release + } + packagingOptions { exclude 'META-INF/ASL2.0' exclude 'META-INF/LICENSE' @@ -61,7 +67,6 @@ android { productFlavors { mock { - applicationIdSuffix = ".mock" } prod { @@ -84,16 +89,11 @@ android { buildTypes { release { shrinkResources false - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),'proguard-project.txt' - signingConfig signingConfigs.release - buildConfigField "String", "PARSE_APPLICATION_ID", appConfig.PARSE_APPLICATION_ID - buildConfigField "String", "PARSE_CLIENT_KEY", appConfig.PARSE_CLIENT_KEY + minifyEnabled true + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),'proguard-rules.pro' ext.enableCrashlytics = true } debug { - buildConfigField "String", "PARSE_APPLICATION_ID", appConfig.PARSE_APPLICATION_ID - buildConfigField "String", "PARSE_CLIENT_KEY", appConfig.PARSE_CLIENT_KEY ext.enableCrashlytics = false // Run code coverage reports by default on debug builds. testCoverageEnabled = true @@ -141,7 +141,6 @@ dependencies { exclude module: 'support-annotations' exclude module: 'support-v4' } - compile 'com.google.android.gms:play-services-analytics:8.3.0' compile fileTree(dir: 'libs', include: ['*.jar']) compile project(':fabbutton') @@ -162,5 +161,8 @@ dependencies { transitive = true; } compile 'za.co.riggaroo:materialhelptutorial:1.0.3' - compile 'com.google.android.gms:play-services-appindexing:8.3.0' + compile "com.google.android.gms:play-services-appinvite:$rootProject.ext.googlePlayServicesVersion" + compile "com.google.android.gms:play-services-appindexing:$rootProject.ext.googlePlayServicesVersion" + compile "com.android.support:design:$rootProject.ext.googlePlayServicesVersion" + compile "com.google.android.gms:play-services-analytics:$rootProject.ext.googlePlayServicesVersion" } diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index a8b8aeb..ecf03c4 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -15,5 +15,41 @@ #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} +-ignorewarnings + -keep class me.zhanghai.android.materialprogressbar.** { *; } --keep class com.joanzapata.** { *; } \ No newline at end of file +-keep class com.joanzapata.** { *; } +-keep class com.parse.** { *; } + + + + +-keep public class * implements com.bumptech.glide.module.GlideModule +-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** { + **[] $VALUES; + public *; +} + +-keep class com.crashlytics.** { *; } +-keep class com.crashlytics.android.** +-keepattributes SourceFile,LineNumberTable + +-keepattributes Signature +-keepattributes InnerClass +-keep class com.squareup.okhttp.** {*;} +-keep class za.co.riggaroo.materialhelptutorial.view.** { *;} +-keep class mbanje.kurt.fabbutton.** {*;} +-keep class com.google.android.gms.** {*;} +-keep class android.support.v7.** {*;} + +-keep class android.support.design.** {*;} +-keep class android.support.v4.** {*;} +# For using GSON @Expose annotation +-keepattributes *Annotation* + +# Gson specific classes +-keep class sun.misc.Unsafe { *; } +#-keep class com.google.gson.stream.** { *; } + +# Application classes that will be serialized/deserialized over Gson +-keep class org.bookdash.android.domain.pojo.gson.** { *; } \ No newline at end of file diff --git a/app/src/androidTest/java/org/bookdash/android/presentation/about/AboutActivityTest.java b/app/src/androidTest/java/org/bookdash/android/presentation/about/AboutActivityTest.java index 76f095c..d24eff9 100644 --- a/app/src/androidTest/java/org/bookdash/android/presentation/about/AboutActivityTest.java +++ b/app/src/androidTest/java/org/bookdash/android/presentation/about/AboutActivityTest.java @@ -11,6 +11,8 @@ import android.text.Html; import android.view.View; +import junit.framework.Assert; + import org.bookdash.android.R; import org.junit.After; import org.junit.Before; @@ -71,11 +73,15 @@ public void loadAboutBookDash_SeeInformation() throws Throwable { @Test public void clickLearnMore_OpenBrowser() throws Throwable { - onView(withText("LEARN MORE")).perform(scrollTo(),click()); + onView(withText(R.string.learn_more)).perform(scrollTo(), click()); intended(allOf(hasAction(Intent.ACTION_VIEW), hasData(Uri.parse("http://bookdash.org")) ) ); } + @Test + public void testGetScreenName(){ + Assert.assertEquals("About Screen",testRule.getActivity().getScreenName()); + } } diff --git a/app/src/androidTest/java/org/bookdash/android/presentation/listbooks/OverflowMenuOptionsTest.java b/app/src/androidTest/java/org/bookdash/android/presentation/listbooks/OverflowMenuOptionsTest.java index 8b29386..6907aa5 100644 --- a/app/src/androidTest/java/org/bookdash/android/presentation/listbooks/OverflowMenuOptionsTest.java +++ b/app/src/androidTest/java/org/bookdash/android/presentation/listbooks/OverflowMenuOptionsTest.java @@ -2,6 +2,7 @@ import android.content.Intent; import android.net.Uri; +import android.support.design.widget.NavigationView; import android.support.test.InstrumentationRegistry; import android.support.test.espresso.intent.Intents; import android.support.test.rule.ActivityTestRule; @@ -28,6 +29,7 @@ import static android.support.test.espresso.intent.matcher.IntentMatchers.hasData; import static android.support.test.espresso.matcher.RootMatchers.isDialog; import static android.support.test.espresso.matcher.RootMatchers.*; +import static android.support.test.espresso.matcher.ViewMatchers.isClickable; import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription; import static android.support.test.espresso.matcher.ViewMatchers.withId; @@ -61,31 +63,27 @@ public void tearDown() { Intents.release(); } + @Test + public void languageItemClick_ShowLanguageChooser() { + onView(withId(R.id.action_language_choice)).perform(click()); + //Then + onView(withText(R.string.language_selection_heading)).inRoot(isDialog()).check(matches(isDisplayed())); + } @Test public void aboutMenuClick_ShowAboutBookDashScreen() { - //Given - openActionBarOverflowOrOptionsMenu(InstrumentationRegistry.getTargetContext()); - //When - String title = InstrumentationRegistry.getTargetContext().getString(R.string.action_about); - onView(withText(title)).perform(click()); - + selectNavDrawItem(R.id.action_about); //Then intended(hasComponent(AboutActivity.class.getName())); } - @Test public void rateThisAppClick_ShowPlayStoreDetail() { //Given - openActionBarOverflowOrOptionsMenu(InstrumentationRegistry.getTargetContext()); - //When - String title = InstrumentationRegistry.getTargetContext().getString(R.string.rate_this_app); - onView(withText(title)).perform(click()); - + selectNavDrawItem(R.id.action_rate_app); //Then intended(allOf(hasAction(Intent.ACTION_VIEW), hasData(Uri.parse("market://details?id=" + org.bookdash.android.BuildConfig.APPLICATION_ID)) @@ -95,13 +93,9 @@ public void rateThisAppClick_ShowPlayStoreDetail() { @Test public void contributorsClicked_ShowThanksPopover() { - //Given - openActionBarOverflowOrOptionsMenu(InstrumentationRegistry.getTargetContext()); //When - String title = InstrumentationRegistry.getTargetContext().getString(R.string.settings_thank_yous); - onView(withText(title)).perform(click()); - + selectNavDrawItem(R.id.action_thanks); //Then onView(withText("Contributors")).inRoot(isDialog()).check(matches(isDisplayed())); @@ -109,15 +103,25 @@ public void contributorsClicked_ShowThanksPopover() { @Test public void contributorsOkClick_HideThanksPopover() { - //Given - openActionBarOverflowOrOptionsMenu(InstrumentationRegistry.getTargetContext()); - //When - String title = InstrumentationRegistry.getTargetContext().getString(R.string.settings_thank_yous); - onView(withText(title)).perform(click()); + selectNavDrawItem(R.id.action_thanks); //Then onView(withText(android.R.string.ok)).perform(click()); } + + // Due to the NavigationItem not being exposed, we have to do this work around to test NavigationView + // https://code.google.com/p/android/issues/detail?id=187701 + public void selectNavDrawItem(final int navItemId){ + onView(allOf(withContentDescription(containsString("Navigate up")), isClickable())).perform(click()); + final NavigationView navigation =(NavigationView) mActivityTestRule.getActivity().findViewById(R.id.navigation_view); + mActivityTestRule.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + navigation.getMenu().performIdentifierAction(navItemId, 0); + navigation.setCheckedItem(navItemId); + } + }); + } } diff --git a/app/src/androidTestMock/java/org.bookdash.android/presentation/bookinfo/BookInfoActivityTest.java b/app/src/androidTestMock/java/org.bookdash.android/presentation/bookinfo/BookInfoActivityTest.java index 92892e8..f8f3420 100644 --- a/app/src/androidTestMock/java/org.bookdash.android/presentation/bookinfo/BookInfoActivityTest.java +++ b/app/src/androidTestMock/java/org.bookdash.android/presentation/bookinfo/BookInfoActivityTest.java @@ -39,7 +39,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.bookdash.android.mock.test.BuildConfig; +import org.bookdash.android.test.BuildConfig; import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom; diff --git a/app/src/androidTestMock/java/org.bookdash.android/presentation/listbooks/ListBooksActivityTest.java b/app/src/androidTestMock/java/org.bookdash.android/presentation/listbooks/ListBooksActivityTest.java index b96a276..03eea6a 100644 --- a/app/src/androidTestMock/java/org.bookdash.android/presentation/listbooks/ListBooksActivityTest.java +++ b/app/src/androidTestMock/java/org.bookdash.android/presentation/listbooks/ListBooksActivityTest.java @@ -3,6 +3,7 @@ import android.content.Intent; import android.net.Uri; import android.os.Build; +import android.support.design.widget.NavigationView; import android.support.test.InstrumentationRegistry; import android.support.test.espresso.ViewInteraction; import android.support.test.espresso.intent.Intents; @@ -13,6 +14,8 @@ import android.test.suitebuilder.annotation.LargeTest; import android.view.MenuItem; +import junit.framework.Assert; + import org.bookdash.android.R; import org.bookdash.android.data.settings.FakeSettingsApiImpl; import org.bookdash.android.presentation.about.AboutActivity; @@ -25,7 +28,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.bookdash.android.mock.test.BuildConfig; +import org.bookdash.android.test.BuildConfig; import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist; @@ -38,7 +41,9 @@ import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent; import static android.support.test.espresso.intent.matcher.IntentMatchers.hasData; import static android.support.test.espresso.matcher.RootMatchers.isDialog; +import static android.support.test.espresso.matcher.ViewMatchers.isClickable; import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; +import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription; import static android.support.test.espresso.matcher.ViewMatchers.withId; import static android.support.test.espresso.matcher.ViewMatchers.withText; import static android.support.test.espresso.action.ViewActions.click; @@ -102,11 +107,11 @@ public void openScreen_AppBarTitleIsCorrect() { matchToolbarTitle("Book Dash"); } + @Test public void chooseDifferentLanguage_NewBooksLoaded(){ - openActionBarOverflowOrOptionsMenu(InstrumentationRegistry.getTargetContext()); - String title = InstrumentationRegistry.getTargetContext().getString(R.string.action_language_choice); - onView(withText(title)).perform(click()); + // selectNavDrawItem(R.id.action_language_choice); + onView(withId(R.id.action_language_choice)).perform(click()); //When onView(withText("Zulu")).perform(click()); @@ -118,6 +123,10 @@ public void chooseDifferentLanguage_NewBooksLoaded(){ onView(withText("Why is Nita Upside Down?")).check(doesNotExist()); } + @Test + public void testGetScreenName_IsBookListing(){ + Assert.assertEquals("BookListingScreen", activityTestRule.getActivity().getScreenName()); + } diff --git a/app/src/androidTestMock/java/org.bookdash.android/presentation/listbooks/ListBooksFailActivityTest.java b/app/src/androidTestMock/java/org.bookdash.android/presentation/listbooks/ListBooksFailActivityTest.java index ed1022d..ef42c54 100644 --- a/app/src/androidTestMock/java/org.bookdash.android/presentation/listbooks/ListBooksFailActivityTest.java +++ b/app/src/androidTestMock/java/org.bookdash.android/presentation/listbooks/ListBooksFailActivityTest.java @@ -42,7 +42,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.bookdash.android.mock.test.BuildConfig; +import org.bookdash.android.test.BuildConfig; import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom; diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6cb5448..cac59ee 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -47,6 +47,24 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/org/bookdash/android/BookDashApplication.java b/app/src/main/java/org/bookdash/android/BookDashApplication.java index 4c314fc..28666ce 100644 --- a/app/src/main/java/org/bookdash/android/BookDashApplication.java +++ b/app/src/main/java/org/bookdash/android/BookDashApplication.java @@ -28,7 +28,7 @@ public class BookDashApplication extends Application { public static boolean isTablet = false; public static String FILES_DIR; - // private Tracker mTracker; + private Tracker mTracker; @Override public void onCreate() { @@ -59,7 +59,7 @@ public void done(ParseException e) { }); isTablet = getResources().getBoolean(R.bool.is_tablet); FILES_DIR = getFilesDir().getPath(); - // getDefaultTracker().enableAutoActivityTracking(true); + getDefaultTracker().enableAutoActivityTracking(true); } @@ -67,12 +67,12 @@ public void done(ParseException e) { * Gets the default {@link Tracker} for this {@link Application}. * @return tracker */ - /* synchronized public Tracker getDefaultTracker() { + synchronized public Tracker getDefaultTracker() { if (mTracker == null) { GoogleAnalytics analytics = GoogleAnalytics.getInstance(this); // To enable debug logging use: adb shell setprop log.tag.GAv4 DEBUG mTracker = analytics.newTracker(R.xml.global_tracker); } return mTracker; - }*/ + } } diff --git a/app/src/main/java/org/bookdash/android/data/books/BookDetailRepository.java b/app/src/main/java/org/bookdash/android/data/books/BookDetailRepository.java index ed74446..3717b1a 100644 --- a/app/src/main/java/org/bookdash/android/data/books/BookDetailRepository.java +++ b/app/src/main/java/org/bookdash/android/data/books/BookDetailRepository.java @@ -16,7 +16,7 @@ */ public interface BookDetailRepository { - void getBooksForLanguage(String language, @NonNull GetBooksForLanguageCallback booksForLanguageCallback); + void getBooksForLanguage(String language, boolean downloadedOnly, @NonNull GetBooksForLanguageCallback booksForLanguageCallback); interface GetBooksForLanguageCallback { void onBooksLoaded(List books); diff --git a/app/src/main/java/org/bookdash/android/data/books/BookDetailRepositoryImpl.java b/app/src/main/java/org/bookdash/android/data/books/BookDetailRepositoryImpl.java index 83325c4..6bd32d6 100644 --- a/app/src/main/java/org/bookdash/android/data/books/BookDetailRepositoryImpl.java +++ b/app/src/main/java/org/bookdash/android/data/books/BookDetailRepositoryImpl.java @@ -8,10 +8,12 @@ import org.bookdash.android.domain.pojo.Language; import org.bookdash.android.domain.pojo.gson.BookPages; +import java.util.ArrayList; import java.util.List; /** * Can add caching here or fetching from db instead of from service + * * @author rebeccafranks * @since 15/11/03. */ @@ -19,17 +21,21 @@ public class BookDetailRepositoryImpl implements BookDetailRepository { private final BookDetailApi bookDetailApi; - public BookDetailRepositoryImpl(@NonNull BookDetailApi bookDetailApi){ + public BookDetailRepositoryImpl(@NonNull BookDetailApi bookDetailApi) { this.bookDetailApi = bookDetailApi; } @Override - public void getBooksForLanguage(@NonNull String language, @NonNull final GetBooksForLanguageCallback booksForLanguageCallback) { + public void getBooksForLanguage(@NonNull String language, final boolean downloadedOnly, @NonNull final GetBooksForLanguageCallback booksForLanguageCallback) { bookDetailApi.getBooksForLanguages(language, new BookDetailApi.BookServiceCallback>() { @Override public void onLoaded(List result) { - booksForLanguageCallback.onBooksLoaded(result); + if (downloadedOnly) { + booksForLanguageCallback.onBooksLoaded(filterOnlyDownloadedBooks(result)); + } else { + booksForLanguageCallback.onBooksLoaded(result); + } } @Override @@ -39,6 +45,16 @@ public void onError(Exception error) { }); } + private List filterOnlyDownloadedBooks(List bookDetails){ + List bookDetailsDownloaded = new ArrayList<>(); + + for (BookDetail b: bookDetails){ + if (b.isDownloadedAlready() || b.isDownloading()){ + bookDetailsDownloaded.add(b); + } + } + return bookDetailsDownloaded; + } @Override public void getBookDetail(String bookDetailId, @NonNull final GetBookDetailCallback bookDetailCallback) { bookDetailApi.getBookDetail(bookDetailId, new BookDetailApi.BookServiceCallback() { @@ -70,7 +86,6 @@ public void onError(Exception error) { } - @Override public void getLanguages(@NonNull final GetLanguagesCallback languagesCallback) { bookDetailApi.getLanguages(new BookDetailApi.BookServiceCallback>() { diff --git a/app/src/main/java/org/bookdash/android/domain/pojo/BookDetail.java b/app/src/main/java/org/bookdash/android/domain/pojo/BookDetail.java index f51214e..aadedc9 100644 --- a/app/src/main/java/org/bookdash/android/domain/pojo/BookDetail.java +++ b/app/src/main/java/org/bookdash/android/domain/pojo/BookDetail.java @@ -26,6 +26,7 @@ public class BookDetail extends ParseObject { public static final String BOOK_ENABLED_COL = "book_enabled"; public static final String CREATED_AT_COL = "createdAt"; public static final String BOOK_INFO_FILE_NAME = "bookdetails.json"; + private static final String WEB_URL_COL = "book_website_link"; private boolean isDownloading = false; @@ -38,7 +39,8 @@ public BookDetail(String title, String bookCoverUrl, String objectId, Language l put(BOOK_TITLE_COL, title); put(BOOK_COVER_PAGE_URL_COL, bookCoverUrl); put(BOOK_LANGUAGE_COL, languageId); - put(OBJECT_ID, objectId); + setObjectId(objectId); + // put(OBJECT_ID, objectId); //put(); } @@ -66,9 +68,6 @@ public String getAboutBook() { return getString(ABOUT_BOOK_COL); } - public String getBookDetailId() { - return getString(OBJECT_ID); - } public String getFolderLocation(String filesDir) { return getFolderLocation(new File(filesDir, getObjectId() + File.separator)); @@ -100,6 +99,11 @@ public BookDetailParcelable toBookParcelable() { bookDetailParcelable.setBookTitle(getBookTitle()); bookDetailParcelable.setBookImageUrl(getBookCoverUrl()); bookDetailParcelable.setBookDetailObjectId(getObjectId()); + bookDetailParcelable.setWebUrl(getWebUrl()); return bookDetailParcelable; } + + public String getWebUrl() { + return getString(WEB_URL_COL); + } } \ No newline at end of file diff --git a/app/src/main/java/org/bookdash/android/domain/pojo/BookDetailParcelable.java b/app/src/main/java/org/bookdash/android/domain/pojo/BookDetailParcelable.java index f55ec5e..01cb3ad 100644 --- a/app/src/main/java/org/bookdash/android/domain/pojo/BookDetailParcelable.java +++ b/app/src/main/java/org/bookdash/android/domain/pojo/BookDetailParcelable.java @@ -33,6 +33,7 @@ public void setBookTitle(String bookTitle) { private String bookImageUrl; private String bookDownloadUrl; private String bookTitle; + private String webUrl; public String getBookDetailObjectId() { return bookDetailObjectId; @@ -55,6 +56,7 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeString(this.bookDownloadUrl); dest.writeString(this.bookTitle); dest.writeString(this.bookDetailObjectId); + dest.writeString(this.webUrl); } public BookDetailParcelable() { @@ -65,6 +67,7 @@ protected BookDetailParcelable(Parcel in) { this.bookDownloadUrl = in.readString(); this.bookTitle = in.readString(); this.bookDetailObjectId = in.readString(); + this.webUrl = in.readString(); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @@ -76,4 +79,12 @@ public BookDetailParcelable[] newArray(int size) { return new BookDetailParcelable[size]; } }; + + public String getWebUrl() { + return webUrl; + } + + public void setWebUrl(String webUrl) { + this.webUrl = webUrl; + } } diff --git a/app/src/main/java/org/bookdash/android/presentation/about/AboutPresenter.java b/app/src/main/java/org/bookdash/android/presentation/about/AboutPresenter.java index a586446..13aa4db 100644 --- a/app/src/main/java/org/bookdash/android/presentation/about/AboutPresenter.java +++ b/app/src/main/java/org/bookdash/android/presentation/about/AboutPresenter.java @@ -1,10 +1,5 @@ package org.bookdash.android.presentation.about; -import com.google.android.gms.analytics.HitBuilders; -import com.google.android.gms.analytics.Tracker; - -import org.bookdash.android.BookDashApplication; -import org.bookdash.android.data.utils.GAnalytics; /** * @author rebeccafranks diff --git a/app/src/main/java/org/bookdash/android/presentation/activity/BaseAppCompatActivity.java b/app/src/main/java/org/bookdash/android/presentation/activity/BaseAppCompatActivity.java index 3df5f8d..9e7d2d8 100644 --- a/app/src/main/java/org/bookdash/android/presentation/activity/BaseAppCompatActivity.java +++ b/app/src/main/java/org/bookdash/android/presentation/activity/BaseAppCompatActivity.java @@ -3,8 +3,6 @@ import android.os.Bundle; import android.support.v7.app.AppCompatActivity; -import com.google.android.gms.analytics.HitBuilders; -import com.google.android.gms.analytics.Tracker; import org.bookdash.android.BookDashApplication; @@ -15,7 +13,7 @@ public abstract class BaseAppCompatActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - BookDashApplication application = (BookDashApplication) getApplication(); +// BookDashApplication application = (BookDashApplication) getApplication(); /* tracker = application.getDefaultTracker(); tracker.setScreenName(getScreenName()); tracker.send(new HitBuilders.ScreenViewBuilder().build());*/ diff --git a/app/src/main/java/org/bookdash/android/presentation/bookinfo/BookInfoActivity.java b/app/src/main/java/org/bookdash/android/presentation/bookinfo/BookInfoActivity.java index ea28c79..4c1da09 100644 --- a/app/src/main/java/org/bookdash/android/presentation/bookinfo/BookInfoActivity.java +++ b/app/src/main/java/org/bookdash/android/presentation/bookinfo/BookInfoActivity.java @@ -8,6 +8,7 @@ import android.graphics.BitmapFactory; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.GradientDrawable; +import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; @@ -15,12 +16,15 @@ import android.support.design.widget.CollapsingToolbarLayout; import android.support.design.widget.CoordinatorLayout; import android.support.design.widget.Snackbar; +import android.support.v7.app.ActionBar; import android.support.v7.widget.CardView; import android.support.v7.widget.Toolbar; import android.transition.Transition; import android.util.DisplayMetrics; import android.util.Log; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.ViewTreeObserver; import android.view.Window; @@ -33,6 +37,9 @@ import android.widget.ProgressBar; import android.widget.TextView; +import com.google.android.gms.appindexing.Action; +import com.google.android.gms.appindexing.AppIndex; +import com.google.android.gms.common.api.GoogleApiClient; import com.parse.GetDataCallback; import com.parse.ParseException; import com.parse.ParseFile; @@ -78,27 +85,15 @@ public class BookInfoActivity extends BaseAppCompatActivity implements BookInfoC private ProgressBar loadingProgressBar; private CardView contributorCard, mainBookCard; private Toolbar toolbar; - private android.support.v7.app.ActionBar actionBar; + private ActionBar actionBar; private BookDetail bookInfo; - - /* private SharedElementCallback fabLoginSharedElementCallback = new SharedElementCallback() { - @Override - public Parcelable onCaptureSharedElementSnapshot(View sharedElement, - Matrix viewToGlobalMatrix, - RectF screenBounds) { - // store a snapshot of the fab to fade out when morphing to the login dialog - int bitmapWidth = Math.round(screenBounds.width()); - int bitmapHeight = Math.round(screenBounds.height()); - Bitmap bitmap = null; - if (bitmapWidth > 0 && bitmapHeight > 0) { - bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888); - sharedElement.draw(new Canvas(bitmap)); - } - return bitmap; - } - };*/ - - + private Action viewAction; + /** + * ATTENTION: This was auto-generated to implement the App Indexing API. + * See https://g.co/AppIndexing/AndroidStudio for more information. + */ + private GoogleApiClient client; + private Button errorRetryButton; @Override @@ -140,32 +135,13 @@ public void onTransitionResume(Transition transition) { imageViewBook = (ImageView) findViewById(R.id.image_view_book_cover); appBarLayout = (AppBarLayout) findViewById(R.id.app_bar_layout); mainBookCard = (CardView) findViewById(R.id.card_view_main_book_info); - final BookDetailParcelable bookDetailParcelable = getIntent().getParcelableExtra(BOOK_PARCEL); toolbar = (Toolbar) findViewById(R.id.toolbar); coordinatorLayout = (CoordinatorLayout) findViewById(R.id.coordinator_layout_content); errorLayout = findViewById(R.id.linear_layout_error); errorText = (TextView) findViewById(R.id.text_view_error_screen); - Button errorRetryButton = (Button) findViewById(R.id.button_retry); - loadingProgressBar = (ProgressBar) findViewById(R.id.activity_loading_book_info); - errorRetryButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - actionsListener.loadBookInformation(bookDetailParcelable.getBookDetailObjectId()); - } - }); - - binding.setVariable(BR.download_click, new View.OnClickListener() { - @Override - public void onClick(View v) { - if (bookInfo == null){ - showSnackBarMessage(R.string.book_not_available); - return; - } - floatingActionButton.showProgress(true); - actionsListener.downloadBook(bookInfo); - } - }); + errorRetryButton = (Button) findViewById(R.id.button_retry); + loadingProgressBar = (ProgressBar) findViewById(R.id.activity_loading_book_info); setSupportActionBar(toolbar); actionBar = getSupportActionBar(); if (actionBar != null) { @@ -177,72 +153,98 @@ public void onClick(View v) { floatingActionButton = (FabButton) findViewById(R.id.fab_download); floatingActionButton.setScaleX(0); floatingActionButton.setScaleY(0); - // floatingActionButton.setPivotX(0.5f); - //floatingActionButton.setPivotY(0.5f); - + binding.setVariable(BR.download_click, new View.OnClickListener() { + @Override + public void onClick(View v) { + if (bookInfo == null) { + showSnackBarMessage(R.string.book_not_available); + return; + } + if (!bookInfo.isDownloadedAlready()){ + floatingActionButton.resetIcon(); + floatingActionButton.showProgress(true); + floatingActionButton.setProgress(0, true); + } + actionsListener.downloadBook(bookInfo); + } + }); actionsListener = new BookInfoPresenter(this.getApplicationContext(), this, Injection.provideBookRepo()); - actionsListener.loadBookInformation(bookDetailParcelable.getBookDetailObjectId()); - - actionsListener.loadImage(bookDetailParcelable.getBookImageUrl()); calculateLayoutHeight(); imageViewBook.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver .OnPreDrawListener() { @Override public boolean onPreDraw() { imageViewBook.getViewTreeObserver().removeOnPreDrawListener(this); - /* if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - startPostponedEnterTransition(); - }else {*/ - enterAnimation(); - /* }*/ + enterAnimation(); return true; } }); - // showProgress(false); + final BookDetailParcelable bookDetailParcelable = getIntent().getParcelableExtra(BOOK_PARCEL); + + if (bookDetailParcelable != null) { + String bookDetailId = bookDetailParcelable.getBookDetailObjectId(); + startLoadingBook(bookDetailId); + actionsListener.loadImage(bookDetailParcelable.getBookImageUrl()); + } else { + onNewIntent(getIntent()); + } + client = new GoogleApiClient.Builder(this).addApi(AppIndex.API).build(); + + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_book_info, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + int id = item.getItemId(); + + if (id == R.id.action_share_book) { + actionsListener.shareBookClicked(bookInfo); + return true; + } + + return super.onOptionsItemSelected(item); + } + + private void startLoadingBook(final String bookDetailId) { + errorRetryButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + actionsListener.loadBookInformation(bookDetailId); + } + }); + + actionsListener.loadBookInformation(bookDetailId); showBookDetailView(); + } + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + String action = intent.getAction(); + String data = intent.getDataString(); + Log.d(TAG, "onNewIntent() called: action" + action); + + if (Intent.ACTION_VIEW.equals(action) && data != null) { + Uri uri = Uri.parse(data); + String bookId = uri.getLastPathSegment(); + String invitationId = uri.getQueryParameter("invitation_id"); + Log.d(TAG, "Action View: book id:" + bookId + ". Full URL:" + uri.toString() + ". InvitationId:" + invitationId); + startLoadingBook(bookId); + } } - /** - * Animate in the title, description and author – can't do this in a content transition as they - * are within the ListView so do it manually. Also handle the FAB tanslation here so that it - * plays nicely with #calculateFabPosition - */ private void enterAnimation() { - /* if (gradientBackground != null) { - gradientBackground.setAlpha(0); - ObjectAnimator anim = ObjectAnimator.ofFloat(gradientBackground, "alpha", 0f, 1f); - anim.setDuration(500); - anim.setStartDelay(50); - anim.setInterpolator(new DecelerateInterpolator()); - anim.start(); - }*/ + floatingActionButton.setScaleX(0); floatingActionButton.setScaleY(0); floatingActionButton.animate().setStartDelay(500).scaleY(1).scaleX(1).setInterpolator(new OvershootInterpolator()).setDuration(getResources().getInteger(android.R.integer.config_mediumAnimTime)).start(); - //mainBookCard.setTranslationY(mainBookCard.getHeight()); - //mainBookCard.animate().translationY(0).setInterpolator(new DecelerateInterpolator()).start(); - /* scrollView.setAlpha(0); - ObjectAnimator anim2 = ObjectAnimator.ofFloat(scrollView, "alpha", 0f, 1f); - anim2.setDuration(500); - anim2.setStartDelay(50); - anim2.setInterpolator(new DecelerateInterpolator()); - anim2.start(); - // contributorCard.setTranslationY(contributorCard.getHeight()); - // contributorCard.animate().translationY(0).setInterpolator(new DecelerateInterpolator()).start(); - contributorCard.setAlpha(0); - ObjectAnimator animContributor = ObjectAnimator.ofFloat(contributorCard, "alpha", 0f, 1f); - animContributor.setDuration(500); - animContributor.setStartDelay(50); - animContributor.setInterpolator(new DecelerateInterpolator()); - animContributor.start();*/ - - /* ObjectAnimator anim2 = ObjectAnimator.ofFloat(appBarLayout, "alpha", 0f, 1f); - anim2.setDuration(500); - anim2.setStartDelay(50); - anim2.setInterpolator(new DecelerateInterpolator()); - anim2.start();*/ + } @@ -298,12 +300,21 @@ public void showSnackBarMessage(int message) { Snackbar.make(scrollView, message, Snackbar.LENGTH_LONG).show(); } - + private int progress= 0; @Override - public void showDownloadProgress(int downloadProgress) { - + public void showDownloadProgress(final int downloadProgress) { + if (progress == downloadProgress){ + return; + } + progress = downloadProgress; Log.d(TAG, "Download progress:" + downloadProgress); - floatingActionButton.setProgress(downloadProgress, true); + + runOnUiThread(new Runnable() { + @Override + public void run() { + floatingActionButton.setProgress(downloadProgress, true); + } + }); } @@ -317,7 +328,7 @@ public void showDownloadFinished() { @Override public void setToolbarTitle(String title) { toolbar.setTitle(title); - android.support.v7.app.ActionBar actionBar = getSupportActionBar(); + ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.setTitle(title); } @@ -328,8 +339,22 @@ public void setToolbarTitle(String title) { @Override public void setBookInfoBinding(BookDetail bookInfo) { + if (client!=null) { + client.connect(); + } + this.bookInfo = bookInfo; binding.setVariable(BR.book_info, bookInfo); + actionsListener.loadImage(bookInfo.getBookCoverUrl()); + viewAction = Action.newAction( + Action.TYPE_VIEW, + bookInfo.getBookTitle(), + bookInfo.getWebUrl() == null ? null : Uri.parse(bookInfo.getWebUrl()), + Uri.parse("android-app://org.bookdash.android/http/bookdash.org/books/" + bookInfo.getObjectId()) + ); + if (viewAction != null && client!=null) { + AppIndex.AppIndexApi.start(client, viewAction); + } } @Override @@ -405,6 +430,15 @@ public void setToolbarColor(int color) { } } + @Override + public void sendShareEvent(String bookTitle) { + Intent sendIntent = new Intent(); + sendIntent.setAction(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_TEXT, getString(R.string.sharing_book_title, bookTitle)); + sendIntent.setType("text/plain"); + startActivity(sendIntent); + } + private void setContributorInfo(@NonNull View v, @NonNull BookContributor bookContributor) { TextView textViewContributor = (TextView) v.findViewById(R.id.textViewContributorName); TextView textViewRole = (TextView) v.findViewById(R.id.textViewRole); @@ -432,4 +466,21 @@ public void done(byte[] bytes, ParseException e) { }); } + @Override + public void onStart() { + super.onStart(); + + + } + + @Override + public void onStop() { + super.onStop(); + + if (viewAction != null) { + AppIndex.AppIndexApi.end(client, viewAction); + + } + client.disconnect(); + } } diff --git a/app/src/main/java/org/bookdash/android/presentation/bookinfo/BookInfoContract.java b/app/src/main/java/org/bookdash/android/presentation/bookinfo/BookInfoContract.java index 8e33f75..6120edb 100644 --- a/app/src/main/java/org/bookdash/android/presentation/bookinfo/BookInfoContract.java +++ b/app/src/main/java/org/bookdash/android/presentation/bookinfo/BookInfoContract.java @@ -43,6 +43,8 @@ interface View { void setAccentColor(int accentColor); void setToolbarColor(int color); + + void sendShareEvent(String bookTitle); } interface UserActionsListener { @@ -53,5 +55,6 @@ interface UserActionsListener { void loadImage(String url); + void shareBookClicked(BookDetail bookInfo); } } diff --git a/app/src/main/java/org/bookdash/android/presentation/bookinfo/BookInfoPresenter.java b/app/src/main/java/org/bookdash/android/presentation/bookinfo/BookInfoPresenter.java index 4b79478..0b8ad02 100644 --- a/app/src/main/java/org/bookdash/android/presentation/bookinfo/BookInfoPresenter.java +++ b/app/src/main/java/org/bookdash/android/presentation/bookinfo/BookInfoPresenter.java @@ -130,6 +130,12 @@ public void onResourceReady(Bitmap resource, GlideAnimation glid }); } + @Override + public void shareBookClicked(BookDetail bookInfo) { + + booksView.sendShareEvent(bookInfo.getBookTitle()); + } + private void extractPaletteColors(Bitmap resource) { Palette.from(resource).generate(new Palette.PaletteAsyncListener() { @Override diff --git a/app/src/main/java/org/bookdash/android/presentation/listbooks/BookAdapter.java b/app/src/main/java/org/bookdash/android/presentation/listbooks/BookAdapter.java index 7abd524..a22d43a 100644 --- a/app/src/main/java/org/bookdash/android/presentation/listbooks/BookAdapter.java +++ b/app/src/main/java/org/bookdash/android/presentation/listbooks/BookAdapter.java @@ -42,6 +42,7 @@ public void onBindViewHolder(BookViewHolder holder, int position) { holder.mTextView.setText(bookDetail.getBookTitle()); Glide.with(context).load(bookDetail.getBookCoverUrl()).into(holder.mImageView); holder.bookDetail = bookDetail; + holder.mImageViewDownloaded.setVisibility(bookDetail.isDownloadedAlready() ? View.VISIBLE : View.INVISIBLE); holder.mCardView.setTag(holder); holder.mCardView.setOnClickListener(onClickListener); diff --git a/app/src/main/java/org/bookdash/android/presentation/listbooks/BookViewHolder.java b/app/src/main/java/org/bookdash/android/presentation/listbooks/BookViewHolder.java index d5fa977..586264d 100644 --- a/app/src/main/java/org/bookdash/android/presentation/listbooks/BookViewHolder.java +++ b/app/src/main/java/org/bookdash/android/presentation/listbooks/BookViewHolder.java @@ -1,5 +1,6 @@ package org.bookdash.android.presentation.listbooks; +import android.media.Image; import android.support.v7.widget.CardView; import android.support.v7.widget.RecyclerView; import android.view.View; @@ -19,11 +20,13 @@ public class BookViewHolder extends RecyclerView.ViewHolder { public ImageView mImageView; public CardView mCardView; public BookDetail bookDetail; + public ImageView mImageViewDownloaded; public BookViewHolder(View v) { super(v); mCardView = (CardView) v.findViewById(R.id.card_view); mTextView = (TextView) v.findViewById(R.id.textViewBookName); mImageView = (ImageView) v.findViewById(R.id.imageViewBookCover); mTextViewBookAuthor = (TextView) v.findViewById(R.id.textViewAuthor); + mImageViewDownloaded = (ImageView) v.findViewById(R.id.imageViewBookDownloaded); } } diff --git a/app/src/main/java/org/bookdash/android/presentation/listbooks/ListBooksActivity.java b/app/src/main/java/org/bookdash/android/presentation/listbooks/ListBooksActivity.java index 0c8edd7..d6a5e95 100644 --- a/app/src/main/java/org/bookdash/android/presentation/listbooks/ListBooksActivity.java +++ b/app/src/main/java/org/bookdash/android/presentation/listbooks/ListBooksActivity.java @@ -6,7 +6,11 @@ import android.graphics.Rect; import android.net.Uri; import android.os.Bundle; +import android.support.design.widget.NavigationView; import android.support.design.widget.Snackbar; +import android.support.v4.view.GravityCompat; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.app.ActionBar; import android.support.v7.app.AlertDialog; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; @@ -20,10 +24,14 @@ import android.widget.LinearLayout; import android.widget.TextView; -import com.google.android.gms.analytics.HitBuilders; -import com.google.android.gms.analytics.Tracker; -import org.bookdash.android.BookDashApplication; +import com.google.android.gms.appinvite.AppInvite; +import com.google.android.gms.appinvite.AppInviteInvitation; +import com.google.android.gms.appinvite.AppInviteInvitationResult; +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.ResultCallback; + import org.bookdash.android.BuildConfig; import org.bookdash.android.Injection; import org.bookdash.android.R; @@ -40,21 +48,34 @@ public class ListBooksActivity extends BaseAppCompatActivity implements ListBooksContract.View { + private static final int INVITE_REQUEST_CODE = 1; private ListBooksContract.UserActionsListener actionsListener; - + private GoogleApiClient googleApiClient; + private Toolbar toolbar; + private DrawerLayout drawerLayout; + private NavigationView navigationView; + private Button buttonRetry; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); actionsListener = new ListBooksPresenter(this, Injection.provideBookRepo(), Injection.provideSettingsRepo(this)); - - Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); + toolbar = (Toolbar) findViewById(R.id.toolbar); + navigationView = (NavigationView) findViewById(R.id.navigation_view); setSupportActionBar(toolbar); + final ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setHomeAsUpIndicator(R.drawable.ic_menu_black_24dp); + actionBar.setDisplayHomeAsUpEnabled(true); + } + + setUpNavDrawer(); circularProgressBar = (CircularProgressBar) findViewById(R.id.activity_loading_books); linearLayoutErrorScreen = (LinearLayout) findViewById(R.id.linear_layout_error); - Button buttonRetry = (Button) findViewById(R.id.button_retry); + buttonRetry = (Button) findViewById(R.id.button_retry); textViewErrorMessage = (TextView) findViewById(R.id.text_view_error_screen); mRecyclerView = (AutofitRecyclerView) findViewById(R.id.recycler_view_books); @@ -72,14 +93,115 @@ public void getItemOffsets(Rect outRect, View view, RecyclerView parent, Recycle @Override public void onClick(View v) { Log.d(TAG, "Retry button clicked"); - actionsListener.loadBooksForLanguagePreference(); + actionsListener.loadBooksForLanguagePreference(false); } }); actionsListener.loadLanguages(); - actionsListener.loadBooksForLanguagePreference(); + actionsListener.loadBooksForLanguagePreference(false); + checkIfComingFromInvite(); + } + private void setUpNavDrawer() { + if (toolbar != null) { + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + toolbar.setNavigationOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + drawerLayout.openDrawer(GravityCompat.START); + } + }); + } + navigationView.setCheckedItem(R.id.action_all_books); + navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() { + @Override + public boolean onNavigationItemSelected(MenuItem menuItem) { + menuItem.setChecked(true); + switch (menuItem.getItemId()) { + + case R.id.action_all_books: { + showAllBooks(); + break; + } + case R.id.action_downloads: + showDownloadedBooks(); + break; + case R.id.action_about: + showAboutPage(); + break; + case R.id.action_settings: { + showSettingsScreen(); + break; + } + case R.id.action_thanks: { + showThanksPopover(); + break; + } + case R.id.action_invite_friends: { + openInvitePage(); + break; + } + case R.id.action_rate_app: { + showRatingPlayStore(); + break; + } + default: + + } + drawerLayout.closeDrawers(); + return true; + } + + + }); + } + + private void showDownloadedBooks() { + downloadOnly = true; + + actionsListener.loadBooksForLanguagePreference(downloadOnly); + } + + private void showAllBooks() { + downloadOnly = false; + actionsListener.loadBooksForLanguagePreference(downloadOnly); + } + + private void showSettingsScreen() { + + } + + private void checkIfComingFromInvite() { + googleApiClient = new GoogleApiClient.Builder(this) + .addApi(AppInvite.API) + .addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() { + @Override + public void onConnectionFailed(ConnectionResult connectionResult) { + Log.d(TAG, "onConnectionFailed: onResult:" + connectionResult.toString()); + + } + }) + /* .enableAutoManage(this, new GoogleApiClient.OnConnectionFailedListener() { + @Override + public void onConnectionFailed(ConnectionResult connectionResult) { + Log.d(TAG, "onConnectionFailed: onResult:" + connectionResult.toString()); + + } + })*/ + .build(); + if (googleApiClient != null) { + googleApiClient.connect(); + + AppInvite.AppInviteApi.getInvitation(googleApiClient, this, true) + .setResultCallback( + new ResultCallback() { + @Override + public void onResult(AppInviteInvitationResult result) { + Log.d(TAG, "getInvitation:onResult:" + result.getStatus()); + } + }); + } } private static final String TAG = ListBooksActivity.class.getCanonicalName(); @@ -89,8 +211,23 @@ public void onClick(View v) { private LinearLayout linearLayoutErrorScreen; private TextView textViewErrorMessage; + @Override + protected void onStop() { + super.onStop(); + if (googleApiClient != null) { + googleApiClient.disconnect(); + } + } + + @Override + protected void onStart() { + super.onStart(); + if (googleApiClient != null) { + googleApiClient.connect(); + } + } private View.OnClickListener bookClickListener = new View.OnClickListener() { @Override @@ -134,17 +271,55 @@ public boolean onOptionsItemSelected(MenuItem item) { showThanksPopover(); return true; } + if (id == R.id.action_invite_friends) { + openInvitePage(); + return true; + } return super.onOptionsItemSelected(item); } + private void openInvitePage() { + try { + Intent intent = new AppInviteInvitation.IntentBuilder(getString(R.string.invitation_title)) + .setMessage(getString(R.string.invitation_message)) + .setCallToActionText(getString(R.string.invitation_cta)) + // .setDeepLink(Uri.parse("http://bookdash.org/books/dK5BJWxPIf")) + .build(); + startActivityForResult(intent, INVITE_REQUEST_CODE); + } catch (ActivityNotFoundException ac) { + Snackbar.make(mRecyclerView, R.string.common_google_play_services_api_unavailable_text, Snackbar.LENGTH_LONG).show(); + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + Log.d(TAG, "onActivityResult: requestCode=" + requestCode + ", resultCode=" + resultCode); + + if (requestCode == INVITE_REQUEST_CODE) { + if (resultCode == RESULT_OK) { + // Check how many invitations were sent and log a message + // The ids array contains the unique invitation ids for each invitation sent + // (one for each contact select by the user). You can use these for analytics + // as the ID will be consistent on the sending and receiving devices. + String[] ids = AppInviteInvitation.getInvitationIds(resultCode, data); + Log.d(TAG, getString(R.string.sent_invitations_fmt, ids.length)); + } else { + // Sending failed or it was canceled, show failure message to the user + Log.d(TAG, "invite send failed:" + requestCode + ",resultCode:" + resultCode); + } + } + } + private boolean downloadOnly = false; private DialogInterface.OnClickListener languageClickListener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (dialog != null) { dialog.dismiss(); } - actionsListener.saveSelectedLanguage(which); + + actionsListener.saveSelectedLanguage(which, downloadOnly); /* tracker.send(new HitBuilders.EventBuilder() .setCategory("LanguageChange") @@ -203,10 +378,12 @@ public void openBookDetails(View v) { } else {*/ - Intent intent = new Intent(ListBooksActivity.this, BookInfoActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(BookInfoActivity.BOOK_PARCEL, ((BookViewHolder) v.getTag()).bookDetail.toBookParcelable()); - startActivity(intent); + Intent intent = new Intent(ListBooksActivity.this, BookInfoActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + BookViewHolder viewHolder = (BookViewHolder) v.getTag(); + BookDetail bookDetailResult = viewHolder.bookDetail; + intent.putExtra(BookInfoActivity.BOOK_PARCEL, bookDetailResult.toBookParcelable()); + startActivity(intent); /* }*/ } @@ -226,12 +403,13 @@ public void showRatingPlayStore() { } @Override - public void showErrorScreen(boolean show, String errorMessage) { + public void showErrorScreen(boolean show, String errorMessage, boolean showRetryButton) { if (show) { linearLayoutErrorScreen.setVisibility(View.VISIBLE); } else { linearLayoutErrorScreen.setVisibility(View.GONE); } + buttonRetry.setVisibility(showRetryButton ? View.VISIBLE : View.GONE); textViewErrorMessage.setText(errorMessage); } @@ -245,6 +423,16 @@ public void showLoading(boolean visible) { @Override public void showBooks(List bookDetailList) { + if (bookDetailList.isEmpty()) { + if (downloadOnly) { + showErrorScreen(true, getString(R.string.no_books_downloaded), false); + + } else { + showErrorScreen(true, getString(R.string.no_books_available), true); + + } + // return; + } RecyclerView.Adapter mAdapter = new BookAdapter(bookDetailList, ListBooksActivity.this, bookClickListener); mRecyclerView.setAdapter(mAdapter); mRecyclerView.scheduleLayoutAnimation(); diff --git a/app/src/main/java/org/bookdash/android/presentation/listbooks/ListBooksContract.java b/app/src/main/java/org/bookdash/android/presentation/listbooks/ListBooksContract.java index e811c82..ccbf536 100644 --- a/app/src/main/java/org/bookdash/android/presentation/listbooks/ListBooksContract.java +++ b/app/src/main/java/org/bookdash/android/presentation/listbooks/ListBooksContract.java @@ -14,7 +14,7 @@ interface View { void showThanksPopover(); void showAboutPage(); void showRatingPlayStore(); - void showErrorScreen(boolean show, String errorMessage); + void showErrorScreen(boolean show, String errorMessage, boolean showRetryButton); void showLoading(boolean visible); void showBooks(List bookDetailList); void showSnackBarError(int message); @@ -23,8 +23,8 @@ interface View { interface UserActionsListener { void loadLanguages(); - void saveSelectedLanguage(int indexOfLanguage); - void loadBooksForLanguagePreference(); + void saveSelectedLanguage(int indexOfLanguage, boolean downloadOnly); + void loadBooksForLanguagePreference(boolean downloadedOnly); void clickOpenLanguagePopover(); } diff --git a/app/src/main/java/org/bookdash/android/presentation/listbooks/ListBooksPresenter.java b/app/src/main/java/org/bookdash/android/presentation/listbooks/ListBooksPresenter.java index 923483e..f24280f 100644 --- a/app/src/main/java/org/bookdash/android/presentation/listbooks/ListBooksPresenter.java +++ b/app/src/main/java/org/bookdash/android/presentation/listbooks/ListBooksPresenter.java @@ -26,25 +26,26 @@ public ListBooksPresenter(ListBooksContract.View listBooksView, BookDetailReposi this.settingsRepository = settingsRepository; } @Override - public void loadBooksForLanguagePreference() { + public void loadBooksForLanguagePreference(boolean downloadedOnly) { String languagePreference = settingsRepository.getLanguagePreference(); - loadBooksForLanguage(languagePreference); + loadBooksForLanguage(languagePreference, downloadedOnly); } - private void loadBooksForLanguage(String language) { + private void loadBooksForLanguage(String language, boolean downloadedOnly) { listBooksView.showLoading(true); - bookDetailRepository.getBooksForLanguage(language, new BookDetailRepository.GetBooksForLanguageCallback() { + listBooksView.showErrorScreen(false, "", false); + bookDetailRepository.getBooksForLanguage(language,downloadedOnly, new BookDetailRepository.GetBooksForLanguageCallback() { @Override public void onBooksLoaded(List books) { listBooksView.showLoading(false); - listBooksView.showErrorScreen(false, ""); + listBooksView.showErrorScreen(false, "", false); listBooksView.showBooks(books); } @Override public void onBooksLoadError(Exception e) { listBooksView.showLoading(false); - listBooksView.showErrorScreen(true, e.getMessage().toUpperCase()); + listBooksView.showErrorScreen(true, e.getMessage().toUpperCase(), true); } }); @@ -68,10 +69,10 @@ public void onLanguagesLoadError(Exception e) { } @Override - public void saveSelectedLanguage(int indexOfLanguage) { + public void saveSelectedLanguage(int indexOfLanguage, boolean downloadOnly) { settingsRepository.saveLanguagePreference(languages.get(indexOfLanguage).getLanguageName()); - loadBooksForLanguage(languages.get(indexOfLanguage).getLanguageName()); + loadBooksForLanguage(languages.get(indexOfLanguage).getLanguageName(), downloadOnly); } @Override diff --git a/app/src/main/java/org/bookdash/android/presentation/readbook/BookDetailActivity.java b/app/src/main/java/org/bookdash/android/presentation/readbook/BookDetailActivity.java index d80856a..699cde5 100644 --- a/app/src/main/java/org/bookdash/android/presentation/readbook/BookDetailActivity.java +++ b/app/src/main/java/org/bookdash/android/presentation/readbook/BookDetailActivity.java @@ -5,8 +5,6 @@ import android.view.ViewTreeObserver; import android.view.WindowManager; -import com.google.android.gms.analytics.HitBuilders; - import org.bookdash.android.R; import org.bookdash.android.presentation.activity.BaseAppCompatActivity; import org.bookdash.android.domain.pojo.gson.BookPages; diff --git a/app/src/main/java/org/bookdash/android/presentation/splash/SplashActivity.java b/app/src/main/java/org/bookdash/android/presentation/splash/SplashActivity.java index f20ac0e..7b23df1 100644 --- a/app/src/main/java/org/bookdash/android/presentation/splash/SplashActivity.java +++ b/app/src/main/java/org/bookdash/android/presentation/splash/SplashActivity.java @@ -16,6 +16,7 @@ * @since 2015/07/16 3:16 PM */ public class SplashActivity extends AppCompatActivity implements SplashContract.View { + private static final int SPLASH_SCREEN_REQUEST_CODE = 1; private SplashContract.UserActionsListener splashPresenter; @Override @@ -23,20 +24,23 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_splash); splashPresenter = new SplashPresenter(this, Injection.provideSettingsRepo(this)); + showSplashAfterDelay(); + } + + private void showSplashAfterDelay(){ (new Handler()).postDelayed(new Runnable() { @Override public void run() { splashPresenter.loadSplash(); } }, 1000); - } + } @Override public void loadTutorial() { Intent mainAct = new Intent(SplashActivity.this, MaterialTutorialActivity.class); mainAct.putParcelableArrayListExtra(MaterialTutorialActivity.MATERIAL_TUTORIAL_ARG_TUTORIAL_ITEMS, splashPresenter.getTutorialItems(this)); - startActivityForResult(mainAct, 4325); - // finish(); + startActivityForResult(mainAct, SPLASH_SCREEN_REQUEST_CODE); } @@ -51,7 +55,7 @@ public void loadMainScreen() { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { // super.onActivityResult(requestCode, resultCode, data); - if (resultCode == RESULT_OK && requestCode == 4325){ + if (resultCode == RESULT_OK && requestCode == SPLASH_SCREEN_REQUEST_CODE){ splashPresenter.finishedTutorial(); } diff --git a/app/src/main/kotlin/org.bookdash.android/data/BookDetailRepositoryExtenstion.kt b/app/src/main/kotlin/org.bookdash.android/data/BookDetailRepositoryExtenstion.kt new file mode 100644 index 0000000..e69de29 diff --git a/app/src/main/res/drawable-hdpi/bookdashlogo.png b/app/src/main/res/drawable-hdpi/bookdashlogo.png new file mode 100644 index 0000000..e01d996 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/bookdashlogo.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_book.png b/app/src/main/res/drawable-hdpi/ic_action_book.png deleted file mode 100755 index 0b605fd..0000000 Binary files a/app/src/main/res/drawable-hdpi/ic_action_book.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_language.png b/app/src/main/res/drawable-hdpi/ic_action_language.png deleted file mode 100644 index 4637d5f..0000000 Binary files a/app/src/main/res/drawable-hdpi/ic_action_language.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_file_cloud_download.png b/app/src/main/res/drawable-hdpi/ic_file_cloud_download.png deleted file mode 100755 index 6897d94..0000000 Binary files a/app/src/main/res/drawable-hdpi/ic_file_cloud_download.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_book.png b/app/src/main/res/drawable-mdpi/ic_action_book.png deleted file mode 100755 index 7e5e089..0000000 Binary files a/app/src/main/res/drawable-mdpi/ic_action_book.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_language.png b/app/src/main/res/drawable-mdpi/ic_action_language.png deleted file mode 100644 index 2e25a9c..0000000 Binary files a/app/src/main/res/drawable-mdpi/ic_action_language.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_file_cloud_download.png b/app/src/main/res/drawable-mdpi/ic_file_cloud_download.png deleted file mode 100755 index 3349d1a..0000000 Binary files a/app/src/main/res/drawable-mdpi/ic_file_cloud_download.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_book.png b/app/src/main/res/drawable-xhdpi/ic_action_book.png deleted file mode 100755 index dee0c2f..0000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_action_book.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_language.png b/app/src/main/res/drawable-xhdpi/ic_action_language.png deleted file mode 100644 index 3b5a00a..0000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_action_language.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_file_cloud_download.png b/app/src/main/res/drawable-xhdpi/ic_file_cloud_download.png deleted file mode 100755 index a752c95..0000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_file_cloud_download.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_book.png b/app/src/main/res/drawable-xxhdpi/ic_action_book.png deleted file mode 100755 index dae1f50..0000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_action_book.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_language.png b/app/src/main/res/drawable-xxhdpi/ic_action_language.png deleted file mode 100644 index 8600b66..0000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_action_language.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_file_cloud_download.png b/app/src/main/res/drawable-xxhdpi/ic_file_cloud_download.png deleted file mode 100755 index 26c0146..0000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_file_cloud_download.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_book.png b/app/src/main/res/drawable-xxxhdpi/ic_action_book.png deleted file mode 100755 index cd6e459..0000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_action_book.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_language.png b/app/src/main/res/drawable-xxxhdpi/ic_action_language.png deleted file mode 100644 index 8012927..0000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_action_language.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_file_cloud_download.png b/app/src/main/res/drawable-xxxhdpi/ic_file_cloud_download.png deleted file mode 100755 index 5f8be80..0000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_file_cloud_download.png and /dev/null differ diff --git a/app/src/main/res/drawable/book_dash_logo.png b/app/src/main/res/drawable/book_dash_logo.png deleted file mode 100644 index cd13c19..0000000 Binary files a/app/src/main/res/drawable/book_dash_logo.png and /dev/null differ diff --git a/app/src/main/res/drawable/ic_check_circle_black_24dp.xml b/app/src/main/res/drawable/ic_check_circle_black_24dp.xml new file mode 100644 index 0000000..333aa3e --- /dev/null +++ b/app/src/main/res/drawable/ic_check_circle_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_cloud_download_black_24dp.xml b/app/src/main/res/drawable/ic_cloud_download_black_24dp.xml new file mode 100644 index 0000000..261c312 --- /dev/null +++ b/app/src/main/res/drawable/ic_cloud_download_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_collections_bookmark_black_24dp.xml b/app/src/main/res/drawable/ic_collections_bookmark_black_24dp.xml new file mode 100644 index 0000000..ef14799 --- /dev/null +++ b/app/src/main/res/drawable/ic_collections_bookmark_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_file_download_black_24dp.xml b/app/src/main/res/drawable/ic_file_download_black_24dp.xml new file mode 100644 index 0000000..492b41d --- /dev/null +++ b/app/src/main/res/drawable/ic_file_download_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_grade_black_24dp.xml b/app/src/main/res/drawable/ic_grade_black_24dp.xml new file mode 100644 index 0000000..a87ca09 --- /dev/null +++ b/app/src/main/res/drawable/ic_grade_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_group_add_black_24dp.xml b/app/src/main/res/drawable/ic_group_add_black_24dp.xml new file mode 100644 index 0000000..2de3e0e --- /dev/null +++ b/app/src/main/res/drawable/ic_group_add_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_info_outline_black_24dp.xml b/app/src/main/res/drawable/ic_info_outline_black_24dp.xml new file mode 100644 index 0000000..8d4f1ce --- /dev/null +++ b/app/src/main/res/drawable/ic_info_outline_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_black_24dp.xml b/app/src/main/res/drawable/ic_menu_black_24dp.xml new file mode 100644 index 0000000..04ee485 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_people_outline_black_24dp.xml b/app/src/main/res/drawable/ic_people_outline_black_24dp.xml new file mode 100644 index 0000000..9aca56e --- /dev/null +++ b/app/src/main/res/drawable/ic_people_outline_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_public_black_24dp.xml b/app/src/main/res/drawable/ic_public_black_24dp.xml new file mode 100644 index 0000000..60e024f --- /dev/null +++ b/app/src/main/res/drawable/ic_public_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_settings_black_24dp.xml b/app/src/main/res/drawable/ic_settings_black_24dp.xml new file mode 100644 index 0000000..c11919c --- /dev/null +++ b/app/src/main/res/drawable/ic_settings_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_share.xml b/app/src/main/res/drawable/ic_share.xml new file mode 100644 index 0000000..8c4d989 --- /dev/null +++ b/app/src/main/res/drawable/ic_share.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout-land/activity_book_information.xml b/app/src/main/res/layout-land/activity_book_information.xml index 4d0d5f0..540241a 100644 --- a/app/src/main/res/layout-land/activity_book_information.xml +++ b/app/src/main/res/layout-land/activity_book_information.xml @@ -275,8 +275,7 @@ app:fbb_endBitmap="@drawable/ic_open_book" app:fbb_hideProgressOnComplete="true" app:fbb_progressColor="?attr/colorAccent" - app:fbb_progressWidthRatio="0.125" - app:fbb_autoStart="true" + app:fbb_progressWidthRatio="0.1" app:fbb_showEndBitmap="true" app:fbb_showShadow="true" app:layout_anchor="@id/rel_top_title_section" diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml index 3a64f80..ee60dcc 100644 --- a/app/src/main/res/layout/activity_about.xml +++ b/app/src/main/res/layout/activity_about.xml @@ -94,7 +94,7 @@ android:layout_height="200dp" android:layout_gravity="center" android:layout_marginBottom="8dp" - android:src="@drawable/book_dash_logo" + android:src="@drawable/bookdashlogo" android:layout_marginTop="8dp" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" /> diff --git a/app/src/main/res/layout/activity_book_information.xml b/app/src/main/res/layout/activity_book_information.xml index 7897150..d1c89ce 100644 --- a/app/src/main/res/layout/activity_book_information.xml +++ b/app/src/main/res/layout/activity_book_information.xml @@ -261,10 +261,10 @@ android:visibility="visible" app:fbb_endBitmap="@drawable/ic_open_book" app:fbb_hideProgressOnComplete="true" + app:fbb_autoStart="true" app:fbb_progressColor="?attr/colorAccent" - app:fbb_progressWidthRatio="0.125" + app:fbb_progressWidthRatio="0.1" app:fbb_showEndBitmap="true" - app:fbb_autoStart="true" app:fbb_showShadow="true" app:layout_anchor="@id/app_bar_layout" app:layout_anchorGravity="bottom|right|end" /> diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index ae37b17..5415bf9 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,98 +1,115 @@ - + android:fitsSystemWindows="true"> - + android:layout_height="match_parent" + android:background="@color/background_light_grey" + tools:context=".MainActivity"> - - - - + android:layout_height="match_parent"> - - - - - - - - - - - - - + android:layout_height="match_parent" + android:layout_gravity="center_horizontal" + + android:clipToPadding="false" + android:columnWidth="@dimen/book_column_width_padding" + android:gravity="center_horizontal" + android:layoutAnimation="@anim/grid_layout_animation" + android:paddingLeft="@dimen/book_list_padding" + android:paddingRight="@dimen/book_list_padding" + android:scrollbars="vertical" + android:visibility="visible" + app:layout_behavior="@string/appbar_scrolling_view_behavior" + tools:listitem="@layout/list_item_book" /> + + + + + -