diff --git a/app/src/main/java/com/studio4plus/homerplayer/ApplicationModule.java b/app/src/main/java/com/studio4plus/homerplayer/ApplicationModule.java index 0f74378c..f74425d8 100644 --- a/app/src/main/java/com/studio4plus/homerplayer/ApplicationModule.java +++ b/app/src/main/java/com/studio4plus/homerplayer/ApplicationModule.java @@ -5,9 +5,12 @@ import android.content.SharedPreferences; import android.content.res.Resources; import android.net.Uri; +import android.os.Handler; +import android.os.HandlerThread; import android.preference.PreferenceManager; import com.studio4plus.homerplayer.analytics.AnalyticsTracker; +import com.studio4plus.homerplayer.concurrency.BackgroundExecutor; import java.util.Locale; @@ -64,4 +67,12 @@ EventBus provideEventBus() { AnalyticsTracker provideAnalyticsTracker(GlobalSettings globalSettings, EventBus eventBus) { return new AnalyticsTracker(globalSettings, eventBus); } + + @Provides @Singleton @Named("IO_EXECUTOR") + BackgroundExecutor provideIoExecutor(Context applicationContext) { + HandlerThread ioThread = new HandlerThread("IO"); + ioThread.start(); + Handler ioHandler = new Handler(ioThread.getLooper()); + return new BackgroundExecutor(new Handler(applicationContext.getMainLooper()), ioHandler); + } } diff --git a/app/src/main/java/com/studio4plus/homerplayer/concurrency/BackgroundDeferred.java b/app/src/main/java/com/studio4plus/homerplayer/concurrency/BackgroundDeferred.java new file mode 100644 index 00000000..7d1c9316 --- /dev/null +++ b/app/src/main/java/com/studio4plus/homerplayer/concurrency/BackgroundDeferred.java @@ -0,0 +1,42 @@ +package com.studio4plus.homerplayer.concurrency; + +import android.os.Handler; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; + +public class BackgroundDeferred extends BaseDeferred implements Runnable { + + private final @NonNull List> listeners = new ArrayList<>(); + + private final @NonNull Callable task; + private final @NonNull Handler mainThreadHandler; + + BackgroundDeferred(@NonNull Callable task, @NonNull Handler mainThreadHandler) { + this.task = task; + this.mainThreadHandler = mainThreadHandler; + } + + @Override + public void run() { + try { + final @NonNull V newResult = task.call(); + mainThreadHandler.post(new Runnable() { + @Override + public void run() { + setResult(newResult); + } + }); + } catch (final Exception e) { + mainThreadHandler.post(new Runnable() { + @Override + public void run() { + setException(e); + } + }); + } + } +} diff --git a/app/src/main/java/com/studio4plus/homerplayer/concurrency/BackgroundExecutor.java b/app/src/main/java/com/studio4plus/homerplayer/concurrency/BackgroundExecutor.java new file mode 100644 index 00000000..9b0773b7 --- /dev/null +++ b/app/src/main/java/com/studio4plus/homerplayer/concurrency/BackgroundExecutor.java @@ -0,0 +1,23 @@ +package com.studio4plus.homerplayer.concurrency; + +import android.os.Handler; +import android.support.annotation.NonNull; + +import java.util.concurrent.Callable; + +public class BackgroundExecutor { + + private final @NonNull Handler mainThreadHandler; + private final @NonNull Handler taskHandler; + + public BackgroundExecutor(@NonNull Handler mainThreadHandler, @NonNull Handler taskHandler) { + this.mainThreadHandler = mainThreadHandler; + this.taskHandler = taskHandler; + } + + public SimpleFuture postTask(@NonNull Callable task) { + BackgroundDeferred deferred = new BackgroundDeferred<>(task, mainThreadHandler); + taskHandler.post(deferred); + return deferred; + } +} diff --git a/app/src/main/java/com/studio4plus/homerplayer/util/SimpleDeferred.java b/app/src/main/java/com/studio4plus/homerplayer/concurrency/BaseDeferred.java similarity index 77% rename from app/src/main/java/com/studio4plus/homerplayer/util/SimpleDeferred.java rename to app/src/main/java/com/studio4plus/homerplayer/concurrency/BaseDeferred.java index 23b9dc61..1cc86183 100644 --- a/app/src/main/java/com/studio4plus/homerplayer/util/SimpleDeferred.java +++ b/app/src/main/java/com/studio4plus/homerplayer/concurrency/BaseDeferred.java @@ -1,4 +1,4 @@ -package com.studio4plus.homerplayer.util; +package com.studio4plus.homerplayer.concurrency; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -8,11 +8,11 @@ /** * A straightforward implementation of SimpleFuture. - * It's intended of use only on a single thread. + * It's intended for use only on a single thread. * * Note: I don't need the full power of ListenableFutures nor Rx yet. */ -public class SimpleDeferred implements SimpleFuture { +public class BaseDeferred implements SimpleFuture { private final @NonNull List> listeners = new ArrayList<>(); private @Nullable V result; @@ -32,15 +32,15 @@ public void removeListener(@NonNull Listener listener) { listeners.remove(listener); } - public void setResult(@NonNull V result) { + protected void setResult(@NonNull V result) { this.result = result; for (Listener listener : listeners) listener.onResult(result); } - public void setException(@NonNull Throwable exception) { + protected void setException(@NonNull Throwable exception) { this.exception = exception; for (Listener listener : listeners) listener.onException(exception); } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/studio4plus/homerplayer/concurrency/SimpleDeferred.java b/app/src/main/java/com/studio4plus/homerplayer/concurrency/SimpleDeferred.java new file mode 100644 index 00000000..a35f1666 --- /dev/null +++ b/app/src/main/java/com/studio4plus/homerplayer/concurrency/SimpleDeferred.java @@ -0,0 +1,14 @@ +package com.studio4plus.homerplayer.concurrency; + +import android.support.annotation.NonNull; + +public class SimpleDeferred extends BaseDeferred { + + public void setResult(@NonNull V result) { + super.setResult(result); + } + + public void setException(@NonNull Throwable exception) { + super.setException(exception); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/studio4plus/homerplayer/util/SimpleFuture.java b/app/src/main/java/com/studio4plus/homerplayer/concurrency/SimpleFuture.java similarity index 86% rename from app/src/main/java/com/studio4plus/homerplayer/util/SimpleFuture.java rename to app/src/main/java/com/studio4plus/homerplayer/concurrency/SimpleFuture.java index 61bf0a3f..0ed4923d 100644 --- a/app/src/main/java/com/studio4plus/homerplayer/util/SimpleFuture.java +++ b/app/src/main/java/com/studio4plus/homerplayer/concurrency/SimpleFuture.java @@ -1,4 +1,4 @@ -package com.studio4plus.homerplayer.util; +package com.studio4plus.homerplayer.concurrency; import android.support.annotation.NonNull; diff --git a/app/src/main/java/com/studio4plus/homerplayer/filescanner/FileScanner.java b/app/src/main/java/com/studio4plus/homerplayer/filescanner/FileScanner.java new file mode 100644 index 00000000..25af7979 --- /dev/null +++ b/app/src/main/java/com/studio4plus/homerplayer/filescanner/FileScanner.java @@ -0,0 +1,49 @@ +package com.studio4plus.homerplayer.filescanner; + +import android.content.Context; +import android.os.Environment; + +import com.studio4plus.homerplayer.ApplicationScope; +import com.studio4plus.homerplayer.concurrency.BackgroundExecutor; +import com.studio4plus.homerplayer.concurrency.SimpleFuture; + +import java.io.File; +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Named; + +@ApplicationScope +public class FileScanner { + public static final String SAMPLE_BOOK_FILE_NAME = ".sample"; + + private final String audioBooksDirectoryPath; + private final BackgroundExecutor ioExecutor; + private final Context applicationContext; + + @Inject + public FileScanner( + @Named("AUDIOBOOKS_DIRECTORY") String audioBooksDirectoryPath, + @Named("IO_EXECUTOR") BackgroundExecutor ioExecutor, + Context applicationContext) { + this.audioBooksDirectoryPath = audioBooksDirectoryPath; + this.ioExecutor = ioExecutor; + this.applicationContext = applicationContext; + } + + public SimpleFuture> scanAudioBooksDirectories() { + ScanFilesTask task = new ScanFilesTask(applicationContext, audioBooksDirectoryPath); + return ioExecutor.postTask(task); + } + + /** + * Provide the default directory for audio books. + * + * The directory is in the devices external storage. Other than that there is nothing + * special about it (e.g. it may be on an removable storage). + */ + public File getDefaultAudioBooksDirectory() { + File externalStorage = Environment.getExternalStorageDirectory(); + return new File(externalStorage, audioBooksDirectoryPath); + } +} diff --git a/app/src/main/java/com/studio4plus/homerplayer/model/FileSet.java b/app/src/main/java/com/studio4plus/homerplayer/filescanner/FileSet.java similarity index 94% rename from app/src/main/java/com/studio4plus/homerplayer/model/FileSet.java rename to app/src/main/java/com/studio4plus/homerplayer/filescanner/FileSet.java index b2662c2b..d5c43446 100644 --- a/app/src/main/java/com/studio4plus/homerplayer/model/FileSet.java +++ b/app/src/main/java/com/studio4plus/homerplayer/filescanner/FileSet.java @@ -1,4 +1,4 @@ -package com.studio4plus.homerplayer.model; +package com.studio4plus.homerplayer.filescanner; import com.google.common.base.Preconditions; diff --git a/app/src/main/java/com/studio4plus/homerplayer/model/FileScanner.java b/app/src/main/java/com/studio4plus/homerplayer/filescanner/ScanFilesTask.java similarity index 81% rename from app/src/main/java/com/studio4plus/homerplayer/model/FileScanner.java rename to app/src/main/java/com/studio4plus/homerplayer/filescanner/ScanFilesTask.java index 81ad8e44..939d99a6 100644 --- a/app/src/main/java/com/studio4plus/homerplayer/model/FileScanner.java +++ b/app/src/main/java/com/studio4plus/homerplayer/filescanner/ScanFilesTask.java @@ -1,160 +1,156 @@ -package com.studio4plus.homerplayer.model; - -import android.content.Context; -import android.os.Environment; -import android.util.Base64; - -import com.studio4plus.homerplayer.util.DirectoryFilter; -import com.studio4plus.homerplayer.util.FilesystemUtil; -import com.studio4plus.homerplayer.util.OrFilter; - -import java.io.File; -import java.io.FileFilter; -import java.nio.ByteBuffer; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; - -import javax.inject.Inject; -import javax.inject.Named; - -public class FileScanner { - - static final String SAMPLE_BOOK_FILE_NAME = ".sample"; - private static final String[] SUPPORTED_SUFFIXES = {".mp3", ".m4a", ".ogg"}; - - private final String audioBooksDirectoryPath; - private final Context context; - - @Inject - public FileScanner( - @Named("AUDIOBOOKS_DIRECTORY") String audioBooksDirectoryPath, - Context context) { - this.audioBooksDirectoryPath = audioBooksDirectoryPath; - this.context = context; - } - - public List scanAudioBooksDirectories() { - List fileSets = new ArrayList<>(); - List dirsToScan = FilesystemUtil.listRootDirs(context); - File defaultStorage = Environment.getExternalStorageDirectory(); - if (!containsByValue(dirsToScan, defaultStorage)) - dirsToScan.add(defaultStorage); - - for (File rootDir : dirsToScan) { - File audioBooksDir = new File(rootDir, audioBooksDirectoryPath); - scanAndAppendBooks(audioBooksDir, fileSets); - } - return fileSets; - } - - /** - * Provide the default directory for audio books. - * - * The directory is in the devices external storage. Other than that there is nothing - * special about it (e.g. it may be on an removable storage). - */ - public File getDefaultAudioBooksDirectory() { - File externalStorage = Environment.getExternalStorageDirectory(); - return new File(externalStorage, audioBooksDirectoryPath); - } - - private void scanAndAppendBooks(File audioBooksDir, List fileSets) { - if (audioBooksDir.exists() && audioBooksDir.isDirectory() && audioBooksDir.canRead()) { - File[] audioBookDirs = audioBooksDir.listFiles(new DirectoryFilter()); - if (audioBookDirs != null) { - for (File directory : audioBookDirs) { - FileSet fileSet = createFileSet(directory); - if (fileSet != null && !fileSets.contains(fileSet)) - fileSets.add(fileSet); - } - } - } - } - - private FileSet createFileSet(File bookDirectory) { - File[] allFiles = getAllAudioFiles(bookDirectory); - int bookDirectoryPathLength = bookDirectory.getAbsolutePath().length(); - - ByteBuffer bufferLong = ByteBuffer.allocate(Long.SIZE); - try { - MessageDigest digest = MessageDigest.getInstance("MD5"); - for (File file : allFiles) { - String path = file.getAbsolutePath(); - String relativePath = path.substring(bookDirectoryPathLength); - - // TODO: what if the same book is in two directories? - bufferLong.putLong(0, file.length()); - digest.update(relativePath.getBytes()); - digest.update(bufferLong); - } - String id = Base64.encodeToString(digest.digest(), Base64.NO_PADDING | Base64.NO_WRAP); - if (allFiles.length > 0) { - File sampleIndicator = new File(bookDirectory, SAMPLE_BOOK_FILE_NAME); - boolean isDemoSample = sampleIndicator.exists(); - return new FileSet(id, bookDirectory, allFiles, isDemoSample); - } else { - return null; - } - } catch (NoSuchAlgorithmException e) { - // Never happens. - e.printStackTrace(); - throw new RuntimeException("MD5 not available"); - } - } - - private File[] getAllAudioFiles(File directory) { - List files = new ArrayList<>(); - FileFilter audioFiles = new FileFilter() { - @Override - public boolean accept(File pathname) { - return isAudioFile(pathname); - } - }; - - FileFilter filesAndDirectoriesFilter = new OrFilter(audioFiles, new DirectoryFilter()); - addFilesRecursive(directory, filesAndDirectoriesFilter, files); - return files.toArray(new File[files.size()]); - } - - private void addFilesRecursive(File directory, FileFilter filter, List allFiles) { - File[] files = directory.listFiles(filter); - // listFiles may return null. Skip such directories. - if (files == null) - return; - - Arrays.sort(files, new Comparator() { - @Override - public int compare(File lhs, File rhs) { - return lhs.getName().compareToIgnoreCase(rhs.getName()); - } - }); - - for (File file : files) { - if (file.isDirectory()) { - addFilesRecursive(file, filter, allFiles); - } else { - allFiles.add(file); - } - } - } - - private boolean containsByValue(List items, Type needle) { - for (Type item : items) - if (item.equals(needle)) - return true; - return false; - } - - private static boolean isAudioFile(File file) { - String lowerCaseFileName = file.getName().toLowerCase(); - for (String suffix : SUPPORTED_SUFFIXES) - if (lowerCaseFileName.endsWith(suffix)) - return true; - - return false; - } -} +package com.studio4plus.homerplayer.filescanner; + +import android.content.Context; +import android.os.Environment; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Base64; + +import com.studio4plus.homerplayer.util.DirectoryFilter; +import com.studio4plus.homerplayer.util.FilesystemUtil; +import com.studio4plus.homerplayer.util.OrFilter; + +import java.io.File; +import java.io.FileFilter; +import java.nio.ByteBuffer; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.Callable; + +public class ScanFilesTask implements Callable> { + + private static final String[] SUPPORTED_SUFFIXES = {".mp3", ".m4a", ".ogg"}; + + private final @NonNull Context applicationContext; + private final @NonNull String audioBooksDirectoryPath; + + ScanFilesTask(@NonNull Context applicationContext, @NonNull String audioBooksDirectoryPath) { + this.applicationContext = applicationContext; + this.audioBooksDirectoryPath = audioBooksDirectoryPath; + } + + + @Override + public List call() throws Exception { + return scanAudioBooksDirectories(); + } + + private List scanAudioBooksDirectories() { + List fileSets = new ArrayList<>(); + List dirsToScan = FilesystemUtil.listRootDirs(applicationContext); + File defaultStorage = Environment.getExternalStorageDirectory(); + if (!containsByValue(dirsToScan, defaultStorage)) + dirsToScan.add(defaultStorage); + + for (File rootDir : dirsToScan) { + File audioBooksDir = new File(rootDir, audioBooksDirectoryPath); + scanAndAppendBooks(audioBooksDir, fileSets); + } + return fileSets; + } + + + private void scanAndAppendBooks(File audioBooksDir, List fileSets) { + if (audioBooksDir.exists() && audioBooksDir.isDirectory() && audioBooksDir.canRead()) { + File[] audioBookDirs = audioBooksDir.listFiles(new DirectoryFilter()); + if (audioBookDirs != null) { + for (File directory : audioBookDirs) { + FileSet fileSet = createFileSet(directory); + if (fileSet != null && !fileSets.contains(fileSet)) + fileSets.add(fileSet); + } + } + } + } + + @Nullable + private FileSet createFileSet(File bookDirectory) { + File[] allFiles = getAllAudioFiles(bookDirectory); + int bookDirectoryPathLength = bookDirectory.getAbsolutePath().length(); + + ByteBuffer bufferLong = ByteBuffer.allocate(Long.SIZE); + try { + MessageDigest digest = MessageDigest.getInstance("MD5"); + for (File file : allFiles) { + String path = file.getAbsolutePath(); + String relativePath = path.substring(bookDirectoryPathLength); + + // TODO: what if the same book is in two directories? + bufferLong.putLong(0, file.length()); + digest.update(relativePath.getBytes()); + digest.update(bufferLong); + } + String id = Base64.encodeToString(digest.digest(), Base64.NO_PADDING | Base64.NO_WRAP); + if (allFiles.length > 0) { + File sampleIndicator = new File(bookDirectory, FileScanner.SAMPLE_BOOK_FILE_NAME); + boolean isDemoSample = sampleIndicator.exists(); + return new FileSet(id, bookDirectory, allFiles, isDemoSample); + } else { + return null; + } + } catch (NoSuchAlgorithmException e) { + // Never happens. + e.printStackTrace(); + throw new RuntimeException("MD5 not available"); + } + } + + + @NonNull + private File[] getAllAudioFiles(File directory) { + List files = new ArrayList<>(); + FileFilter audioFiles = new FileFilter() { + @Override + public boolean accept(File pathname) { + return isAudioFile(pathname); + } + }; + + FileFilter filesAndDirectoriesFilter = new OrFilter(audioFiles, new DirectoryFilter()); + addFilesRecursive(directory, filesAndDirectoriesFilter, files); + return files.toArray(new File[files.size()]); + } + + + private void addFilesRecursive(File directory, FileFilter filter, List allFiles) { + File[] files = directory.listFiles(filter); + // listFiles may return null. Skip such directories. + if (files == null) + return; + + Arrays.sort(files, new Comparator() { + @Override + public int compare(File lhs, File rhs) { + return lhs.getName().compareToIgnoreCase(rhs.getName()); + } + }); + + for (File file : files) { + if (file.isDirectory()) { + addFilesRecursive(file, filter, allFiles); + } else { + allFiles.add(file); + } + } + } + + private boolean containsByValue(List items, Type needle) { + for (Type item : items) + if (item.equals(needle)) + return true; + return false; + } + + private static boolean isAudioFile(File file) { + String lowerCaseFileName = file.getName().toLowerCase(); + for (String suffix : SUPPORTED_SUFFIXES) + if (lowerCaseFileName.endsWith(suffix)) + return true; + + return false; + } +} diff --git a/app/src/main/java/com/studio4plus/homerplayer/model/AudioBook.java b/app/src/main/java/com/studio4plus/homerplayer/model/AudioBook.java index 73552571..9097b19c 100644 --- a/app/src/main/java/com/studio4plus/homerplayer/model/AudioBook.java +++ b/app/src/main/java/com/studio4plus/homerplayer/model/AudioBook.java @@ -1,6 +1,7 @@ package com.studio4plus.homerplayer.model; import com.google.common.base.Preconditions; +import com.studio4plus.homerplayer.filescanner.FileSet; import com.studio4plus.homerplayer.util.DebugUtil; import java.io.File; diff --git a/app/src/main/java/com/studio4plus/homerplayer/model/AudioBookManager.java b/app/src/main/java/com/studio4plus/homerplayer/model/AudioBookManager.java index 91733947..c78a0c65 100644 --- a/app/src/main/java/com/studio4plus/homerplayer/model/AudioBookManager.java +++ b/app/src/main/java/com/studio4plus/homerplayer/model/AudioBookManager.java @@ -1,10 +1,16 @@ package com.studio4plus.homerplayer.model; import android.support.annotation.MainThread; +import android.support.annotation.NonNull; +import com.crashlytics.android.Crashlytics; +import com.studio4plus.homerplayer.ApplicationScope; +import com.studio4plus.homerplayer.concurrency.SimpleFuture; import com.studio4plus.homerplayer.events.AudioBooksChangedEvent; import com.studio4plus.homerplayer.events.CurrentBookChangedEvent; import com.studio4plus.homerplayer.events.MediaStoreUpdateEvent; +import com.studio4plus.homerplayer.filescanner.FileScanner; +import com.studio4plus.homerplayer.filescanner.FileSet; import java.io.File; import java.util.ArrayList; @@ -13,17 +19,17 @@ import java.util.List; import javax.inject.Inject; -import javax.inject.Singleton; import de.greenrobot.event.EventBus; -@Singleton +@ApplicationScope public class AudioBookManager { private final List audioBooks = new ArrayList<>(); private final FileScanner fileScanner; private final Storage storage; private AudioBook currentBook; + private boolean isInitialized = false; @Inject @MainThread @@ -77,11 +83,32 @@ public File getDefaultAudioBooksDirectory() { return fileScanner.getDefaultAudioBooksDirectory(); } + @MainThread + public boolean isInitialized() { + return isInitialized; + } + @MainThread public void scanFiles() { - boolean audioBooksChanged; - List fileSets = fileScanner.scanAudioBooksDirectories(); + SimpleFuture> future = fileScanner.scanAudioBooksDirectories(); + future.addListener(new SimpleFuture.Listener>() { + @Override + public void onResult(@NonNull List result) { + isInitialized = true; + processScanResult(result); + } + + @Override + public void onException(@NonNull Throwable t) { + isInitialized = true; + // TODO: clear the list of books? + Crashlytics.logException(t); + } + }); + } + @MainThread + private void processScanResult(@NonNull List fileSets) { // This isn't very efficient but there shouldn't be more than a dozen audio books on the // device. List booksToRemove = new ArrayList<>(); @@ -99,23 +126,21 @@ public void scanFiles() { } if (booksToRemove.contains(currentBook)) currentBook = null; - audioBooksChanged = audioBooks.removeAll(booksToRemove); + boolean audioBooksChanged = audioBooks.removeAll(booksToRemove); LibraryContentType contentType = LibraryContentType.EMPTY; - if (fileSets != null) { - for (FileSet fileSet : fileSets) { - if (getById(fileSet.id) == null) { - AudioBook audioBook = new AudioBook(fileSet); - storage.readAudioBookState(audioBook); - audioBook.setUpdateObserver(storage); - audioBooks.add(audioBook); - audioBooksChanged = true; - } - LibraryContentType newContentType = fileSet.isDemoSample - ? LibraryContentType.SAMPLES_ONLY : LibraryContentType.USER_CONTENT; - if (newContentType.supersedes(contentType)) - contentType = newContentType; + for (FileSet fileSet : fileSets) { + if (getById(fileSet.id) == null) { + AudioBook audioBook = new AudioBook(fileSet); + storage.readAudioBookState(audioBook); + audioBook.setUpdateObserver(storage); + audioBooks.add(audioBook); + audioBooksChanged = true; } + LibraryContentType newContentType = fileSet.isDemoSample + ? LibraryContentType.SAMPLES_ONLY : LibraryContentType.USER_CONTENT; + if (newContentType.supersedes(contentType)) + contentType = newContentType; } if (audioBooks.size() > 0) { diff --git a/app/src/main/java/com/studio4plus/homerplayer/model/DemoSamplesInstaller.java b/app/src/main/java/com/studio4plus/homerplayer/model/DemoSamplesInstaller.java index 925dcc65..37419e57 100644 --- a/app/src/main/java/com/studio4plus/homerplayer/model/DemoSamplesInstaller.java +++ b/app/src/main/java/com/studio4plus/homerplayer/model/DemoSamplesInstaller.java @@ -8,6 +8,7 @@ import com.crashlytics.android.Crashlytics; import com.github.saturngod.Decompress; import com.google.common.io.Files; +import com.studio4plus.homerplayer.filescanner.FileScanner; import org.json.JSONException; import org.json.JSONObject; diff --git a/app/src/main/java/com/studio4plus/homerplayer/ui/MainActivity.java b/app/src/main/java/com/studio4plus/homerplayer/ui/MainActivity.java index 94c9027a..c93eaf8e 100644 --- a/app/src/main/java/com/studio4plus/homerplayer/ui/MainActivity.java +++ b/app/src/main/java/com/studio4plus/homerplayer/ui/MainActivity.java @@ -19,10 +19,10 @@ import com.studio4plus.homerplayer.HomerPlayerApplication; import com.studio4plus.homerplayer.R; import com.studio4plus.homerplayer.battery.BatteryStatusProvider; +import com.studio4plus.homerplayer.concurrency.SimpleDeferred; import com.studio4plus.homerplayer.ui.classic.ClassicMainUiModule; import com.studio4plus.homerplayer.ui.classic.DaggerClassicMainUiComponent; -import com.studio4plus.homerplayer.util.SimpleDeferred; -import com.studio4plus.homerplayer.util.SimpleFuture; +import com.studio4plus.homerplayer.concurrency.SimpleFuture; import java.util.concurrent.TimeUnit; @@ -38,7 +38,8 @@ public class MainActivity extends AppCompatActivity implements SpeakerProvider { private MainUiComponent mainUiComponent; private BatteryStatusIndicator batteryStatusIndicator; - private @Nullable SimpleDeferred ttsDeferred; + private @Nullable + SimpleDeferred ttsDeferred; private OrientationActivityDelegate orientationDelegate; @Inject public UiControllerMain controller; diff --git a/app/src/main/java/com/studio4plus/homerplayer/ui/SpeakerProvider.java b/app/src/main/java/com/studio4plus/homerplayer/ui/SpeakerProvider.java index 21e421cb..c9ad33cc 100644 --- a/app/src/main/java/com/studio4plus/homerplayer/ui/SpeakerProvider.java +++ b/app/src/main/java/com/studio4plus/homerplayer/ui/SpeakerProvider.java @@ -2,7 +2,7 @@ import android.support.annotation.NonNull; -import com.studio4plus.homerplayer.util.SimpleFuture; +import com.studio4plus.homerplayer.concurrency.SimpleFuture; public interface SpeakerProvider { @NonNull SimpleFuture obtainTts(); diff --git a/app/src/main/java/com/studio4plus/homerplayer/ui/UiControllerBookList.java b/app/src/main/java/com/studio4plus/homerplayer/ui/UiControllerBookList.java index 1ce642a0..538e1f99 100644 --- a/app/src/main/java/com/studio4plus/homerplayer/ui/UiControllerBookList.java +++ b/app/src/main/java/com/studio4plus/homerplayer/ui/UiControllerBookList.java @@ -11,7 +11,7 @@ import com.studio4plus.homerplayer.events.AudioBooksChangedEvent; import com.studio4plus.homerplayer.events.CurrentBookChangedEvent; import com.studio4plus.homerplayer.model.AudioBookManager; -import com.studio4plus.homerplayer.util.SimpleFuture; +import com.studio4plus.homerplayer.concurrency.SimpleFuture; import javax.inject.Inject; diff --git a/app/src/main/java/com/studio4plus/homerplayer/ui/UiControllerMain.java b/app/src/main/java/com/studio4plus/homerplayer/ui/UiControllerMain.java index 113735d6..a764c51e 100644 --- a/app/src/main/java/com/studio4plus/homerplayer/ui/UiControllerMain.java +++ b/app/src/main/java/com/studio4plus/homerplayer/ui/UiControllerMain.java @@ -65,8 +65,7 @@ void onActivityCreated() { void onActivityStart() { Crashlytics.log("activity start"); - if (playbackService != null) - setInitialState(); + maybeSetInitialState(); } void onActivityPause() { @@ -87,6 +86,7 @@ void onActivityDestroy() { @SuppressWarnings({"UnusedParameters", "UnusedDeclaration"}) public void onEvent(AudioBooksChangedEvent event) { currentState.onBooksChanged(this); + maybeSetInitialState(); } @SuppressWarnings({"UnusedParameters", "UnusedDeclaration"}) @@ -104,8 +104,7 @@ public void onServiceConnected(ComponentName componentName, IBinder service) { Crashlytics.log("onServiceConnected"); Preconditions.checkState(playbackService == null); playbackService = ((PlaybackService.ServiceBinder) service).getService(); - if (currentState instanceof InitState) - setInitialState(); + maybeSetInitialState(); } @Override @@ -113,15 +112,18 @@ public void onServiceDisconnected(ComponentName componentName) { playbackService = null; } - private void setInitialState() { - Preconditions.checkNotNull(playbackService); - if (playbackService.getState() != PlaybackService.State.IDLE) { - Preconditions.checkState(hasAnyBooks()); - changeState(StateFactory.PLAYBACK); - } else if (hasAnyBooks()) { - changeState(StateFactory.BOOK_LIST); - } else { - changeState(StateFactory.NO_BOOKS); + private void maybeSetInitialState() { + if (currentState instanceof InitState && playbackService != null && + audioBookManager.isInitialized()) { + + if (playbackService.getState() != PlaybackService.State.IDLE) { + Preconditions.checkState(hasAnyBooks()); + changeState(StateFactory.PLAYBACK); + } else if (hasAnyBooks()) { + changeState(StateFactory.BOOK_LIST); + } else { + changeState(StateFactory.NO_BOOKS); + } } }