diff --git a/app/build.gradle b/app/build.gradle index e583099..adb195a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,6 +11,7 @@ apply plugin: 'com.android.application' apply plugin: 'com.google.gms.google-services' apply plugin: 'io.fabric' apply plugin: 'com.github.triplet.play' + repositories { maven { url 'https://maven.fabric.io/public' } } @@ -18,21 +19,23 @@ repositories { android { - dataBinding { enabled = true} + dataBinding { enabled = true } compileSdkVersion 23 - buildToolsVersion "23.0.2" + buildToolsVersion "23.0.3" defaultConfig { applicationId "org.bookdash.android" minSdkVersion 16 targetSdkVersion 23 - versionCode 17 - versionName "1.0.17" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + versionCode 18 + versionName "1.1.0" + testInstrumentationRunner "org.bookdash.android.presentation.CustomTestRunner" buildConfigField "String", "PARSE_APPLICATION_ID", "\"${BOOKDASH_PARSE_APP_ID}\"" buildConfigField "String", "PARSE_CLIENT_KEY", "\"${BOOKDASH_PARSE_CLIENT_ID}\"" + vectorDrawables.useSupportLibrary = true } + File signFile = rootProject.file('release-keystore.properties') if (signFile.exists()) { signingConfigs { @@ -74,7 +77,7 @@ android { } // Remove mockRelease as it's not needed. android.variantFilter { variant -> - if(variant.buildType.name.equals('release') + if (variant.buildType.name.equals('release') && variant.getFlavors().get(0).name.equals('mock')) { variant.setIgnore(true); } @@ -90,7 +93,7 @@ android { release { shrinkResources false minifyEnabled true - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),'proguard-rules.pro' + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' ext.enableCrashlytics = true } debug { @@ -110,37 +113,37 @@ dependencies { testCompile "junit:junit:$rootProject.ext.junitVersion" testCompile "org.mockito:mockito-all:$rootProject.ext.mockitoVersion" testCompile "org.hamcrest:hamcrest-all:$rootProject.ext.hamcrestVersion" - testCompile ("org.powermock:powermock-module-junit4:$rootProject.ext.powerMockito") - testCompile ("org.powermock:powermock-api-mockito:$rootProject.ext.powerMockito") + testCompile("org.powermock:powermock-module-junit4:$rootProject.ext.powerMockito") + testCompile("org.powermock:powermock-api-mockito:$rootProject.ext.powerMockito") // Android Testing Support Library's runner and rules - androidTestCompile ('com.android.support.test.espresso:espresso-web:2.2.1'){ + androidTestCompile('com.android.support.test.espresso:espresso-web:2.2.1') { exclude module: 'support-annotations' exclude module: 'support-v4' } - androidTestCompile ("com.android.support.test:runner:$rootProject.ext.runnerVersion"){ + androidTestCompile("com.android.support.test:runner:$rootProject.ext.runnerVersion") { exclude module: 'support-annotations' exclude module: 'support-v4' } - androidTestCompile ("com.android.support.test:rules:$rootProject.ext.runnerVersion"){ + androidTestCompile("com.android.support.test:rules:$rootProject.ext.runnerVersion") { exclude module: 'support-annotations' exclude module: 'support-v4' } // Espresso UI Testing - androidTestCompile ("com.android.support.test.espresso:espresso-core:$rootProject.ext.espressoVersion"){ + androidTestCompile("com.android.support.test.espresso:espresso-core:$rootProject.ext.espressoVersion") { exclude module: 'recyclerview-v7' exclude module: 'support-annotations' exclude module: 'support-v4' exclude group: "javax.inject" } - androidTestCompile ("com.android.support.test.espresso:espresso-contrib:$rootProject.ext.espressoVersion") { + androidTestCompile("com.android.support.test.espresso:espresso-contrib:$rootProject.ext.espressoVersion") { exclude module: 'recyclerview-v7' exclude module: 'support-annotations' exclude module: 'support-v4' } - androidTestCompile ("com.android.support.test.espresso:espresso-intents:$rootProject.ext.espressoVersion"){ + androidTestCompile("com.android.support.test.espresso:espresso-intents:$rootProject.ext.espressoVersion") { exclude module: 'recyclerview-v7' exclude module: 'support-annotations' exclude module: 'support-v4' @@ -159,8 +162,8 @@ dependencies { compile 'com.google.code.gson:gson:2.4' compile 'com.github.castorflex.smoothprogressbar:library-circular:1.2.0' - // compile 'com.parse.bolts:bolts-android:1.3.0' - // compile 'com.parse:parse-android:1.11.0' + // compile 'com.parse.bolts:bolts-android:1.3.0' + // compile 'com.parse:parse-android:1.11.0' compile('com.crashlytics.sdk.android:crashlytics:2.5.3@aar') { transitive = true; } @@ -170,3 +173,23 @@ dependencies { compile "com.android.support:design:$rootProject.ext.googlePlayServicesVersion" compile "com.google.android.gms:play-services-analytics:$rootProject.ext.googlePlayServicesVersion" } + +// Grant animation permissions to avoid test failure because of ui sync. +task grantAnimationPermissions(type: Exec, dependsOn: 'installMock') { + group = 'test' + description = 'Grant permissions for testing.' + + def absolutePath = file('..') // Get project absolute path + commandLine "$absolutePath/set_animation_permissions.sh org.bookdash.android".split(" ") +} + +// Source: http://stackoverflow.com/q/29908110/112705 +afterEvaluate { + // When launching individual tests from Android Studio, it seems that only the assemble tasks + // get called directly, not the install* versions + tasks.each { task -> + if (task.name.startsWith('assembleMockAndroidTest')) { + task.dependsOn grantAnimationPermissions + } + } +} diff --git a/app/src/androidTest/java/org/bookdash/android/presentation/CustomTestRunner.java b/app/src/androidTest/java/org/bookdash/android/presentation/CustomTestRunner.java new file mode 100644 index 0000000..053fcf7 --- /dev/null +++ b/app/src/androidTest/java/org/bookdash/android/presentation/CustomTestRunner.java @@ -0,0 +1,104 @@ +package org.bookdash.android.presentation; + +import android.Manifest; +import android.app.KeyguardManager; +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.os.IBinder; +import android.os.PowerManager; +import android.support.test.runner.AndroidJUnitRunner; +import android.util.Log; + +import java.lang.reflect.Method; + +/** + * Tests can fail for other reasons than code, it´ because of the animations and espresso sync and + * emulator state (screen off or locked) + *

+ * Before all the tests prepare the device to run tests and avoid these problems. + *

+ * - Disable animations + * - Disable keyguard lock + * - Set it to be awake all the time (dont let the processor sleep) + * + * @see https://github.com/JakeWharton/u2020 + * @see https://gist.github.com/daj/7b48f1b8a92abf960e7b + */ +public final class CustomTestRunner extends AndroidJUnitRunner { + + private static final String TAG = "CustomTestRunner"; + + @Override + public void onStart() { + + runOnMainSync(new Runnable() { + @Override + public void run() { + Context app = CustomTestRunner.this.getTargetContext().getApplicationContext(); + + CustomTestRunner.this.disableAnimations(app); + + String name = CustomTestRunner.class.getSimpleName(); + unlockScreen(app, name); + keepSceenAwake(app, name); + } + }); + + super.onStart(); + } + + + @Override + public void finish(int resultCode, Bundle results) { + super.finish(resultCode, results); + enableAnimations(getContext()); + } + private void keepSceenAwake(Context app, String name) { + PowerManager power = (PowerManager) app.getSystemService(Context.POWER_SERVICE); + power.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE, name) + .acquire(); + } + + private void unlockScreen(Context app, String name) { + KeyguardManager keyguard = (KeyguardManager) app.getSystemService(Context.KEYGUARD_SERVICE); + keyguard.newKeyguardLock(name).disableKeyguard(); + } + + void disableAnimations(Context context) { + int permStatus = context.checkCallingOrSelfPermission(Manifest.permission.SET_ANIMATION_SCALE); + if (permStatus == PackageManager.PERMISSION_GRANTED) { + setSystemAnimationsScale(0.0f); + } + } + + void enableAnimations(Context context) { + int permStatus = context.checkCallingOrSelfPermission(Manifest.permission.SET_ANIMATION_SCALE); + if (permStatus == PackageManager.PERMISSION_GRANTED) { + setSystemAnimationsScale(1.0f); + } + } + + private void setSystemAnimationsScale(float animationScale) { + try { + Class windowManagerStubClazz = Class.forName("android.view.IWindowManager$Stub"); + Method asInterface = windowManagerStubClazz.getDeclaredMethod("asInterface", IBinder.class); + Class serviceManagerClazz = Class.forName("android.os.ServiceManager"); + Method getService = serviceManagerClazz.getDeclaredMethod("getService", String.class); + Class windowManagerClazz = Class.forName("android.view.IWindowManager"); + Method setAnimationScales = windowManagerClazz.getDeclaredMethod("setAnimationScales", float[].class); + Method getAnimationScales = windowManagerClazz.getDeclaredMethod("getAnimationScales"); + + IBinder windowManagerBinder = (IBinder) getService.invoke(null, "window"); + Object windowManagerObj = asInterface.invoke(null, windowManagerBinder); + float[] currentScales = (float[]) getAnimationScales.invoke(windowManagerObj); + for (int i = 0; i < currentScales.length; i++) { + currentScales[i] = animationScale; + } + setAnimationScales.invoke(windowManagerObj, new Object[]{currentScales}); + Log.d(TAG, "Changed permissions of animations"); + } catch (Exception e) { + Log.e(TAG, "Could not change animation scale to " + animationScale + " :'("); + } + } +} diff --git a/app/src/androidTest/java/org/bookdash/android/presentation/about/AboutActivityTest.java b/app/src/androidTest/java/org/bookdash/android/presentation/about/AboutFragmentTest.java similarity index 73% rename from app/src/androidTest/java/org/bookdash/android/presentation/about/AboutActivityTest.java rename to app/src/androidTest/java/org/bookdash/android/presentation/about/AboutFragmentTest.java index d24eff9..c3575a0 100644 --- a/app/src/androidTest/java/org/bookdash/android/presentation/about/AboutActivityTest.java +++ b/app/src/androidTest/java/org/bookdash/android/presentation/about/AboutFragmentTest.java @@ -6,38 +6,30 @@ import android.support.test.espresso.intent.Intents; import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; -import android.support.v4.widget.NestedScrollView; import android.test.suitebuilder.annotation.SmallTest; import android.text.Html; -import android.view.View; import junit.framework.Assert; import org.bookdash.android.R; +import org.bookdash.android.presentation.main.MainActivity; +import org.bookdash.android.presentation.utils.NavigationUtils; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; - import static android.support.test.espresso.Espresso.onView; -import static android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu; import static android.support.test.espresso.action.ViewActions.click; import static android.support.test.espresso.action.ViewActions.scrollTo; import static android.support.test.espresso.assertion.ViewAssertions.matches; +import static android.support.test.espresso.intent.Intents.intended; import static android.support.test.espresso.intent.matcher.IntentMatchers.hasAction; 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.isDisplayed; -import static android.support.test.espresso.matcher.ViewMatchers.withId; import static android.support.test.espresso.matcher.ViewMatchers.withText; import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.contains; - - -import static android.support.test.espresso.intent.Intents.intended; -import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent; /** * @author rebeccafranks @@ -45,17 +37,19 @@ */ @RunWith(AndroidJUnit4.class) @SmallTest -public class AboutActivityTest { +public class AboutFragmentTest { @Rule - public ActivityTestRule testRule = new ActivityTestRule<>(AboutActivity.class); + public ActivityTestRule testRule = new ActivityTestRule<>(MainActivity.class); @Before - public void setUp(){ + public void setUp() { Intents.init(); + NavigationUtils.selectNavDrawItem(testRule.getActivity(), R.id.action_about); + } @After - public void tearDown(){ + public void tearDown() { Intents.release(); } @@ -76,12 +70,13 @@ public void clickLearnMore_OpenBrowser() throws Throwable { onView(withText(R.string.learn_more)).perform(scrollTo(), click()); intended(allOf(hasAction(Intent.ACTION_VIEW), - hasData(Uri.parse("http://bookdash.org")) + hasData(Uri.parse("http://bookdash.org")) ) ); } + @Test - public void testGetScreenName(){ - Assert.assertEquals("About Screen",testRule.getActivity().getScreenName()); + public void testGetScreenName() { + Assert.assertEquals("MainActivity", 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 6907aa5..46ef061 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 @@ -1,20 +1,13 @@ package org.bookdash.android.presentation.listbooks; -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; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.LargeTest; -import android.view.MenuItem; -import org.bookdash.android.BuildConfig; import org.bookdash.android.R; -import org.bookdash.android.presentation.about.AboutActivity; -import org.hamcrest.BaseMatcher; -import org.hamcrest.Description; +import org.bookdash.android.presentation.main.MainActivity; +import org.bookdash.android.presentation.utils.NavigationUtils; import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -22,24 +15,12 @@ import org.junit.runner.RunWith; import static android.support.test.espresso.Espresso.onView; -import static android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu; import static android.support.test.espresso.action.ViewActions.click; import static android.support.test.espresso.assertion.ViewAssertions.matches; -import static android.support.test.espresso.intent.matcher.IntentMatchers.hasAction; -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; import static android.support.test.espresso.matcher.ViewMatchers.withText; -import static org.hamcrest.Matchers.*; -import static org.hamcrest.Matchers.contains; - - -import static android.support.test.espresso.intent.Intents.intended; -import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent; /** * @author Rebecca Franks (rebecca.franks@dstvdm.com) @@ -49,8 +30,8 @@ @LargeTest public class OverflowMenuOptionsTest { @Rule - public ActivityTestRule mActivityTestRule = - new ActivityTestRule<>(ListBooksActivity.class); + public ActivityTestRule mActivityTestRule = + new ActivityTestRule<>(MainActivity.class); @Before @@ -73,29 +54,17 @@ public void languageItemClick_ShowLanguageChooser() { @Test public void aboutMenuClick_ShowAboutBookDashScreen() { //When - selectNavDrawItem(R.id.action_about); + NavigationUtils.selectNavDrawItem(mActivityTestRule.getActivity(), R.id.action_about); //Then - intended(hasComponent(AboutActivity.class.getName())); + onView(withText(R.string.title_activity_about)).check(matches(isDisplayed())); } - @Test - public void rateThisAppClick_ShowPlayStoreDetail() { - //Given - //When - 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)) - ) - ); - } - @Test public void contributorsClicked_ShowThanksPopover() { //When - selectNavDrawItem(R.id.action_thanks); + NavigationUtils.selectNavDrawItem(mActivityTestRule.getActivity(), R.id.action_thanks); //Then onView(withText("Contributors")).inRoot(isDialog()).check(matches(isDisplayed())); @@ -104,24 +73,12 @@ public void contributorsClicked_ShowThanksPopover() { @Test public void contributorsOkClick_HideThanksPopover() { //When - selectNavDrawItem(R.id.action_thanks); + NavigationUtils.selectNavDrawItem(mActivityTestRule.getActivity(), 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/androidTest/java/org/bookdash/android/presentation/utils/NavigationUtils.java b/app/src/androidTest/java/org/bookdash/android/presentation/utils/NavigationUtils.java new file mode 100644 index 0000000..81fe5f9 --- /dev/null +++ b/app/src/androidTest/java/org/bookdash/android/presentation/utils/NavigationUtils.java @@ -0,0 +1,29 @@ +package org.bookdash.android.presentation.utils; + +import android.app.Activity; +import android.support.design.widget.NavigationView; + +import org.bookdash.android.R; + +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.action.ViewActions.click; +import static android.support.test.espresso.matcher.ViewMatchers.isClickable; +import static android.support.test.espresso.matcher.ViewMatchers.withContentDescription; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; + + +public class NavigationUtils { + + public static void selectNavDrawItem(Activity activity, final int navItemId){ + onView(allOf(withContentDescription(containsString("Navigate up")), isClickable())).perform(click()); + final NavigationView navigation =(NavigationView) activity.findViewById(R.id.navigation_view); + activity.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 f8f3420..1cfd805 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 @@ -8,64 +8,16 @@ import android.test.suitebuilder.annotation.LargeTest; import org.bookdash.android.domain.pojo.BookDetailParcelable; -import org.bookdash.android.presentation.listbooks.BookViewHolder; -import org.bookdash.android.presentation.listbooks.ListBooksActivity; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import android.content.Intent; -import android.net.Uri; -import android.os.Build; -import android.support.test.InstrumentationRegistry; -import android.support.test.espresso.ViewInteraction; -import android.support.test.espresso.intent.Intents; -import android.support.test.espresso.matcher.BoundedMatcher; -import android.support.test.rule.ActivityTestRule; -import android.support.test.runner.AndroidJUnit4; -import android.support.v7.widget.Toolbar; -import android.test.suitebuilder.annotation.LargeTest; -import android.view.MenuItem; - -import org.bookdash.android.R; -import org.bookdash.android.presentation.about.AboutActivity; -import org.bookdash.android.presentation.bookinfo.BookInfoActivity; -import org.hamcrest.BaseMatcher; -import org.hamcrest.Description; -import org.hamcrest.Matcher; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.bookdash.android.test.BuildConfig; - -import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom; -import static android.support.test.espresso.Espresso.*; -import static android.support.test.espresso.action.ViewActions.click; +import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.assertion.ViewAssertions.matches; -import static android.support.test.espresso.intent.Intents.intended; -import static android.support.test.espresso.intent.matcher.IntentMatchers.hasAction; -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.isDisplayed; import static android.support.test.espresso.matcher.ViewMatchers.withEffectiveVisibility; -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; -import static android.support.test.espresso.intent.matcher.IntentMatchers.hasAction; -import static android.support.test.espresso.intent.matcher.IntentMatchers.hasData; -import static android.support.test.espresso.matcher.ViewMatchers.withId; -import static android.support.test.espresso.matcher.ViewMatchers.withText; -import static org.hamcrest.Matchers.*; -import static org.hamcrest.Matchers.contains; - - -import static android.support.test.espresso.intent.Intents.intended; -import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent; /** * @author rebeccafranks @@ -88,10 +40,11 @@ public void setUp() { public void tearDown() { Intents.release(); } + public static final String BOOK_OBJ_ID = "f4r2gho2h"; @Test - public void loadBookInfo_DisplayBookInformation(){ + public void loadBookInfo_DisplayBookInformation() { Intent intent = new Intent(); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); BookDetailParcelable bookDetailParcelable = new BookDetailParcelable(); 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 03eea6a..cbc2869 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 @@ -1,10 +1,5 @@ package org.bookdash.android.presentation.listbooks; -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; import android.support.test.espresso.matcher.BoundedMatcher; @@ -12,15 +7,13 @@ import android.support.test.runner.AndroidJUnit4; import android.support.v7.widget.Toolbar; 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; import org.bookdash.android.presentation.bookinfo.BookInfoActivity; -import org.hamcrest.BaseMatcher; +import org.bookdash.android.presentation.main.MainActivity; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.junit.After; @@ -28,35 +21,18 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.bookdash.android.test.BuildConfig; - -import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist; -import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom; -import static android.support.test.espresso.Espresso.*; +import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.action.ViewActions.click; +import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist; import static android.support.test.espresso.assertion.ViewAssertions.matches; import static android.support.test.espresso.intent.Intents.intended; -import static android.support.test.espresso.intent.matcher.IntentMatchers.hasAction; 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.isAssignableFrom; 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; -import static android.support.test.espresso.intent.matcher.IntentMatchers.hasAction; -import static android.support.test.espresso.intent.matcher.IntentMatchers.hasData; -import static android.support.test.espresso.matcher.ViewMatchers.withId; -import static android.support.test.espresso.matcher.ViewMatchers.withText; -import static org.hamcrest.Matchers.*; -import static org.hamcrest.Matchers.contains; - - -import static android.support.test.espresso.intent.Intents.intended; -import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent; +import static org.hamcrest.Matchers.is; /** * @author rebeccafranks @@ -67,8 +43,8 @@ public class ListBooksActivityTest { @Rule - public ActivityTestRule activityTestRule = - new ActivityTestRule<>(ListBooksActivity.class); + public ActivityTestRule activityTestRule = + new ActivityTestRule<>(MainActivity.class); @Before public void setUp() { @@ -109,8 +85,8 @@ public void openScreen_AppBarTitleIsCorrect() { @Test - public void chooseDifferentLanguage_NewBooksLoaded(){ - // selectNavDrawItem(R.id.action_language_choice); + public void chooseDifferentLanguage_NewBooksLoaded() { + // selectNavDrawItem(R.id.action_language_choice); onView(withId(R.id.action_language_choice)).perform(click()); //When @@ -124,12 +100,11 @@ public void chooseDifferentLanguage_NewBooksLoaded(){ } @Test - public void testGetScreenName_IsBookListing(){ - Assert.assertEquals("BookListingScreen", activityTestRule.getActivity().getScreenName()); + public void testGetScreenName_IsBookListing() { + Assert.assertEquals("MainActivity", activityTestRule.getActivity().getScreenName()); } - private static ViewInteraction matchToolbarTitle( CharSequence title) { return onView(isAssignableFrom(Toolbar.class)) 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 ef42c54..2a6cc84 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 @@ -4,70 +4,22 @@ import android.support.test.espresso.intent.Intents; import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; -import android.test.InstrumentationTestCase; import android.test.suitebuilder.annotation.LargeTest; import org.bookdash.android.R; import org.bookdash.android.data.books.FakeBookDetailApiImpl; -import org.bookdash.android.data.settings.FakeSettingsApiImpl; +import org.bookdash.android.presentation.main.MainActivity; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import java.util.List; -import android.content.Intent; -import android.net.Uri; -import android.os.Build; -import android.support.test.InstrumentationRegistry; -import android.support.test.espresso.ViewInteraction; -import android.support.test.espresso.intent.Intents; -import android.support.test.espresso.matcher.BoundedMatcher; -import android.support.test.rule.ActivityTestRule; -import android.support.test.runner.AndroidJUnit4; -import android.support.v7.widget.Toolbar; -import android.test.suitebuilder.annotation.LargeTest; -import android.view.MenuItem; - -import org.bookdash.android.R; -import org.bookdash.android.data.settings.FakeSettingsApiImpl; -import org.bookdash.android.presentation.about.AboutActivity; -import org.bookdash.android.presentation.bookinfo.BookInfoActivity; -import org.hamcrest.BaseMatcher; -import org.hamcrest.Description; -import org.hamcrest.Matcher; -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.bookdash.android.test.BuildConfig; - - -import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom; -import static android.support.test.espresso.Espresso.*; +import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.action.ViewActions.click; import static android.support.test.espresso.assertion.ViewAssertions.matches; -import static android.support.test.espresso.intent.Intents.intended; -import static android.support.test.espresso.intent.matcher.IntentMatchers.hasAction; -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.isDisplayed; -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; -import static android.support.test.espresso.intent.matcher.IntentMatchers.hasAction; -import static android.support.test.espresso.intent.matcher.IntentMatchers.hasData; -import static android.support.test.espresso.matcher.ViewMatchers.withId; -import static android.support.test.espresso.matcher.ViewMatchers.withText; -import static org.hamcrest.Matchers.*; -import static org.hamcrest.Matchers.contains; - - -import static android.support.test.espresso.intent.Intents.intended; -import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent; /** * @author rebeccafranks @@ -77,22 +29,22 @@ @LargeTest public class ListBooksFailActivityTest { @Rule - public ActivityTestRule activityActivityTestRule = new ActivityTestRule<>(ListBooksActivity.class, true, false); + public ActivityTestRule activityActivityTestRule = new ActivityTestRule<>(MainActivity.class, true, false); @Before - public void setUp(){ + public void setUp() { Intents.init(); } @After - public void tearDown(){ + public void tearDown() { Intents.release(); } @Test - public void loadLanguages_LoadError_ShowSnackBarMessage(){ + public void loadLanguages_LoadError_ShowSnackBarMessage() { FakeBookDetailApiImpl.setShouldFailService(true); activityActivityTestRule.launchActivity(null); @@ -104,7 +56,7 @@ public void loadLanguages_LoadError_ShowSnackBarMessage(){ } @Test - public void loadBooks_LoadError_ShowRetryButton(){ + public void loadBooks_LoadError_ShowRetryButton() { FakeBookDetailApiImpl.setShouldFailService(true); activityActivityTestRule.launchActivity(null); @@ -115,7 +67,7 @@ public void loadBooks_LoadError_ShowRetryButton(){ } @Test - public void clickRetryButton_ReloadsBooksDisplayed(){ + public void clickRetryButton_ReloadsBooksDisplayed() { //Given FakeBookDetailApiImpl.setShouldFailService(true); activityActivityTestRule.launchActivity(null); diff --git a/app/src/androidTestMock/java/org.bookdash.android/presentation/splash/SplashScreenTest.java b/app/src/androidTestMock/java/org.bookdash.android/presentation/splash/SplashScreenTest.java index 4fbf970..84c52e8 100644 --- a/app/src/androidTestMock/java/org.bookdash.android/presentation/splash/SplashScreenTest.java +++ b/app/src/androidTestMock/java/org.bookdash.android/presentation/splash/SplashScreenTest.java @@ -10,7 +10,7 @@ import android.test.suitebuilder.annotation.LargeTest; import org.bookdash.android.data.settings.FakeSettingsApiImpl; -import org.bookdash.android.presentation.listbooks.ListBooksActivity; +import org.bookdash.android.presentation.main.MainActivity; import org.bookdash.android.presentation.splash.util.ElapsedTimeIdlingResource; import org.junit.After; import org.junit.Before; @@ -72,7 +72,7 @@ public void viewSplashScreenSecondTime_NavigateToListBooksAfter1000ms() throws I Espresso.registerIdlingResources(idlingResource); - intended(hasComponent(ListBooksActivity.class.getName())); + intended(hasComponent(MainActivity.class.getName())); Espresso.unregisterIdlingResources(idlingResource); @@ -90,7 +90,7 @@ public void viewSplashScreenFinish_StartListBooks(){ intending(hasComponent(MaterialTutorialActivity.class.getName())).respondWith(result); - intended(hasComponent(ListBooksActivity.class.getName())); + intended(hasComponent(MainActivity.class.getName())); Espresso.unregisterIdlingResources(idlingResource); } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b8df01d..6b00261 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,38 +1,39 @@ - + - - - + + + - - - - + + + + + - + + + - - + android:value=".presentation.main.MainActivity"/> + - - - + + + + - - - - + + + + - + android:pathPrefix="/books" + android:scheme="http"/> - - + - - - + + - - + + - + - - + + - + - + - - - + + + - + + android:value="@integer/google_play_services_version"/> + android:value="b2579d751611dc3b58788bad80a51ad37e140ecb"/> diff --git a/app/src/main/java/org/bookdash/android/data/books/BookDetailApi.java b/app/src/main/java/org/bookdash/android/data/books/BookDetailApi.java index 2ad0f8c..e8d94b0 100644 --- a/app/src/main/java/org/bookdash/android/data/books/BookDetailApi.java +++ b/app/src/main/java/org/bookdash/android/data/books/BookDetailApi.java @@ -16,12 +16,15 @@ public interface BookDetailApi { void getBooksForLanguages(String language, BookServiceCallback> bookServiceCallback); + void getDownloadedBooks(BookServiceCallback> bookServiceCallback); + interface BookServiceCallback { void onLoaded(T result); void onError(Exception error); } - interface BookServiceProgressCallback{ + + interface BookServiceProgressCallback { void onProgressChanged(int progress); } @@ -34,4 +37,5 @@ interface BookServiceProgressCallback{ void downloadBook(BookDetail bookDetail, BookServiceCallback downloadBookCallback, BookServiceProgressCallback bookServiceProgressCallback); + void deleteBook(BookDetail bookDetail, BookServiceCallback deleteBook); } diff --git a/app/src/main/java/org/bookdash/android/data/books/BookDetailApiImpl.java b/app/src/main/java/org/bookdash/android/data/books/BookDetailApiImpl.java index a7ee645..1d13b57 100644 --- a/app/src/main/java/org/bookdash/android/data/books/BookDetailApiImpl.java +++ b/app/src/main/java/org/bookdash/android/data/books/BookDetailApiImpl.java @@ -1,6 +1,7 @@ package org.bookdash.android.data.books; import android.support.annotation.NonNull; +import android.support.annotation.WorkerThread; import android.util.Log; import com.google.gson.Gson; @@ -12,19 +13,20 @@ import com.parse.ProgressCallback; import org.bookdash.android.BookDashApplication; +import org.bookdash.android.data.utils.FileManager; +import org.bookdash.android.data.utils.ZipManager; import org.bookdash.android.domain.pojo.Book; import org.bookdash.android.domain.pojo.BookContributor; import org.bookdash.android.domain.pojo.BookDetail; import org.bookdash.android.domain.pojo.Language; import org.bookdash.android.domain.pojo.gson.BookPages; -import org.bookdash.android.data.utils.FileManager; -import org.bookdash.android.data.utils.ZipManager; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.Executor; @@ -65,6 +67,38 @@ public void done(List list, ParseException e) { }); } + 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 getDownloadedBooks(final BookServiceCallback> bookServiceCallback) { + + ParseQuery queryBookDetail = ParseQuery.getQuery(BookDetail.class); + queryBookDetail.setCachePolicy(ParseQuery.CachePolicy.NETWORK_ELSE_CACHE); + queryBookDetail.include(BookDetail.BOOK_LANGUAGE_COL); + queryBookDetail.include(BookDetail.BOOK_ID_COL); + queryBookDetail.whereEqualTo(BookDetail.BOOK_ENABLED_COL, true); + queryBookDetail.addDescendingOrder(BookDetail.CREATED_AT_COL); + queryBookDetail.findInBackground(new FindCallback() { + @Override + public void done(List list, ParseException e) { + if (e != null) { + bookServiceCallback.onError(e); + return; + } + bookServiceCallback.onLoaded(filterOnlyDownloadedBooks(list)); + } + }); + } + @Override public void getBookDetail(String bookDetailId, final BookServiceCallback bookServiceCallback) { ParseQuery queryBookDetail = ParseQuery.getQuery(BookDetail.class); @@ -127,9 +161,9 @@ public void done(List list, ParseException e) { @Override public void downloadBook(final BookDetail bookInfo, @NonNull final BookServiceCallback downloadBookCallback, @NonNull final BookServiceProgressCallback progressCallback) { - if (bookInfo.isDownloadedAlready()){ + if (bookInfo.isDownloadedAlready()) { progressCallback.onProgressChanged(100); - downloadBookCallback.onLoaded(getBookPages(bookInfo.getFolderLocation(BookDashApplication.FILES_DIR) + File.separator + BookDetail.BOOK_INFO_FILE_NAME )); + downloadBookCallback.onLoaded(getBookPages(bookInfo.getFolderLocation() + File.separator + BookDetail.BOOK_INFO_FILE_NAME)); return; } bookInfo.getBookFile().getDataInBackground(new GetDataCallback() { @@ -161,6 +195,29 @@ public void done(Integer progressInt) { }); } + @Override + public void deleteBook(final BookDetail bookDetail, final BookServiceCallback deleteBook) { + Task.call(new Callable() { + @Override + public Void call() throws Exception { + try { + deleteLocalBook(bookDetail); + deleteBook.onLoaded(true); + } catch (Exception e) { + deleteBook.onError(e); + } + return null; + + } + }, DISK_EXECUTOR); + } + + @WorkerThread + private void deleteLocalBook(BookDetail bookDetail) { + FileManager.deleteFolder(bookDetail.getFolderLocation()); + FileManager.deleteFolder(BookDashApplication.FILES_DIR + "/" + bookDetail.getObjectId()); + } + private void getBookPages(final BookDetail bookInfo, final byte[] bytes, final BookServiceCallback bookServiceCallback) { Task.call(new Callable() { @Override @@ -177,19 +234,21 @@ public BookPages call() throws Exception { } + @WorkerThread private BookPages saveBook(byte[] bytes, BookDetail bookDetail) { String targetLocation = BookDashApplication.FILES_DIR + File.separator + bookDetail.getObjectId(); String fileLocation = BookDashApplication.FILES_DIR + File.separator + bookDetail.getBookFile().getName(); File f = new File("", targetLocation); - if (!f.exists()) { + if (!f.exists() || f.list().length == 0) { FileManager.saveFile(BookDashApplication.FILES_DIR, bytes, File.separator + bookDetail.getBookFile().getName()); ZipManager zipManager = new ZipManager(); zipManager.unzip(fileLocation, targetLocation); + FileManager.deleteFile(BookDashApplication.FILES_DIR, File.separator + bookDetail.getBookFile().getName()); } - return getBookPages(bookDetail.getFolderLocation(BookDashApplication.FILES_DIR) + File.separator + BookDetail.BOOK_INFO_FILE_NAME); + return getBookPages(bookDetail.getFolderLocation() + File.separator + BookDetail.BOOK_INFO_FILE_NAME); } 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 3717b1a..843900d 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,10 @@ */ public interface BookDetailRepository { - void getBooksForLanguage(String language, boolean downloadedOnly, @NonNull GetBooksForLanguageCallback booksForLanguageCallback); + + void getBooksForLanguage(@NonNull String language, @NonNull GetBooksForLanguageCallback booksForLanguageCallback); + + void getDownloadedBooks(GetBooksForLanguageCallback getBooksForLanguageCallback); interface GetBooksForLanguageCallback { void onBooksLoaded(List books); @@ -37,7 +40,6 @@ interface GetContributorsCallback { } - interface GetLanguagesCallback { void onLanguagesLoaded(List languages); @@ -46,7 +48,9 @@ interface GetLanguagesCallback { interface GetBookPagesCallback { void onBookPagesLoaded(BookPages bookPages); + void onBookPagesLoadError(Exception e); + void onBookPagesDownloadProgressUpdate(int progress); } @@ -57,4 +61,13 @@ interface GetBookPagesCallback { void getLanguages(@NonNull GetLanguagesCallback languagesCallback); void downloadBook(BookDetail bookDetail, @NonNull GetBookPagesCallback bookPagesCallback); + + void deleteBook(BookDetail bookDetail, @NonNull DeleteBookCallBack deleteBookCallBack); + + interface DeleteBookCallBack { + void onBookDeleted(BookDetail bookDetail); + + void onBookDeleteFailed(Exception e); + + } } 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 6bd32d6..591d837 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,7 +8,6 @@ import org.bookdash.android.domain.pojo.Language; import org.bookdash.android.domain.pojo.gson.BookPages; -import java.util.ArrayList; import java.util.List; /** @@ -26,16 +25,12 @@ public BookDetailRepositoryImpl(@NonNull BookDetailApi bookDetailApi) { } @Override - public void getBooksForLanguage(@NonNull String language, final boolean downloadedOnly, @NonNull final GetBooksForLanguageCallback booksForLanguageCallback) { + public void getBooksForLanguage(@NonNull String language, @NonNull final GetBooksForLanguageCallback booksForLanguageCallback) { bookDetailApi.getBooksForLanguages(language, new BookDetailApi.BookServiceCallback>() { @Override public void onLoaded(List result) { - if (downloadedOnly) { - booksForLanguageCallback.onBooksLoaded(filterOnlyDownloadedBooks(result)); - } else { - booksForLanguageCallback.onBooksLoaded(result); - } + booksForLanguageCallback.onBooksLoaded(result); } @Override @@ -45,16 +40,24 @@ public void onError(Exception error) { }); } - private List filterOnlyDownloadedBooks(List bookDetails){ - List bookDetailsDownloaded = new ArrayList<>(); + @Override + public void getDownloadedBooks(final GetBooksForLanguageCallback getBooksForLanguageCallback) { + bookDetailApi.getDownloadedBooks(new BookDetailApi.BookServiceCallback>() { - for (BookDetail b: bookDetails){ - if (b.isDownloadedAlready() || b.isDownloading()){ - bookDetailsDownloaded.add(b); + @Override + public void onLoaded(List result) { + getBooksForLanguageCallback.onBooksLoaded(result); } - } - return bookDetailsDownloaded; + + @Override + public void onError(Exception error) { + getBooksForLanguageCallback.onBooksLoadError(error); + } + }); + } + + @Override public void getBookDetail(String bookDetailId, @NonNull final GetBookDetailCallback bookDetailCallback) { bookDetailApi.getBookDetail(bookDetailId, new BookDetailApi.BookServiceCallback() { @@ -122,5 +125,20 @@ public void onProgressChanged(int progress) { }); } + @Override + public void deleteBook(final BookDetail bookDetail, @NonNull final DeleteBookCallBack deleteBookCallBack) { + bookDetailApi.deleteBook(bookDetail, new BookDetailApi.BookServiceCallback() { + @Override + public void onLoaded(Boolean result) { + deleteBookCallBack.onBookDeleted(bookDetail); + } + + @Override + public void onError(Exception error) { + deleteBookCallBack.onBookDeleteFailed(error); + } + }); + } + } diff --git a/app/src/main/java/org/bookdash/android/data/utils/FileManager.java b/app/src/main/java/org/bookdash/android/data/utils/FileManager.java index 4ed8409..d9e8fd0 100644 --- a/app/src/main/java/org/bookdash/android/data/utils/FileManager.java +++ b/app/src/main/java/org/bookdash/android/data/utils/FileManager.java @@ -20,7 +20,7 @@ public static void saveFile(String filesDir, byte[] bytes, String fileName) { FileOutputStream outputStream = null; try { - if (file.exists()){ + if (file.exists()) { Log.d(TAG, "File exists- not saving again"); return; } @@ -40,8 +40,32 @@ public static void saveFile(String filesDir, byte[] bytes, String fileName) { } } - public static boolean deleteFile(String filesDir, String fileName){ + + public static boolean deleteFile(String filesDir, String fileName) { File file = new File(filesDir, fileName); return file.delete(); } + + public static void deleteFolder(String folderLocation) { + deleteRecursive(new File(folderLocation)); + } + + private static void deleteRecursive(File fileOrDirectory) { + if (fileOrDirectory.isDirectory()) { + File[] list = fileOrDirectory.listFiles(); + if (list != null) { + for (int i = 0; i < list.length; i++) { + File tmpF = list[i]; + if (tmpF.isDirectory()) { + deleteRecursive(tmpF); + } + tmpF.delete(); + } + } + if (!fileOrDirectory.delete()) { + Log.e(TAG, "can't delete folder : " + fileOrDirectory); + } + } + } + } 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 aadedc9..d8dfde3 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 @@ -40,7 +40,7 @@ public BookDetail(String title, String bookCoverUrl, String objectId, Language l put(BOOK_COVER_PAGE_URL_COL, bookCoverUrl); put(BOOK_LANGUAGE_COL, languageId); setObjectId(objectId); - // put(OBJECT_ID, objectId); + // put(OBJECT_ID, objectId); //put(); } @@ -69,23 +69,30 @@ public String getAboutBook() { } - public String getFolderLocation(String filesDir) { - return getFolderLocation(new File(filesDir, getObjectId() + File.separator)); + public String getFolderLocation() { + return getFolderLocation(new File(BookDashApplication.FILES_DIR, getObjectId() + File.separator)); } private String getFolderLocation(File file) { - if (file.isDirectory() && (file.canRead())) { + if (file != null && file.isDirectory() && (file.canRead())) { File[] files = file.listFiles(); + if (files == null || files.length == 0 || files[0] == null) { + return null; + } return files[0].getAbsoluteFile().toString(); } return null; } public boolean isDownloadedAlready() { - String targetLocation = BookDashApplication.FILES_DIR + File.separator + getObjectId(); - File f = new File("", targetLocation); + String folderLocation = getFolderLocation(); + if (folderLocation == null || folderLocation.isEmpty()) { + return false; + } + File f = new File("", folderLocation); return f.exists(); } + public boolean isDownloading() { return isDownloading; } diff --git a/app/src/main/java/org/bookdash/android/presentation/about/AboutActivity.java b/app/src/main/java/org/bookdash/android/presentation/about/AboutActivity.java deleted file mode 100644 index 810ca4f..0000000 --- a/app/src/main/java/org/bookdash/android/presentation/about/AboutActivity.java +++ /dev/null @@ -1,69 +0,0 @@ -package org.bookdash.android.presentation.about; - -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.support.v7.app.ActionBar; -import android.support.v7.widget.Toolbar; -import android.text.Html; -import android.text.util.Linkify; -import android.view.View; -import android.widget.Button; -import android.widget.TextView; - -import org.bookdash.android.BookDashApplication; -import org.bookdash.android.R; -import org.bookdash.android.presentation.activity.BaseAppCompatActivity; - -public class AboutActivity extends BaseAppCompatActivity implements AboutContract.View { - - private AboutPresenter aboutPresenter; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_about); - aboutPresenter = new AboutPresenter(this); - - Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - ActionBar actionBar = getSupportActionBar(); - - if (actionBar != null) { - actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setTitle(R.string.about_heading); - } - - TextView textViewWhyBookDash = (TextView) findViewById(R.id.text_why_bookdash); - textViewWhyBookDash.setText(Html.fromHtml(getString(R.string.why_bookdash))); - Linkify.addLinks(textViewWhyBookDash, Linkify.ALL); - - TextView textViewHeading = (TextView) findViewById(R.id.text_view_about); - textViewHeading.setText(Html.fromHtml(getString(R.string.heading_about))); - Linkify.addLinks(textViewHeading, Linkify.ALL); - - Button learnMoreBookDash = (Button) findViewById(R.id.button_learn_more); - learnMoreBookDash.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - aboutPresenter.clickLearnMore(); - } - }); - - - textViewHeading.findFocus(); - } - - @Override - protected String getScreenName() { - return "About Screen"; - } - - - @Override - public void showLearnMorePage(String url) { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(Uri.parse(url)); - startActivity(intent); - } -} diff --git a/app/src/main/java/org/bookdash/android/presentation/about/AboutFragment.java b/app/src/main/java/org/bookdash/android/presentation/about/AboutFragment.java new file mode 100644 index 0000000..00ac6eb --- /dev/null +++ b/app/src/main/java/org/bookdash/android/presentation/about/AboutFragment.java @@ -0,0 +1,101 @@ +package org.bookdash.android.presentation.about; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.text.Html; +import android.text.util.Linkify; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; + +import org.bookdash.android.R; +import org.bookdash.android.presentation.main.NavDrawerInterface; + +public class AboutFragment extends Fragment implements AboutContract.View { + + private AboutPresenter aboutPresenter; + private NavDrawerInterface navDrawerInterface; + + public static AboutFragment newInstance() { + + Bundle args = new Bundle(); + + AboutFragment fragment = new AboutFragment(); + fragment.setArguments(args); + return fragment; + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + return inflater.inflate(R.layout.activity_about, container, false); + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + aboutPresenter = new AboutPresenter(this); + + Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar); + navDrawerInterface.setToolbar(toolbar); + ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar(); + + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setHomeAsUpIndicator(R.drawable.ic_menu_black_24dp); + actionBar.setTitle(R.string.about_heading); + } + + TextView textViewWhyBookDash = (TextView) view.findViewById(R.id.text_why_bookdash); + textViewWhyBookDash.setText(Html.fromHtml(getString(R.string.why_bookdash))); + Linkify.addLinks(textViewWhyBookDash, Linkify.ALL); + + TextView textViewHeading = (TextView) view.findViewById(R.id.text_view_about); + textViewHeading.setText(Html.fromHtml(getString(R.string.heading_about))); + Linkify.addLinks(textViewHeading, Linkify.ALL); + + Button learnMoreBookDash = (Button) view.findViewById(R.id.button_learn_more); + learnMoreBookDash.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + aboutPresenter.clickLearnMore(); + } + }); + + + textViewHeading.findFocus(); + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + if (context instanceof NavDrawerInterface) { + navDrawerInterface = (NavDrawerInterface) context; + + } + } + + @Override + public void onDetach() { + super.onDetach(); + navDrawerInterface = null; + } + + + @Override + public void showLearnMorePage(String url) { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(url)); + startActivity(intent); + } +} 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 4c1da09..b3f4e32 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 @@ -44,6 +44,7 @@ import com.parse.ParseException; import com.parse.ParseFile; +import org.bookdash.android.BR; import org.bookdash.android.Injection; import org.bookdash.android.R; import org.bookdash.android.domain.pojo.BookContributor; @@ -52,7 +53,6 @@ import org.bookdash.android.domain.pojo.gson.BookPages; import org.bookdash.android.presentation.activity.BaseAppCompatActivity; import org.bookdash.android.presentation.readbook.BookDetailActivity; -import org.bookdash.android.BR; import java.util.List; @@ -160,7 +160,7 @@ public void onClick(View v) { showSnackBarMessage(R.string.book_not_available); return; } - if (!bookInfo.isDownloadedAlready()){ + if (!bookInfo.isDownloadedAlready()) { floatingActionButton.resetIcon(); floatingActionButton.showProgress(true); floatingActionButton.setProgress(0, true); @@ -207,6 +207,9 @@ public boolean onOptionsItemSelected(MenuItem item) { if (id == R.id.action_share_book) { actionsListener.shareBookClicked(bookInfo); return true; + } else if (id == android.R.id.home) { + onBackPressed(); + return true; } return super.onOptionsItemSelected(item); @@ -231,7 +234,7 @@ protected void onNewIntent(Intent intent) { Log.d(TAG, "onNewIntent() called: action" + action); if (Intent.ACTION_VIEW.equals(action) && data != null) { - Uri uri = Uri.parse(data); + 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); @@ -300,10 +303,12 @@ public void showSnackBarMessage(int message) { Snackbar.make(scrollView, message, Snackbar.LENGTH_LONG).show(); } - private int progress= 0; + + private int progress = 0; + @Override public void showDownloadProgress(final int downloadProgress) { - if (progress == downloadProgress){ + if (progress == downloadProgress) { return; } progress = downloadProgress; @@ -339,7 +344,7 @@ public void setToolbarTitle(String title) { @Override public void setBookInfoBinding(BookDetail bookInfo) { - if (client!=null) { + if (client != null) { client.connect(); } @@ -352,7 +357,7 @@ public void setBookInfoBinding(BookDetail bookInfo) { 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) { + if (viewAction != null && client != null) { AppIndex.AppIndexApi.start(client, viewAction); } } @@ -401,7 +406,7 @@ public void setStatusBarColor(int color) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Window window = getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); - window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); + // window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); window.setStatusBarColor(color); } } 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 1c94461..83edd79 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 @@ -102,7 +102,7 @@ public void onBookPagesLoaded(BookPages bookPages) { return; } bookInfo.setIsDownloading(false); - booksView.openBook(bookInfo, bookPages, bookInfo.getFolderLocation(BookDashApplication.FILES_DIR)); + booksView.openBook(bookInfo, bookPages, bookInfo.getFolderLocation()); } @Override diff --git a/app/src/main/java/org/bookdash/android/presentation/downloads/DownloadsAdapter.java b/app/src/main/java/org/bookdash/android/presentation/downloads/DownloadsAdapter.java new file mode 100644 index 0000000..3354570 --- /dev/null +++ b/app/src/main/java/org/bookdash/android/presentation/downloads/DownloadsAdapter.java @@ -0,0 +1,62 @@ +package org.bookdash.android.presentation.downloads; + +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.bumptech.glide.Glide; + +import org.bookdash.android.R; +import org.bookdash.android.domain.pojo.BookDetail; + +import java.util.List; + +public class DownloadsAdapter extends RecyclerView.Adapter { + + private final View.OnClickListener deleteClickListener; + private final View.OnClickListener bookClickListener; + private List bookList; + private Context context; + + public DownloadsAdapter(List bookList, Context context, View.OnClickListener deleteClickListener, View.OnClickListener bookClickListener) { + this.bookList = bookList; + this.context = context; + this.deleteClickListener = deleteClickListener; + this.bookClickListener = bookClickListener; + } + + @Override + public DownloadsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View v = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.list_item_download, parent, false); + + return new DownloadsViewHolder(v); + } + + @Override + public void onBindViewHolder(DownloadsViewHolder holder, int position) { + BookDetail book = bookList.get(position); + holder.downloadTitleTextView.setText(book.getBookTitle()); + Glide.with(context).load(book.getBookCoverUrl()).into(holder.downloadImageTextView); + holder.downloadProgressTextView.setText(book.getLanguage().getLanguageName()); + holder.downloadActionButtonView.setOnClickListener(deleteClickListener); + holder.book = book; + holder.downloadActionButtonView.setTag(holder); + holder.downloadRelativeLayout.setOnClickListener(bookClickListener); + holder.downloadRelativeLayout.setTag(holder); + } + + @Override + public int getItemCount() { + if (bookList == null) { + return 0; + } + return bookList.size(); + } + + public void setBooks(List books) { + this.bookList = books; + } +} diff --git a/app/src/main/java/org/bookdash/android/presentation/downloads/DownloadsContract.java b/app/src/main/java/org/bookdash/android/presentation/downloads/DownloadsContract.java new file mode 100644 index 0000000..54b563d --- /dev/null +++ b/app/src/main/java/org/bookdash/android/presentation/downloads/DownloadsContract.java @@ -0,0 +1,31 @@ +package org.bookdash.android.presentation.downloads; + + +import org.bookdash.android.domain.pojo.BookDetail; + +import java.util.List; + +public interface DownloadsContract { + + interface View { + + void showDownloadedBooks(List books); + + void showLoading(boolean show); + + void showErrorScreen(boolean show, String errorMessage, boolean showRetryButton); + + void showSnackBarError(int message); + + void showSnackBarError(String message); + + void showNoBooksDownloadedMessage(); + } + + interface UserActions { + void loadListDownloads(); + + void deleteDownload(BookDetail bookDetail); + } + +} diff --git a/app/src/main/java/org/bookdash/android/presentation/downloads/DownloadsFragment.java b/app/src/main/java/org/bookdash/android/presentation/downloads/DownloadsFragment.java new file mode 100644 index 0000000..86868d9 --- /dev/null +++ b/app/src/main/java/org/bookdash/android/presentation/downloads/DownloadsFragment.java @@ -0,0 +1,233 @@ +package org.bookdash.android.presentation.downloads; + +import android.app.Activity; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.design.widget.Snackbar; +import android.support.v4.app.Fragment; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.Toolbar; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; + +import org.bookdash.android.Injection; +import org.bookdash.android.R; +import org.bookdash.android.domain.pojo.BookDetail; +import org.bookdash.android.presentation.bookinfo.BookInfoActivity; +import org.bookdash.android.presentation.main.NavDrawerInterface; + +import java.util.List; + +import fr.castorflex.android.circularprogressbar.CircularProgressBar; + + +public class DownloadsFragment extends Fragment implements DownloadsContract.View { + + private RecyclerView listDownloadsRecyclerView; + private DownloadsContract.UserActions downloadsPresenter; + private DownloadsAdapter downloadsAdapter; + private LinearLayout linearLayoutErrorScreen; + private Button buttonRetry; + private TextView textViewErrorMessage; + private CircularProgressBar circularProgressBar; + private NavDrawerInterface navDrawerInterface; + private Toolbar toolbar; + + + public static DownloadsFragment newInstance() { + return new DownloadsFragment(); + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_downloads, container, false); + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + listDownloadsRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view_list_downloads); + downloadsPresenter = new DownloadsPresenter(Injection.provideBookRepo(), this); + downloadsAdapter = new DownloadsAdapter(null, getActivity(), new View.OnClickListener() { + @Override + public void onClick(View v) { + DownloadsViewHolder downloadsViewHolder = (DownloadsViewHolder) v.getTag(); + showDeleteDialog(downloadsViewHolder.book); + + } + }, new View.OnClickListener() { + @Override + public void onClick(View v) { + DownloadsViewHolder downloadsViewHolder = (DownloadsViewHolder) v.getTag(); + showBookDetails(downloadsViewHolder.book); + } + }); + listDownloadsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false)); + listDownloadsRecyclerView.setAdapter(downloadsAdapter); + + linearLayoutErrorScreen = (LinearLayout) view.findViewById(R.id.linear_layout_error); + buttonRetry = (Button) view.findViewById(R.id.button_retry); + textViewErrorMessage = (TextView) view.findViewById(R.id.text_view_error_screen); + circularProgressBar = (CircularProgressBar) view.findViewById(R.id.fragment_loading_downloads); + toolbar = (Toolbar) view.findViewById(R.id.toolbar); + if (navDrawerInterface != null) { + navDrawerInterface.setToolbar(toolbar); + + } + + final ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar(); + + if (actionBar != null) { + actionBar.setHomeAsUpIndicator(R.drawable.ic_menu_black_24dp); + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setTitle(getString(R.string.fragment_title_downloads)); + } + downloadsPresenter.loadListDownloads(); + + setHasOptionsMenu(false); + } + + private void showBookDetails(BookDetail bookDetail) { + Intent intent = new Intent(getActivity(), BookInfoActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(BookInfoActivity.BOOK_PARCEL, bookDetail.toBookParcelable()); + startActivity(intent); + } + + private void showDeleteDialog(final BookDetail bookToDelete) { + AlertDialog.Builder builder = + new AlertDialog.Builder(getActivity(), R.style.AppCompatAlertDialogStyle); + builder.setTitle(getString(R.string.delete_book_confirmation)); + builder.setMessage(getString(R.string.downloads_are_you_sure, bookToDelete.getBookTitle())); + builder.setPositiveButton(getString(R.string.delete_ok), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + downloadsPresenter.deleteDownload(bookToDelete); + } + }); + builder.setNegativeButton(getString(R.string.delete_cancel), null); + builder.show(); + } + + @Override + public void showDownloadedBooks(List books) { + downloadsAdapter.setBooks(books); + downloadsAdapter.notifyDataSetChanged(); + listDownloadsRecyclerView.setVisibility(View.VISIBLE); + } + + + @Override + 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); + + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + + } + + @Override + public void showLoading(final boolean visible) { + runUiThread(new Runnable() { + @Override + public void run() { + + circularProgressBar.setVisibility(visible ? View.VISIBLE : View.GONE); + listDownloadsRecyclerView.setVisibility(visible ? View.GONE : View.VISIBLE); + + } + }); + } + + @Override + public void showSnackBarError(final int message) { + runUiThread(new Runnable() { + @Override + public void run() { + Snackbar.make(listDownloadsRecyclerView, message, Snackbar.LENGTH_LONG).show(); + + } + }); + } + + private void runUiThread(Runnable runnable) { + Activity activity = getActivity(); + if (activity == null) { + return; + } + activity.runOnUiThread(runnable); + } + + @Override + public void showSnackBarError(final String message) { + runUiThread(new Runnable() { + @Override + public void run() { + Snackbar.make(listDownloadsRecyclerView, message, Snackbar.LENGTH_LONG).show(); + + } + }); + + } + + @Override + public void showNoBooksDownloadedMessage() { + showErrorScreen(true, getString(R.string.no_books_downloaded), false); + listDownloadsRecyclerView.setVisibility(View.GONE); + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + if (context instanceof NavDrawerInterface) { + navDrawerInterface = (NavDrawerInterface) context; + + } + } + + @Override + public void onDetach() { + super.onDetach(); + navDrawerInterface = null; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case android.R.id.home: + if (navDrawerInterface != null) { + navDrawerInterface.openNavDrawer(); + } + return true; + + } + return super.onOptionsItemSelected(item); + } + + +} diff --git a/app/src/main/java/org/bookdash/android/presentation/downloads/DownloadsPresenter.java b/app/src/main/java/org/bookdash/android/presentation/downloads/DownloadsPresenter.java new file mode 100644 index 0000000..43ac73e --- /dev/null +++ b/app/src/main/java/org/bookdash/android/presentation/downloads/DownloadsPresenter.java @@ -0,0 +1,55 @@ +package org.bookdash.android.presentation.downloads; + +import org.bookdash.android.data.books.BookDetailRepository; +import org.bookdash.android.domain.pojo.BookDetail; + +import java.util.List; + +public class DownloadsPresenter implements DownloadsContract.UserActions { + private static final String TAG = "DownloadsPresenter"; + private final BookDetailRepository bookRepository; + private final DownloadsContract.View view; + + public DownloadsPresenter(BookDetailRepository bookRepository, DownloadsContract.View downloadsView) { + this.bookRepository = bookRepository; + this.view = downloadsView; + } + + public void loadListDownloads() { + view.showLoading(true); + bookRepository.getDownloadedBooks(new BookDetailRepository.GetBooksForLanguageCallback() { + @Override + public void onBooksLoaded(List books) { + view.showLoading(false); + if (books.isEmpty() || books.size() == 0) { + view.showNoBooksDownloadedMessage(); + return; + } + view.showDownloadedBooks(books); + + } + + @Override + public void onBooksLoadError(Exception e) { + view.showErrorScreen(true, e.getMessage(), true); + view.showLoading(false); + } + }); + } + + @Override + public void deleteDownload(BookDetail bookDetail) { + bookRepository.deleteBook(bookDetail, new BookDetailRepository.DeleteBookCallBack() { + + @Override + public void onBookDeleted(BookDetail bookDetail) { + loadListDownloads(); + } + + @Override + public void onBookDeleteFailed(Exception e) { + view.showSnackBarError(e.getMessage()); + } + }); + } +} diff --git a/app/src/main/java/org/bookdash/android/presentation/downloads/DownloadsViewHolder.java b/app/src/main/java/org/bookdash/android/presentation/downloads/DownloadsViewHolder.java new file mode 100644 index 0000000..2c39142 --- /dev/null +++ b/app/src/main/java/org/bookdash/android/presentation/downloads/DownloadsViewHolder.java @@ -0,0 +1,29 @@ +package org.bookdash.android.presentation.downloads; + +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import org.bookdash.android.R; +import org.bookdash.android.domain.pojo.BookDetail; + +public class DownloadsViewHolder extends RecyclerView.ViewHolder { + TextView downloadTitleTextView; + TextView downloadProgressTextView; + ImageView downloadImageTextView; + ImageButton downloadActionButtonView; + RelativeLayout downloadRelativeLayout; + BookDetail book; + + public DownloadsViewHolder(View itemView) { + super(itemView); + downloadActionButtonView = (ImageButton) itemView.findViewById(R.id.image_button_delete_book); + downloadImageTextView = (ImageView) itemView.findViewById(R.id.image_view_download_book_cover); + downloadProgressTextView = (TextView) itemView.findViewById(R.id.download_progress); + downloadTitleTextView = (TextView) itemView.findViewById(R.id.text_view_book_title_download); + downloadRelativeLayout = (RelativeLayout)itemView.findViewById(R.id.download_relative_layout); + } +} 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 0c1e70f..4fb012c 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 @@ -5,12 +5,15 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.RelativeLayout; import com.bumptech.glide.Glide; import org.bookdash.android.R; +import org.bookdash.android.domain.pojo.BookDetail; + import java.util.List; -import org.bookdash.android.domain.pojo.*; + /** * @author Rebecca Franks * @since 2015/07/16 2:06 PM @@ -21,7 +24,6 @@ public class BookAdapter extends RecyclerView.Adapter { private View.OnClickListener onClickListener; - public BookAdapter(List bookDetails, Context context, View.OnClickListener onClickListener) { this.bookDetails = bookDetails; this.context = context; @@ -30,7 +32,7 @@ public BookAdapter(List bookDetails, Context context, View.OnClickLi @Override public BookViewHolder onCreateViewHolder(ViewGroup parent, - int viewType) { + int viewType) { View v = LayoutInflater.from(parent.getContext()) .inflate(R.layout.list_item_book, parent, false); return new BookViewHolder(v); @@ -40,12 +42,14 @@ public BookViewHolder onCreateViewHolder(ViewGroup parent, public void onBindViewHolder(BookViewHolder holder, int position) { BookDetail bookDetail = bookDetails.get(position); holder.bookTitle.setText(bookDetail.getBookTitle()); - Glide.with(context).load(bookDetail.getBookCoverUrl()).into(holder.bookCover); + Glide.with(context).load(bookDetail.getBookCoverUrl()).placeholder(R.drawable.bookdash_placeholder).error(R.drawable.bookdash_placeholder).into(holder.bookCover); holder.bookDetail = bookDetail; holder.downloadedIcon.setVisibility(bookDetail.isDownloadedAlready() ? View.VISIBLE : View.INVISIBLE); holder.cardContainer.setTag(holder); holder.cardContainer.setOnClickListener(onClickListener); - + // int width = holder.cardContainer.getMeasuredWidth(); + /* holder.bookCover.setLayoutParams(new RelativeLayout.LayoutParams(width, width)); + holder.bookCover.invalidate();*/ } @Override 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 298d271..bc15e22 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 @@ -15,7 +15,6 @@ */ public class BookViewHolder extends RecyclerView.ViewHolder { - public TextView author; public TextView bookTitle; public ImageView bookCover; public CardView cardContainer; @@ -27,7 +26,6 @@ public BookViewHolder(View v) { cardContainer = (CardView) v.findViewById(R.id.card_view); bookTitle = (TextView) v.findViewById(R.id.textViewBookName); bookCover = (ImageView) v.findViewById(R.id.imageViewBookCover); - author = (TextView) v.findViewById(R.id.textViewAuthor); downloadedIcon = (ImageView) v.findViewById(R.id.imageViewBookDownloaded); } } 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 ccbf536..ebb30f2 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 @@ -11,9 +11,6 @@ public interface ListBooksContract { interface View { - void showThanksPopover(); - void showAboutPage(); - void showRatingPlayStore(); void showErrorScreen(boolean show, String errorMessage, boolean showRetryButton); void showLoading(boolean visible); void showBooks(List bookDetailList); @@ -23,8 +20,8 @@ interface View { interface UserActionsListener { void loadLanguages(); - void saveSelectedLanguage(int indexOfLanguage, boolean downloadOnly); - void loadBooksForLanguagePreference(boolean downloadedOnly); + void saveSelectedLanguage(int indexOfLanguage); + void loadBooksForLanguagePreference(); void clickOpenLanguagePopover(); } diff --git a/app/src/main/java/org/bookdash/android/presentation/listbooks/ListBooksFragment.java b/app/src/main/java/org/bookdash/android/presentation/listbooks/ListBooksFragment.java new file mode 100644 index 0000000..f6cca56 --- /dev/null +++ b/app/src/main/java/org/bookdash/android/presentation/listbooks/ListBooksFragment.java @@ -0,0 +1,215 @@ +package org.bookdash.android.presentation.listbooks; + +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.design.widget.Snackbar; +import android.support.v4.app.Fragment; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.Toolbar; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; + +import org.bookdash.android.Injection; +import org.bookdash.android.R; +import org.bookdash.android.domain.pojo.BookDetail; +import org.bookdash.android.presentation.bookinfo.BookInfoActivity; +import org.bookdash.android.presentation.main.NavDrawerInterface; +import org.bookdash.android.presentation.widget.SpacesItemDecoration; + +import java.util.List; + +import fr.castorflex.android.circularprogressbar.CircularProgressBar; + + +public class ListBooksFragment extends Fragment implements ListBooksContract.View { + + private static final String TAG = ListBooksFragment.class.getCanonicalName(); + private static final int BOOK_DETAIL_REQUEST_CODE = 43; + private ListBooksContract.UserActionsListener actionsListener; + private Button buttonRetry; + private RecyclerView mRecyclerView; + private CircularProgressBar circularProgressBar; + private LinearLayout linearLayoutErrorScreen; + private TextView textViewErrorMessage; + private Toolbar toolbar; + private NavDrawerInterface navDrawerInterface; + private BookAdapter bookAdapter; + + public static Fragment newInstance() { + return new ListBooksFragment(); + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_list_books, container, false); + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + actionsListener = new ListBooksPresenter(this, Injection.provideBookRepo(), Injection.provideSettingsRepo(getActivity())); + + circularProgressBar = (CircularProgressBar) view.findViewById(R.id.activity_loading_books); + linearLayoutErrorScreen = (LinearLayout) view.findViewById(R.id.linear_layout_error); + buttonRetry = (Button) view.findViewById(R.id.button_retry); + textViewErrorMessage = (TextView) view.findViewById(R.id.text_view_error_screen); + mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view_books); + mRecyclerView.setLayoutManager(new GridLayoutManager(getActivity(), getContext().getResources().getInteger(R.integer.book_span))); + mRecyclerView.setHasFixedSize(true); + mRecyclerView.addItemDecoration(new SpacesItemDecoration(getResources().getInteger(R.integer.list_books_space))); + buttonRetry.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Log.d(TAG, "Retry button clicked"); + actionsListener.loadBooksForLanguagePreference(); + } + }); + toolbar = (Toolbar) view.findViewById(R.id.toolbar); + if (navDrawerInterface != null) { + navDrawerInterface.setToolbar(toolbar); + } + final ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar(); + + + if (actionBar != null) { + actionBar.setHomeAsUpIndicator(R.drawable.ic_menu_black_24dp); + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setTitle(getString(R.string.book_dash)); + } + actionsListener.loadLanguages(); + actionsListener.loadBooksForLanguagePreference(); + setHasOptionsMenu(true); + } + + + private View.OnClickListener bookClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + openBookDetails(v); + } + }; + + + public void openBookDetails(View v) { + Intent intent = new Intent(getActivity(), 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()); + startActivityForResult(intent, BOOK_DETAIL_REQUEST_CODE); + + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + Log.d(TAG, "onActivityResult() called with: " + "requestCode = [" + requestCode + "], resultCode = [" + resultCode + "], data = [" + data + "]"); + + if (requestCode == BOOK_DETAIL_REQUEST_CODE) { + if (bookAdapter != null) { + bookAdapter.notifyDataSetChanged(); + } + } + } + + @Override + 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); + + } + + @Override + public void showLoading(boolean visible) { + circularProgressBar.setVisibility(visible ? View.VISIBLE : View.GONE); + mRecyclerView.setVisibility(visible ? View.GONE : View.VISIBLE); + + } + + @Override + public void showBooks(List bookDetailList) { + if (bookDetailList.isEmpty()) { + showErrorScreen(true, getString(R.string.no_books_available), true); + } + bookAdapter = new BookAdapter(bookDetailList, ListBooksFragment.this.getActivity(), bookClickListener); + mRecyclerView.setAdapter(bookAdapter); + + } + + @Override + public void showSnackBarError(int message) { + Snackbar.make(mRecyclerView, message, Snackbar.LENGTH_LONG).show(); + } + + private DialogInterface.OnClickListener languageClickListener = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (dialog != null) { + dialog.dismiss(); + } + + actionsListener.saveSelectedLanguage(which); + + } + }; + + @Override + public void showLanguagePopover(String[] languages, int selected) { + AlertDialog alertDialogLanguages = new AlertDialog.Builder(getActivity()) + .setTitle(getString(R.string.language_selection_heading)) + .setSingleChoiceItems(languages, selected, languageClickListener).create(); + alertDialogLanguages.show(); + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + if (context instanceof NavDrawerInterface) { + navDrawerInterface = (NavDrawerInterface) context; + + } + } + + @Override + public void onDetach() { + super.onDetach(); + navDrawerInterface = null; + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.menu_main, menu); + } + + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.action_language_choice) { + actionsListener.clickOpenLanguagePopover(); + return true; + } + return super.onOptionsItemSelected(item); + } +} 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 f24280f..59d1e2e 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 @@ -25,16 +25,17 @@ public ListBooksPresenter(ListBooksContract.View listBooksView, BookDetailReposi this.bookDetailRepository = bookDetailRepository; this.settingsRepository = settingsRepository; } + @Override - public void loadBooksForLanguagePreference(boolean downloadedOnly) { + public void loadBooksForLanguagePreference() { String languagePreference = settingsRepository.getLanguagePreference(); - loadBooksForLanguage(languagePreference, downloadedOnly); + loadBooksForLanguage(languagePreference); } - private void loadBooksForLanguage(String language, boolean downloadedOnly) { + private void loadBooksForLanguage(String language) { listBooksView.showLoading(true); listBooksView.showErrorScreen(false, "", false); - bookDetailRepository.getBooksForLanguage(language,downloadedOnly, new BookDetailRepository.GetBooksForLanguageCallback() { + bookDetailRepository.getBooksForLanguage(language, new BookDetailRepository.GetBooksForLanguageCallback() { @Override public void onBooksLoaded(List books) { listBooksView.showLoading(false); @@ -69,10 +70,10 @@ public void onLanguagesLoadError(Exception e) { } @Override - public void saveSelectedLanguage(int indexOfLanguage, boolean downloadOnly) { + public void saveSelectedLanguage(int indexOfLanguage) { settingsRepository.saveLanguagePreference(languages.get(indexOfLanguage).getLanguageName()); - loadBooksForLanguage(languages.get(indexOfLanguage).getLanguageName(), downloadOnly); + loadBooksForLanguage(languages.get(indexOfLanguage).getLanguageName()); } @Override @@ -95,5 +96,4 @@ public void clickOpenLanguagePopover() { } - } diff --git a/app/src/main/java/org/bookdash/android/presentation/listbooks/SquareImageView.java b/app/src/main/java/org/bookdash/android/presentation/listbooks/SquareImageView.java new file mode 100644 index 0000000..86eb30f --- /dev/null +++ b/app/src/main/java/org/bookdash/android/presentation/listbooks/SquareImageView.java @@ -0,0 +1,21 @@ +package org.bookdash.android.presentation.listbooks; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.ImageView; + + +public class SquareImageView extends ImageView { + public SquareImageView(Context context) { + super(context); + } + + public SquareImageView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onMeasure(int widthSpec, int heightSpec) { + super.onMeasure(widthSpec, widthSpec); + } +} diff --git a/app/src/main/java/org/bookdash/android/presentation/listbooks/ListBooksActivity.java b/app/src/main/java/org/bookdash/android/presentation/main/MainActivity.java similarity index 52% rename from app/src/main/java/org/bookdash/android/presentation/listbooks/ListBooksActivity.java rename to app/src/main/java/org/bookdash/android/presentation/main/MainActivity.java index 9186f50..b4b9bd0 100644 --- a/app/src/main/java/org/bookdash/android/presentation/listbooks/ListBooksActivity.java +++ b/app/src/main/java/org/bookdash/android/presentation/main/MainActivity.java @@ -1,30 +1,26 @@ -package org.bookdash.android.presentation.listbooks; +package org.bookdash.android.presentation.main; import android.content.ActivityNotFoundException; -import android.content.DialogInterface; import android.content.Intent; -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.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; 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; import android.text.Html; import android.text.method.LinkMovementMethod; import android.util.Log; -import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.widget.Button; -import android.widget.LinearLayout; import android.widget.TextView; - import com.google.android.gms.appinvite.AppInvite; import com.google.android.gms.appinvite.AppInviteInvitation; import com.google.android.gms.appinvite.AppInviteInvitationResult; @@ -33,38 +29,31 @@ import com.google.android.gms.common.api.ResultCallback; import org.bookdash.android.BuildConfig; -import org.bookdash.android.Injection; import org.bookdash.android.R; -import org.bookdash.android.presentation.about.AboutActivity; +import org.bookdash.android.presentation.about.AboutFragment; import org.bookdash.android.presentation.activity.BaseAppCompatActivity; -import org.bookdash.android.presentation.bookinfo.BookInfoActivity; -import org.bookdash.android.domain.pojo.BookDetail; -import org.bookdash.android.presentation.view.AutofitRecyclerView; - -import java.util.List; - -import fr.castorflex.android.circularprogressbar.CircularProgressBar; +import org.bookdash.android.presentation.downloads.DownloadsFragment; +import org.bookdash.android.presentation.listbooks.ListBooksFragment; -public class ListBooksActivity extends BaseAppCompatActivity implements ListBooksContract.View { +public class MainActivity extends BaseAppCompatActivity implements MainContract.MainView, NavDrawerInterface { private static final int INVITE_REQUEST_CODE = 1; - private ListBooksContract.UserActionsListener actionsListener; + private static final String TAG = "MainActivity"; private GoogleApiClient googleApiClient; - private Toolbar toolbar; private DrawerLayout drawerLayout; private NavigationView navigationView; - private Button buttonRetry; + private MainContract.MainUserActions actionsListener; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - actionsListener = new ListBooksPresenter(this, Injection.provideBookRepo(), Injection.provideSettingsRepo(this)); drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); - toolbar = (Toolbar) findViewById(R.id.toolbar); + navigationView = (NavigationView) findViewById(R.id.navigation_view); - setSupportActionBar(toolbar); + + actionsListener = new MainPresenter(this); final ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { @@ -73,46 +62,12 @@ protected void onCreate(Bundle savedInstanceState) { } setUpNavDrawer(); - circularProgressBar = (CircularProgressBar) findViewById(R.id.activity_loading_books); - linearLayoutErrorScreen = (LinearLayout) findViewById(R.id.linear_layout_error); - buttonRetry = (Button) findViewById(R.id.button_retry); - textViewErrorMessage = (TextView) findViewById(R.id.text_view_error_screen); - mRecyclerView = (AutofitRecyclerView) findViewById(R.id.recycler_view_books); - - mRecyclerView.setHasFixedSize(true); - mRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() { - @Override - public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { - outRect.bottom = 8; - outRect.right = 8; - outRect.left = 8; - outRect.top = 8; - } - }); - buttonRetry.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Log.d(TAG, "Retry button clicked"); - actionsListener.loadBooksForLanguagePreference(false); - } - }); - - actionsListener.loadLanguages(); - actionsListener.loadBooksForLanguagePreference(false); checkIfComingFromInvite(); + showAllBooks(); } 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 @@ -158,14 +113,11 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { } private void showDownloadedBooks() { - downloadOnly = true; - - actionsListener.loadBooksForLanguagePreference(downloadOnly); + actionsListener.clickViewDownloadBooks(); } private void showAllBooks() { - downloadOnly = false; - actionsListener.loadBooksForLanguagePreference(downloadOnly); + actionsListener.clickViewAllBooks(); } private void showSettingsScreen() { @@ -182,13 +134,6 @@ public void onConnectionFailed(ConnectionResult connectionResult) { } }) - /* .enableAutoManage(this, new GoogleApiClient.OnConnectionFailedListener() { - @Override - public void onConnectionFailed(ConnectionResult connectionResult) { - Log.d(TAG, "onConnectionFailed: onResult:" + connectionResult.toString()); - - } - })*/ .build(); if (googleApiClient != null) { googleApiClient.connect(); @@ -204,12 +149,6 @@ public void onResult(AppInviteInvitationResult result) { } } - private static final String TAG = ListBooksActivity.class.getCanonicalName(); - - private AutofitRecyclerView mRecyclerView; - private CircularProgressBar circularProgressBar; - private LinearLayout linearLayoutErrorScreen; - private TextView textViewErrorMessage; @Override protected void onStop() { @@ -229,26 +168,12 @@ protected void onStart() { } } - private View.OnClickListener bookClickListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - - // final BookDetail bookDetail = (BookDetail) v.getTag(); - - openBookDetails(v); - } - }; @Override - protected String getScreenName() { - return "BookListingScreen"; + public String getScreenName() { + return "MainActivity"; } - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.menu_main, menu); - return true; - } @Override public boolean onOptionsItemSelected(MenuItem item) { @@ -259,10 +184,6 @@ public boolean onOptionsItemSelected(MenuItem item) { showAboutPage(); return true; } - if (id == R.id.action_language_choice) { - actionsListener.clickOpenLanguagePopover(); - return true; - } if (id == R.id.action_rate_app) { showRatingPlayStore(); return true; @@ -287,7 +208,7 @@ private void openInvitePage() { .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(); + Snackbar.make(navigationView, R.string.common_google_play_services_api_unavailable_text, Snackbar.LENGTH_LONG).show(); } } @@ -311,34 +232,6 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { } } - 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, downloadOnly); - - /* tracker.send(new HitBuilders.EventBuilder() - .setCategory("LanguageChange") - .setAction(languages.get(which).getLanguageName()) - .build());*/ - - - } - }; - - @Override - public void showLanguagePopover(String[] languages, int selected) { - AlertDialog alertDialogLanguages = new AlertDialog.Builder(this) - .setTitle(getString(R.string.language_selection_heading)) - .setSingleChoiceItems(languages, selected, languageClickListener).create(); - alertDialogLanguages.show(); - } - - @Override public void showThanksPopover() { AlertDialog.Builder thanksDialog = new AlertDialog.Builder(this); @@ -353,39 +246,11 @@ public void showThanksPopover() { @Override public void showAboutPage() { - Intent intent = new Intent(ListBooksActivity.this, AboutActivity.class); - - startActivity(intent); - } - - - public void openBookDetails(View v) { - /* if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) { - ImageView imageView = - ((BookViewHolder) v.getTag()).bookCover; - imageView.setTransitionName(getString(R.string.transition_book)); - v.setBackgroundColor( - ContextCompat.getColor(ListBooksActivity.this, android.R.color.transparent)); - ActivityOptions options = - ActivityOptions.makeSceneTransitionAnimation(ListBooksActivity.this, - Pair.create((View) imageView, getString(R.string.transition_book))); - - - 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, options.toBundle()); - - } else {*/ - - 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); - - /* }*/ + FragmentManager fragmentManager = getSupportFragmentManager(); + FragmentTransaction ft = fragmentManager.beginTransaction(); + Fragment f = AboutFragment.newInstance(); + ft.replace(R.id.fragment_content, f, "ABOUT"); + ft.commit(); } @Override @@ -403,44 +268,42 @@ public void showRatingPlayStore() { } @Override - 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); - + public void showAllBooksPage() { + FragmentManager fragmentManager = getSupportFragmentManager(); + FragmentTransaction ft = fragmentManager.beginTransaction(); + Fragment f = ListBooksFragment.newInstance(); + ft.replace(R.id.fragment_content, f, "ALLBOOKS"); + ft.commit(); } @Override - public void showLoading(boolean visible) { - circularProgressBar.setVisibility(visible ? View.VISIBLE : View.GONE); - mRecyclerView.setVisibility(visible ? View.GONE : View.VISIBLE); - + public void showDownloadedBooksPage() { + FragmentManager fragmentManager = getSupportFragmentManager(); + FragmentTransaction ft = fragmentManager.beginTransaction(); + Fragment f = DownloadsFragment.newInstance(); + ft.replace(R.id.fragment_content, f, "DOWNLOADED_BOOKS"); + ft.commit(); } - @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(); + @Override + public void openNavDrawer() { + drawerLayout.openDrawer(navigationView); } @Override - public void showSnackBarError(int message) { - Snackbar.make(mRecyclerView, message, Snackbar.LENGTH_LONG).show(); + public void closeNavDrawer() { + drawerLayout.closeDrawer(navigationView); } -} + @Override + public void setToolbar(Toolbar toolbar) { + setSupportActionBar(toolbar); + toolbar.setNavigationOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + drawerLayout.openDrawer(GravityCompat.START); + } + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/org/bookdash/android/presentation/main/MainContract.java b/app/src/main/java/org/bookdash/android/presentation/main/MainContract.java new file mode 100644 index 0000000..836fd40 --- /dev/null +++ b/app/src/main/java/org/bookdash/android/presentation/main/MainContract.java @@ -0,0 +1,25 @@ +package org.bookdash.android.presentation.main; + + +public interface MainContract { + interface MainView { + + void showThanksPopover(); + + void showAboutPage(); + + void showRatingPlayStore(); + + void showAllBooksPage(); + + void showDownloadedBooksPage(); + } + + interface MainUserActions { + + void clickViewDownloadBooks(); + + void clickViewAllBooks(); + } +} + diff --git a/app/src/main/java/org/bookdash/android/presentation/main/MainPresenter.java b/app/src/main/java/org/bookdash/android/presentation/main/MainPresenter.java new file mode 100644 index 0000000..fdac318 --- /dev/null +++ b/app/src/main/java/org/bookdash/android/presentation/main/MainPresenter.java @@ -0,0 +1,30 @@ +package org.bookdash.android.presentation.main; + +import org.bookdash.android.data.settings.SettingsApi; +import org.bookdash.android.domain.pojo.Language; + +import java.util.List; + +/** + * Created by rebeccafranks on 16/04/19. + */ +public class MainPresenter implements MainContract.MainUserActions { + + private MainContract.MainView mainView; + + public MainPresenter(MainContract.MainView mainView) { + this.mainView = mainView; + } + + @Override + public void clickViewDownloadBooks() { + mainView.showDownloadedBooksPage(); + } + + @Override + public void clickViewAllBooks() { + mainView.showAllBooksPage(); + } + + +} diff --git a/app/src/main/java/org/bookdash/android/presentation/main/NavDrawerInterface.java b/app/src/main/java/org/bookdash/android/presentation/main/NavDrawerInterface.java new file mode 100644 index 0000000..47bedb6 --- /dev/null +++ b/app/src/main/java/org/bookdash/android/presentation/main/NavDrawerInterface.java @@ -0,0 +1,12 @@ +package org.bookdash.android.presentation.main; + +import android.support.v7.widget.Toolbar; + + +public interface NavDrawerInterface { + void openNavDrawer(); + + void closeNavDrawer(); + + void setToolbar(Toolbar toolbar); +} 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 7b23df1..91472c0 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 @@ -7,7 +7,8 @@ import org.bookdash.android.Injection; import org.bookdash.android.R; -import org.bookdash.android.presentation.listbooks.ListBooksActivity; +import org.bookdash.android.presentation.listbooks.ListBooksFragment; +import org.bookdash.android.presentation.main.MainActivity; import za.co.riggaroo.materialhelptutorial.tutorial.MaterialTutorialActivity; @@ -47,7 +48,7 @@ public void loadTutorial() { @Override public void loadMainScreen() { - Intent mainAct = new Intent(SplashActivity.this, ListBooksActivity.class); + Intent mainAct = new Intent(SplashActivity.this, MainActivity.class); startActivity(mainAct); finish(); } diff --git a/app/src/main/java/org/bookdash/android/presentation/widget/SpacesItemDecoration.java b/app/src/main/java/org/bookdash/android/presentation/widget/SpacesItemDecoration.java new file mode 100644 index 0000000..a980444 --- /dev/null +++ b/app/src/main/java/org/bookdash/android/presentation/widget/SpacesItemDecoration.java @@ -0,0 +1,21 @@ +package org.bookdash.android.presentation.widget; + +import android.graphics.Rect; +import android.support.v7.widget.RecyclerView; +import android.view.View; + +public class SpacesItemDecoration extends RecyclerView.ItemDecoration { + private int space; + + public SpacesItemDecoration(int space) { + this.space = space; + } + + @Override + public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { + outRect.left = space; + outRect.right = space; + outRect.bottom = space; + outRect.top = space; + } +} diff --git a/app/src/main/kotlin/org.bookdash.android/data/BookDetailRepositoryExtenstion.kt b/app/src/main/kotlin/org.bookdash.android/data/BookDetailRepositoryExtenstion.kt deleted file mode 100644 index e69de29..0000000 diff --git a/app/src/main/res/drawable-hdpi/bookdash_placeholder.png b/app/src/main/res/drawable-hdpi/bookdash_placeholder.png new file mode 100644 index 0000000..e8f219e Binary files /dev/null and b/app/src/main/res/drawable-hdpi/bookdash_placeholder.png differ diff --git a/app/src/main/res/drawable-v21/download_selector.xml b/app/src/main/res/drawable-v21/download_selector.xml new file mode 100644 index 0000000..502f203 --- /dev/null +++ b/app/src/main/res/drawable-v21/download_selector.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/download_selector.xml b/app/src/main/res/drawable/download_selector.xml new file mode 100644 index 0000000..09eca7d --- /dev/null +++ b/app/src/main/res/drawable/download_selector.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_chrome_reader_mode_black_24dp.xml b/app/src/main/res/drawable/ic_chrome_reader_mode_black_24dp.xml new file mode 100644 index 0000000..99b5867 --- /dev/null +++ b/app/src/main/res/drawable/ic_chrome_reader_mode_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_delete.xml b/app/src/main/res/drawable/ic_delete.xml new file mode 100644 index 0000000..39e64d6 --- /dev/null +++ b/app/src/main/res/drawable/ic_delete.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_file_download_black.xml b/app/src/main/res/drawable/ic_file_download_black.xml new file mode 100644 index 0000000..492b41d --- /dev/null +++ b/app/src/main/res/drawable/ic_file_download_black.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 d4d5763..43c26bf 100644 --- a/app/src/main/res/layout-land/activity_book_information.xml +++ b/app/src/main/res/layout-land/activity_book_information.xml @@ -101,10 +101,11 @@ diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml index ee60dcc..a470906 100644 --- a/app/src/main/res/layout/activity_about.xml +++ b/app/src/main/res/layout/activity_about.xml @@ -2,22 +2,25 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" + android:fitsSystemWindows="true" android:layout_height="match_parent"> @@ -27,6 +30,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/nestedScrollView" + android:fitsSystemWindows="true" android:layout_marginTop="?attr/actionBarSize" android:background="@color/light_gray_background"> diff --git a/app/src/main/res/layout/activity_book_information.xml b/app/src/main/res/layout/activity_book_information.xml index 8e738a2..b05e5b6 100644 --- a/app/src/main/res/layout/activity_book_information.xml +++ b/app/src/main/res/layout/activity_book_information.xml @@ -1,22 +1,23 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + > - + - + + type="View.OnClickListener"/> + type="org.bookdash.android.domain.pojo.BookDetail"/> @@ -26,7 +27,6 @@ android:layout_height="match_parent"> + app:cpb_sweep_speed="1.0"/> + android:src="@drawable/lb_ic_sad_cloud"/> + android:text="@string/error_loading_books"/>