Add a language spinner

It doesn't do much yet...
This commit is contained in:
Bradlee Speice 2014-11-06 23:13:45 -05:00
parent 23bd5136b5
commit 88a40cbfbb
8 changed files with 135 additions and 65 deletions

View File

@ -40,9 +40,12 @@ import static org.mockito.Mockito.when;
public class BookDownloadManagerTest extends MBTestCase implements Injector { public class BookDownloadManagerTest extends MBTestCase implements Injector {
ObjectGraph mObjectGraph; ObjectGraph mObjectGraph;
@Inject BookDownloadManager bookDownloadManager; @Inject
@Inject RefreshManager refreshManager; BookDownloadManager bookDownloadManager;
@Inject Books installedBooks; @Inject
RefreshManager refreshManager;
@Inject
Books installedBooks;
@Override @Override
public void inject(Object o) { public void inject(Object o) {
@ -56,7 +59,7 @@ public class BookDownloadManagerTest extends MBTestCase implements Injector {
} }
Observable<Book> installableBooks() { Observable<Book> installableBooks() {
return refreshManager.getAvailableModulesFlat() return refreshManager.getFlatModules()
.filter(new Func1<Book, Boolean>() { .filter(new Func1<Book, Boolean>() {
@Override @Override
public Boolean call(Book book) { public Boolean call(Book book) {
@ -121,6 +124,7 @@ public class BookDownloadManagerTest extends MBTestCase implements Injector {
Injector i; Injector i;
ConnectivityManager manager; ConnectivityManager manager;
DownloadPrefs prefs; DownloadPrefs prefs;
BookDownloadManagerTestModules(Injector i) { BookDownloadManagerTestModules(Injector i) {
this.i = i; this.i = i;

View File

@ -72,7 +72,7 @@ public class RefreshManagerTest extends MBTestCase implements Injector {
} }
public void testGetAvailableModulesFlattened() throws Exception { public void testGetAvailableModulesFlattened() throws Exception {
rM.getAvailableModulesFlat() rM.getFlatModules()
.toBlocking() .toBlocking()
.forEach(new Action1<Book>() { .forEach(new Action1<Book>() {
@Override @Override
@ -134,15 +134,15 @@ public class RefreshManagerTest extends MBTestCase implements Injector {
long fourteenDaysAgo = Calendar.getInstance().getTime().getTime() - 1209600; long fourteenDaysAgo = Calendar.getInstance().getTime().getTime() - 1209600;
long sixteenDaysAgo = Calendar.getInstance().getTime().getTime() - 1382400; long sixteenDaysAgo = Calendar.getInstance().getTime().getTime() - 1382400;
assertFalse(rM.doReload(true, fourteenDaysAgo, false)); assertFalse(rM.doReload(true, fourteenDaysAgo, ConnectivityManager.TYPE_DUMMY));
assertFalse(rM.doReload(true, fourteenDaysAgo, true)); assertFalse(rM.doReload(true, fourteenDaysAgo, ConnectivityManager.TYPE_WIFI));
assertFalse(rM.doReload(true, sixteenDaysAgo, false)); assertFalse(rM.doReload(true, sixteenDaysAgo, ConnectivityManager.TYPE_DUMMY));
assertTrue(rM.doReload(true, sixteenDaysAgo, true)); assertTrue(rM.doReload(true, sixteenDaysAgo, ConnectivityManager.TYPE_WIFI));
assertFalse(rM.doReload(false, fourteenDaysAgo, true)); assertFalse(rM.doReload(false, fourteenDaysAgo, ConnectivityManager.TYPE_WIFI));
assertFalse(rM.doReload(false, fourteenDaysAgo, false)); assertFalse(rM.doReload(false, fourteenDaysAgo, ConnectivityManager.TYPE_DUMMY));
assertFalse(rM.doReload(false, sixteenDaysAgo, true)); assertFalse(rM.doReload(false, sixteenDaysAgo, ConnectivityManager.TYPE_WIFI));
assertFalse(rM.doReload(false, sixteenDaysAgo, false)); assertFalse(rM.doReload(false, sixteenDaysAgo, ConnectivityManager.TYPE_DUMMY));
} }
@Module(injects = {RefreshManagerTest.class, RefreshManager.class}) @Module(injects = {RefreshManagerTest.class, RefreshManager.class})
@ -151,6 +151,7 @@ public class RefreshManagerTest extends MBTestCase implements Injector {
Collection<Installer> installers; Collection<Installer> installers;
ConnectivityManager manager; ConnectivityManager manager;
DownloadPrefs prefs; DownloadPrefs prefs;
RMTModules(Collection<Installer> installers) { RMTModules(Collection<Installer> installers) {
this.installers = installers; this.installers = installers;

View File

@ -12,15 +12,29 @@ import com.readystatesoftware.systembartint.SystemBarTintManager;
*/ */
public class BaseFragment extends Fragment { public class BaseFragment extends Fragment {
protected static SystemBarTintManager.SystemBarConfig getConfig(Activity context) {
return new SystemBarTintManager(context).getConfig();
}
/** /**
* Calculate the offset we need to display properly if the System bar is translucent * Calculate the offset we need to display properly if the System bar is translucent
* @param context The {@link android.app.Activity} we are displaying in * @param context The {@link android.app.Activity} we are displaying in
* @param view The {@link android.view.View} we need to calculate the offset for. * @param view The {@link android.view.View} we need to calculate the offset for.
*/ */
@SuppressWarnings("unused")
protected static void setInsets(Activity context, View view) { protected static void setInsets(Activity context, View view) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return; if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;
SystemBarTintManager tintManager = new SystemBarTintManager(context); SystemBarTintManager.SystemBarConfig config = getConfig(context);
SystemBarTintManager.SystemBarConfig config = tintManager.getConfig();
view.setPadding(0, config.getPixelInsetTop(true), config.getPixelInsetRight(), config.getPixelInsetBottom()); view.setPadding(0, config.getPixelInsetTop(true), config.getPixelInsetRight(), config.getPixelInsetBottom());
} }
protected static void setInsetsSpinner(Activity context, View view) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;
SystemBarTintManager.SystemBarConfig config = getConfig(context);
int marginTopBottom = config.getPixelInsetBottom() / 3;
view.setPadding(0, config.getPixelInsetTop(true) + marginTopBottom,
config.getPixelInsetRight(),
marginTopBottom);
}
} }

View File

@ -8,12 +8,16 @@ import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView; import android.widget.ListView;
import android.widget.Spinner;
import android.widget.SpinnerAdapter;
import android.widget.Toast; import android.widget.Toast;
import org.bspeice.minimalbible.Injector; import org.bspeice.minimalbible.Injector;
import org.bspeice.minimalbible.R; import org.bspeice.minimalbible.R;
import org.bspeice.minimalbible.activity.BaseFragment; import org.bspeice.minimalbible.activity.BaseFragment;
import org.bspeice.minimalbible.activity.downloader.manager.LocaleManager;
import org.bspeice.minimalbible.activity.downloader.manager.RefreshManager; import org.bspeice.minimalbible.activity.downloader.manager.RefreshManager;
import org.crosswire.jsword.book.Book; import org.crosswire.jsword.book.Book;
import org.crosswire.jsword.book.BookCategory; import org.crosswire.jsword.book.BookCategory;
@ -36,18 +40,18 @@ import rx.functions.Func2;
*/ */
public class BookListFragment extends BaseFragment { public class BookListFragment extends BaseFragment {
/**
* The fragment argument representing the section number for this fragment.
* Not a candidate for Dart (yet) because I would have to write a Parcelable around it.
*/
protected static final String ARG_BOOK_CATEGORY = "book_category"; protected static final String ARG_BOOK_CATEGORY = "book_category";
@Inject @Inject
protected DownloadPrefs downloadPrefs; protected DownloadPrefs downloadPrefs;
protected ProgressDialog refreshDialog; protected ProgressDialog refreshDialog;
@Inject RefreshManager refreshManager;
@Inject
LocaleManager localeManager;
@InjectView(R.id.lst_download_available) @InjectView(R.id.lst_download_available)
ListView downloadsAvailable; ListView downloadsAvailable;
@Inject RefreshManager refreshManager; @InjectView(R.id.spn_available_languages)
Spinner availableLanguages;
private LayoutInflater inflater; private LayoutInflater inflater;
/** /**
@ -121,7 +125,7 @@ public class BookListFragment extends BaseFragment {
} }
// Listen for the books! // Listen for the books!
refreshManager.getAvailableModulesFlat() refreshManager.getFlatModules()
.filter(new Func1<Book, Boolean>() { .filter(new Func1<Book, Boolean>() {
@Override @Override
public Boolean call(Book book) { public Boolean call(Book book) {
@ -143,12 +147,13 @@ public class BookListFragment extends BaseFragment {
public void call(List<Book> books) { public void call(List<Book> books) {
downloadsAvailable.setAdapter( downloadsAvailable.setAdapter(
new BookListAdapter(inflater, books, (DownloadActivity)getActivity())); new BookListAdapter(inflater, books, (DownloadActivity)getActivity()));
availableLanguages.setAdapter(getLocaleSpinner());
if (BookListFragment.this.getActivity() != null) { if (BookListFragment.this.getActivity() != null) {
// On a screen rotate, getActivity() will be null. But, the activity // On a screen rotate, getActivity() will be null. But, the activity
// will already have been set up correctly, so we don't need to worry // will already have been set up correctly, so we don't need to worry
// about it. // about it.
// If not null, we need to set it up now. // If not null, we need to set it up now.
setInsets(BookListFragment.this.getActivity(), downloadsAvailable); setInsetsSpinner(BookListFragment.this.getActivity(), availableLanguages);
} }
if (refreshDialog != null) { if (refreshDialog != null) {
refreshDialog.cancel(); refreshDialog.cancel();
@ -157,6 +162,16 @@ public class BookListFragment extends BaseFragment {
}); });
} }
@SuppressWarnings("ConstantConditions")
// getAvailableLanguagesList() will not return null
SpinnerAdapter getLocaleSpinner() {
ArrayAdapter<Object> adapter = new ArrayAdapter<Object>(this.getActivity(),
android.R.layout.simple_spinner_item,
localeManager.getAvailableLanguagesList().toArray());
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
return adapter;
}
private class DownloadDialogListener implements private class DownloadDialogListener implements
DialogInterface.OnClickListener { DialogInterface.OnClickListener {
@Override @Override

View File

@ -7,6 +7,7 @@ import org.bspeice.minimalbible.Injector;
import org.bspeice.minimalbible.MinimalBibleModules; import org.bspeice.minimalbible.MinimalBibleModules;
import org.bspeice.minimalbible.activity.downloader.manager.BookDownloadManager; import org.bspeice.minimalbible.activity.downloader.manager.BookDownloadManager;
import org.bspeice.minimalbible.activity.downloader.manager.InstalledManager; import org.bspeice.minimalbible.activity.downloader.manager.InstalledManager;
import org.bspeice.minimalbible.activity.downloader.manager.LocaleManager;
import org.bspeice.minimalbible.activity.downloader.manager.RefreshManager; import org.bspeice.minimalbible.activity.downloader.manager.RefreshManager;
import org.crosswire.jsword.book.Book; import org.crosswire.jsword.book.Book;
import org.crosswire.jsword.book.BookCategory; import org.crosswire.jsword.book.BookCategory;
@ -112,4 +113,9 @@ public class DownloadActivityModules {
return new RefreshManager(installers, prefs, return new RefreshManager(installers, prefs,
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)); (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE));
} }
@Provides
LocaleManager provideLocaleManager(RefreshManager refreshManager) {
return new LocaleManager(refreshManager);
}
} }

View File

@ -0,0 +1,22 @@
package org.bspeice.minimalbible.activity.downloader.manager
import org.crosswire.common.util.Language
class LocaleManager(val rM: RefreshManager) {
val currentLanguage = Language.DEFAULT_LANG.getName()
val languageModuleMap = rM.flatModules
.map { it.getLanguage() ?: Language(Language.UNKNOWN_LANG_CODE) }
.groupBy { it.getName() }
val availableLanguages = languageModuleMap.map { it.getKey() }
val availableLanguagesList = availableLanguages.toSortedList {(left, right) ->
// Prioritize our current language first
if (left == currentLanguage)
-1
else if (right == currentLanguage)
1
else
left.compareTo(right)
}.toBlocking().first()
}

View File

@ -8,6 +8,7 @@ import rx.schedulers.Schedulers
import java.util.Calendar import java.util.Calendar
import org.bspeice.minimalbible.activity.downloader.DownloadPrefs import org.bspeice.minimalbible.activity.downloader.DownloadPrefs
import android.net.ConnectivityManager import android.net.ConnectivityManager
import org.crosswire.jsword.book.BookComparators
/** /**
* Created by bspeice on 10/22/14. * Created by bspeice on 10/22/14.
@ -21,19 +22,23 @@ class RefreshManager(val installers: Collection<Installer>,
Observable.from(installers) Observable.from(installers)
.map { .map {
if (doReload()) { if (doReload()) {
it.reloadBookList() it.reloadBookList() // TODO: Handle InstallException
} }
mapOf(Pair(it, it.getBooks())) mapOf(Pair(it, it.getBooks()))
} }
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.cache(); .cache();
val availableModulesFlat: Observable<Book> val flatModules: Observable<Book> =
get() = availableModules availableModules
// Map -> Lists // Map -> Lists
.flatMap { Observable.from(it.values()) } .flatMap { Observable.from(it.values()) }
// Lists -> Single list // Lists -> Single list
.flatMap { Observable.from(it) }; .flatMap { Observable.from(it) }
val flatModulesSorted = flatModules.toSortedList {(book1, book2) ->
BookComparators.getInitialComparator().compare(book1, book2)
};
// Constructor - Split from the value creation because `subscribe` returns // Constructor - Split from the value creation because `subscribe` returns
// the subscriber object, not the underlying value // the subscriber object, not the underlying value
@ -43,8 +48,9 @@ class RefreshManager(val installers: Collection<Installer>,
val fifteenDaysAgo = Calendar.getInstance().getTime().getTime() - 1296000 val fifteenDaysAgo = Calendar.getInstance().getTime().getTime() - 1296000
fun doReload(enabledDownload: Boolean, lastUpdated: Long, onWifi: Boolean): Boolean = fun doReload(enabledDownload: Boolean, lastUpdated: Long,
if (!enabledDownload || !onWifi) networkState: Int? = ConnectivityManager.TYPE_DUMMY): Boolean =
if (!enabledDownload || networkState == ConnectivityManager.TYPE_WIFI)
false false
else if (lastUpdated < fifteenDaysAgo) else if (lastUpdated < fifteenDaysAgo)
true true
@ -53,12 +59,7 @@ class RefreshManager(val installers: Collection<Installer>,
fun doReload(): Boolean = doReload(prefs.hasEnabledDownload(), fun doReload(): Boolean = doReload(prefs.hasEnabledDownload(),
prefs.downloadRefreshedOn(), prefs.downloadRefreshedOn(),
// TODO: Functional is awesome, but this might be a bit ridiculous connManager?.getActiveNetworkInfo()?.getType())
(if (connManager?.getActiveNetworkInfo() != null)
connManager!!.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI
else
false)
)
fun installerFromBook(b: Book): Observable<Installer> = Observable.just( fun installerFromBook(b: Book): Observable<Installer> = Observable.just(
availableModules.filter { availableModules.filter {

View File

@ -4,12 +4,19 @@
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context="org.bspeice.minimalbible.DownloadActivity$PlaceholderFragment" > tools:context="org.bspeice.minimalbible.DownloadActivity$PlaceholderFragment" >
<ListView <Spinner
android:id="@+id/spn_available_languages"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:id="@+id/lst_download_available"
android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
android:clipToPadding="false" /> android:layout_alignParentTop="true" />
<ListView
android:id="@+id/lst_download_available"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentLeft="true"
android:layout_below="@+id/spn_available_languages" />
</RelativeLayout> </RelativeLayout>