The cache stores its data in a directory on the filesystem. This
+ * directory must be exclusive to the cache; the cache may delete or overwrite
+ * files from its directory. It is an error for multiple processes to use the
+ * same cache directory at the same time.
+ *
+ *
This cache limits the number of bytes that it will store on the
+ * filesystem. When the number of stored bytes exceeds the limit, the cache will
+ * remove entries in the background until the limit is satisfied. The limit is
+ * not strict: the cache may temporarily exceed it while waiting for files to be
+ * deleted. The limit does not include filesystem overhead or the cache
+ * journal so space-sensitive applications should set a conservative limit.
+ *
+ *
Clients call {@link #edit} to create or update the values of an entry. An
+ * entry may have only one editor at one time; if a value is not available to be
+ * edited then {@link #edit} will return null.
+ *
+ *
When an entry is being created it is necessary to
+ * supply a full set of values; the empty value should be used as a
+ * placeholder if necessary.
+ *
When an entry is being edited, it is not necessary
+ * to supply data for every value; values default to their previous
+ * value.
+ *
+ * Every {@link #edit} call must be matched by a call to {@link Editor#commit}
+ * or {@link Editor#abort}. Committing is atomic: a read observes the full set
+ * of values as they were before or after the commit, but never a mix of values.
+ *
+ *
Clients call {@link #get} to read a snapshot of an entry. The read will
+ * observe the value at the time that {@link #get} was called. Updates and
+ * removals after the call do not impact ongoing reads.
+ *
+ *
This class is tolerant of some I/O errors. If files are missing from the
+ * filesystem, the corresponding entries will be dropped from the cache. If
+ * an error occurs while writing a cache value, the edit will fail silently.
+ * Callers should handle other problems by catching {@code IOException} and
+ * responding appropriately.
+ */
+public final class DiskLruCache implements Closeable {
+ static final String JOURNAL_FILE = "journal";
+ static final String JOURNAL_FILE_TEMP = "journal.tmp";
+ static final String JOURNAL_FILE_BACKUP = "journal.bkp";
+ static final String MAGIC = "libcore.io.DiskLruCache";
+ static final String VERSION_1 = "1";
+ static final long ANY_SEQUENCE_NUMBER = -1;
+ static final String STRING_KEY_PATTERN = "[a-z0-9_-]{1,120}";
+ static final Pattern LEGAL_KEY_PATTERN = Pattern.compile(STRING_KEY_PATTERN);
+ private static final String CLEAN = "CLEAN";
+ private static final String DIRTY = "DIRTY";
+ private static final String REMOVE = "REMOVE";
+ private static final String READ = "READ";
+
+ /*
+ * This cache uses a journal file named "journal". A typical journal file
+ * looks like this:
+ * libcore.io.DiskLruCache
+ * 1
+ * 100
+ * 2
+ *
+ * CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054
+ * DIRTY 335c4c6028171cfddfbaae1a9c313c52
+ * CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342
+ * REMOVE 335c4c6028171cfddfbaae1a9c313c52
+ * DIRTY 1ab96a171faeeee38496d8b330771a7a
+ * CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234
+ * READ 335c4c6028171cfddfbaae1a9c313c52
+ * READ 3400330d1dfc7f3f7f4b8d4d803dfcf6
+ *
+ * The first five lines of the journal form its header. They are the
+ * constant string "libcore.io.DiskLruCache", the disk cache's version,
+ * the application's version, the value count, and a blank line.
+ *
+ * Each of the subsequent lines in the file is a record of the state of a
+ * cache entry. Each line contains space-separated values: a state, a key,
+ * and optional state-specific values.
+ * o DIRTY lines track that an entry is actively being created or updated.
+ * Every successful DIRTY action should be followed by a CLEAN or REMOVE
+ * action. DIRTY lines without a matching CLEAN or REMOVE indicate that
+ * temporary files may need to be deleted.
+ * o CLEAN lines track a cache entry that has been successfully published
+ * and may be read. A publish line is followed by the lengths of each of
+ * its values.
+ * o READ lines track accesses for LRU.
+ * o REMOVE lines track entries that have been deleted.
+ *
+ * The journal file is appended to as cache operations occur. The journal may
+ * occasionally be compacted by dropping redundant lines. A temporary file named
+ * "journal.tmp" will be used during compaction; that file should be deleted if
+ * it exists when the cache is opened.
+ */
+
+ private final File directory;
+ private final File journalFile;
+ private final File journalFileTmp;
+ private final File journalFileBackup;
+ private final int appVersion;
+ private long maxSize;
+ private final int valueCount;
+ private long size = 0;
+ private Writer journalWriter;
+ private final LinkedHashMap lruEntries =
+ new LinkedHashMap(0, 0.75f, true);
+ private int redundantOpCount;
+
+ /**
+ * To differentiate between old and current snapshots, each entry is given
+ * a sequence number each time an edit is committed. A snapshot is stale if
+ * its sequence number is not equal to its entry's sequence number.
+ */
+ private long nextSequenceNumber = 0;
+
+ /** This cache uses a single background thread to evict entries. */
+ final ThreadPoolExecutor executorService =
+ new ThreadPoolExecutor(0, 1, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue());
+ private final Callable cleanupCallable = new Callable() {
+ public Void call() throws Exception {
+ synchronized (DiskLruCache.this) {
+ if (journalWriter == null) {
+ return null; // Closed.
+ }
+ trimToSize();
+ if (journalRebuildRequired()) {
+ rebuildJournal();
+ redundantOpCount = 0;
+ }
+ }
+ return null;
+ }
+ };
+
+ private DiskLruCache(File directory, int appVersion, int valueCount, long maxSize) {
+ this.directory = directory;
+ this.appVersion = appVersion;
+ this.journalFile = new File(directory, JOURNAL_FILE);
+ this.journalFileTmp = new File(directory, JOURNAL_FILE_TEMP);
+ this.journalFileBackup = new File(directory, JOURNAL_FILE_BACKUP);
+ this.valueCount = valueCount;
+ this.maxSize = maxSize;
+ }
+
+ /**
+ * Opens the cache in {@code directory}, creating a cache if none exists
+ * there.
+ *
+ * @param directory a writable directory
+ * @param valueCount the number of values per cache entry. Must be positive.
+ * @param maxSize the maximum number of bytes this cache should use to store
+ * @throws IOException if reading or writing the cache directory fails
+ */
+ public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
+ throws IOException {
+ if (maxSize <= 0) {
+ throw new IllegalArgumentException("maxSize <= 0");
+ }
+ if (valueCount <= 0) {
+ throw new IllegalArgumentException("valueCount <= 0");
+ }
+
+ // If a bkp file exists, use it instead.
+ File backupFile = new File(directory, JOURNAL_FILE_BACKUP);
+ if (backupFile.exists()) {
+ File journalFile = new File(directory, JOURNAL_FILE);
+ // If journal file also exists just delete backup file.
+ if (journalFile.exists()) {
+ backupFile.delete();
+ } else {
+ renameTo(backupFile, journalFile, false);
+ }
+ }
+
+ // Prefer to pick up where we left off.
+ DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
+ if (cache.journalFile.exists()) {
+ try {
+ cache.readJournal();
+ cache.processJournal();
+ return cache;
+ } catch (IOException journalIsCorrupt) {
+ System.out
+ .println("DiskLruCache "
+ + directory
+ + " is corrupt: "
+ + journalIsCorrupt.getMessage()
+ + ", removing");
+ cache.delete();
+ }
+ }
+
+ // Create a new empty cache.
+ directory.mkdirs();
+ cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
+ cache.rebuildJournal();
+ return cache;
+ }
+
+ private void readJournal() throws IOException {
+ StrictLineReader reader = new StrictLineReader(new FileInputStream(journalFile), Util.US_ASCII);
+ try {
+ String magic = reader.readLine();
+ String version = reader.readLine();
+ String appVersionString = reader.readLine();
+ String valueCountString = reader.readLine();
+ String blank = reader.readLine();
+ if (!MAGIC.equals(magic)
+ || !VERSION_1.equals(version)
+ || !Integer.toString(appVersion).equals(appVersionString)
+ || !Integer.toString(valueCount).equals(valueCountString)
+ || !"".equals(blank)) {
+ throw new IOException("unexpected journal header: [" + magic + ", " + version + ", "
+ + valueCountString + ", " + blank + "]");
+ }
+
+ int lineCount = 0;
+ while (true) {
+ try {
+ readJournalLine(reader.readLine());
+ lineCount++;
+ } catch (EOFException endOfJournal) {
+ break;
+ }
+ }
+ redundantOpCount = lineCount - lruEntries.size();
+
+ // If we ended on a truncated line, rebuild the journal before appending to it.
+ if (reader.hasUnterminatedLine()) {
+ rebuildJournal();
+ } else {
+ journalWriter = new BufferedWriter(new OutputStreamWriter(
+ new FileOutputStream(journalFile, true), Util.US_ASCII));
+ }
+ } finally {
+ Util.closeQuietly(reader);
+ }
+ }
+
+ private void readJournalLine(String line) throws IOException {
+ int firstSpace = line.indexOf(' ');
+ if (firstSpace == -1) {
+ throw new IOException("unexpected journal line: " + line);
+ }
+
+ int keyBegin = firstSpace + 1;
+ int secondSpace = line.indexOf(' ', keyBegin);
+ final String key;
+ if (secondSpace == -1) {
+ key = line.substring(keyBegin);
+ if (firstSpace == REMOVE.length() && line.startsWith(REMOVE)) {
+ lruEntries.remove(key);
+ return;
+ }
+ } else {
+ key = line.substring(keyBegin, secondSpace);
+ }
+
+ Entry entry = lruEntries.get(key);
+ if (entry == null) {
+ entry = new Entry(key);
+ lruEntries.put(key, entry);
+ }
+
+ if (secondSpace != -1 && firstSpace == CLEAN.length() && line.startsWith(CLEAN)) {
+ String[] parts = line.substring(secondSpace + 1).split(" ");
+ entry.readable = true;
+ entry.currentEditor = null;
+ entry.setLengths(parts);
+ } else if (secondSpace == -1 && firstSpace == DIRTY.length() && line.startsWith(DIRTY)) {
+ entry.currentEditor = new Editor(entry);
+ } else if (secondSpace == -1 && firstSpace == READ.length() && line.startsWith(READ)) {
+ // This work was already done by calling lruEntries.get().
+ } else {
+ throw new IOException("unexpected journal line: " + line);
+ }
+ }
+
+ /**
+ * Computes the initial size and collects garbage as a part of opening the
+ * cache. Dirty entries are assumed to be inconsistent and will be deleted.
+ */
+ private void processJournal() throws IOException {
+ deleteIfExists(journalFileTmp);
+ for (Iterator i = lruEntries.values().iterator(); i.hasNext(); ) {
+ Entry entry = i.next();
+ if (entry.currentEditor == null) {
+ for (int t = 0; t < valueCount; t++) {
+ size += entry.lengths[t];
+ }
+ } else {
+ entry.currentEditor = null;
+ for (int t = 0; t < valueCount; t++) {
+ deleteIfExists(entry.getCleanFile(t));
+ deleteIfExists(entry.getDirtyFile(t));
+ }
+ i.remove();
+ }
+ }
+ }
+
+ /**
+ * Creates a new journal that omits redundant information. This replaces the
+ * current journal if it exists.
+ */
+ private synchronized void rebuildJournal() throws IOException {
+ if (journalWriter != null) {
+ journalWriter.close();
+ }
+
+ Writer writer = new BufferedWriter(
+ new OutputStreamWriter(new FileOutputStream(journalFileTmp), Util.US_ASCII));
+ try {
+ writer.write(MAGIC);
+ writer.write("\n");
+ writer.write(VERSION_1);
+ writer.write("\n");
+ writer.write(Integer.toString(appVersion));
+ writer.write("\n");
+ writer.write(Integer.toString(valueCount));
+ writer.write("\n");
+ writer.write("\n");
+
+ for (Entry entry : lruEntries.values()) {
+ if (entry.currentEditor != null) {
+ writer.write(DIRTY + ' ' + entry.key + '\n');
+ } else {
+ writer.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n');
+ }
+ }
+ } finally {
+ writer.close();
+ }
+
+ if (journalFile.exists()) {
+ renameTo(journalFile, journalFileBackup, true);
+ }
+ renameTo(journalFileTmp, journalFile, false);
+ journalFileBackup.delete();
+
+ journalWriter = new BufferedWriter(
+ new OutputStreamWriter(new FileOutputStream(journalFile, true), Util.US_ASCII));
+ }
+
+ private static void deleteIfExists(File file) throws IOException {
+ if (file.exists() && !file.delete()) {
+ throw new IOException();
+ }
+ }
+
+ private static void renameTo(File from, File to, boolean deleteDestination) throws IOException {
+ if (deleteDestination) {
+ deleteIfExists(to);
+ }
+ if (!from.renameTo(to)) {
+ throw new IOException();
+ }
+ }
+
+ /**
+ * Returns a snapshot of the entry named {@code key}, or null if it doesn't
+ * exist is not currently readable. If a value is returned, it is moved to
+ * the head of the LRU queue.
+ */
+ public synchronized Snapshot get(String key) throws IOException {
+ checkNotClosed();
+ validateKey(key);
+ Entry entry = lruEntries.get(key);
+ if (entry == null) {
+ return null;
+ }
+
+ if (!entry.readable) {
+ return null;
+ }
+
+ // Open all streams eagerly to guarantee that we see a single published
+ // snapshot. If we opened streams lazily then the streams could come
+ // from different edits.
+ InputStream[] ins = new InputStream[valueCount];
+ try {
+ for (int i = 0; i < valueCount; i++) {
+ ins[i] = new FileInputStream(entry.getCleanFile(i));
+ }
+ } catch (FileNotFoundException e) {
+ // A file must have been deleted manually!
+ for (int i = 0; i < valueCount; i++) {
+ if (ins[i] != null) {
+ Util.closeQuietly(ins[i]);
+ } else {
+ break;
+ }
+ }
+ return null;
+ }
+
+ redundantOpCount++;
+ journalWriter.append(READ + ' ' + key + '\n');
+ if (journalRebuildRequired()) {
+ executorService.submit(cleanupCallable);
+ }
+
+ return new Snapshot(key, entry.sequenceNumber, ins, entry.lengths);
+ }
+
+ /**
+ * Returns an editor for the entry named {@code key}, or null if another
+ * edit is in progress.
+ */
+ public Editor edit(String key) throws IOException {
+ return edit(key, ANY_SEQUENCE_NUMBER);
+ }
+
+ private synchronized Editor edit(String key, long expectedSequenceNumber) throws IOException {
+ checkNotClosed();
+ validateKey(key);
+ Entry entry = lruEntries.get(key);
+ if (expectedSequenceNumber != ANY_SEQUENCE_NUMBER && (entry == null
+ || entry.sequenceNumber != expectedSequenceNumber)) {
+ return null; // Snapshot is stale.
+ }
+ if (entry == null) {
+ entry = new Entry(key);
+ lruEntries.put(key, entry);
+ } else if (entry.currentEditor != null) {
+ return null; // Another edit is in progress.
+ }
+
+ Editor editor = new Editor(entry);
+ entry.currentEditor = editor;
+
+ // Flush the journal before creating files to prevent file leaks.
+ journalWriter.write(DIRTY + ' ' + key + '\n');
+ journalWriter.flush();
+ return editor;
+ }
+
+ /** Returns the directory where this cache stores its data. */
+ public File getDirectory() {
+ return directory;
+ }
+
+ /**
+ * Returns the maximum number of bytes that this cache should use to store
+ * its data.
+ */
+ public synchronized long getMaxSize() {
+ return maxSize;
+ }
+
+ /**
+ * Changes the maximum number of bytes the cache can store and queues a job
+ * to trim the existing store, if necessary.
+ */
+ public synchronized void setMaxSize(long maxSize) {
+ this.maxSize = maxSize;
+ executorService.submit(cleanupCallable);
+ }
+
+ /**
+ * Returns the number of bytes currently being used to store the values in
+ * this cache. This may be greater than the max size if a background
+ * deletion is pending.
+ */
+ public synchronized long size() {
+ return size;
+ }
+
+ private synchronized void completeEdit(Editor editor, boolean success) throws IOException {
+ Entry entry = editor.entry;
+ if (entry.currentEditor != editor) {
+ throw new IllegalStateException();
+ }
+
+ // If this edit is creating the entry for the first time, every index must have a value.
+ if (success && !entry.readable) {
+ for (int i = 0; i < valueCount; i++) {
+ if (!editor.written[i]) {
+ editor.abort();
+ throw new IllegalStateException("Newly created entry didn't create value for index " + i);
+ }
+ if (!entry.getDirtyFile(i).exists()) {
+ editor.abort();
+ return;
+ }
+ }
+ }
+
+ for (int i = 0; i < valueCount; i++) {
+ File dirty = entry.getDirtyFile(i);
+ if (success) {
+ if (dirty.exists()) {
+ File clean = entry.getCleanFile(i);
+ dirty.renameTo(clean);
+ long oldLength = entry.lengths[i];
+ long newLength = clean.length();
+ entry.lengths[i] = newLength;
+ size = size - oldLength + newLength;
+ }
+ } else {
+ deleteIfExists(dirty);
+ }
+ }
+
+ redundantOpCount++;
+ entry.currentEditor = null;
+ if (entry.readable | success) {
+ entry.readable = true;
+ journalWriter.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n');
+ if (success) {
+ entry.sequenceNumber = nextSequenceNumber++;
+ }
+ } else {
+ lruEntries.remove(entry.key);
+ journalWriter.write(REMOVE + ' ' + entry.key + '\n');
+ }
+ journalWriter.flush();
+
+ if (size > maxSize || journalRebuildRequired()) {
+ executorService.submit(cleanupCallable);
+ }
+ }
+
+ /**
+ * We only rebuild the journal when it will halve the size of the journal
+ * and eliminate at least 2000 ops.
+ */
+ private boolean journalRebuildRequired() {
+ final int redundantOpCompactThreshold = 2000;
+ return redundantOpCount >= redundantOpCompactThreshold //
+ && redundantOpCount >= lruEntries.size();
+ }
+
+ /**
+ * Drops the entry for {@code key} if it exists and can be removed. Entries
+ * actively being edited cannot be removed.
+ *
+ * @return true if an entry was removed.
+ */
+ public synchronized boolean remove(String key) throws IOException {
+ checkNotClosed();
+ validateKey(key);
+ Entry entry = lruEntries.get(key);
+ if (entry == null || entry.currentEditor != null) {
+ return false;
+ }
+
+ for (int i = 0; i < valueCount; i++) {
+ File file = entry.getCleanFile(i);
+ if (file.exists() && !file.delete()) {
+ throw new IOException("failed to delete " + file);
+ }
+ size -= entry.lengths[i];
+ entry.lengths[i] = 0;
+ }
+
+ redundantOpCount++;
+ journalWriter.append(REMOVE + ' ' + key + '\n');
+ lruEntries.remove(key);
+
+ if (journalRebuildRequired()) {
+ executorService.submit(cleanupCallable);
+ }
+
+ return true;
+ }
+
+ /** Returns true if this cache has been closed. */
+ public synchronized boolean isClosed() {
+ return journalWriter == null;
+ }
+
+ private void checkNotClosed() {
+ if (journalWriter == null) {
+ throw new IllegalStateException("cache is closed");
+ }
+ }
+
+ /** Force buffered operations to the filesystem. */
+ public synchronized void flush() throws IOException {
+ checkNotClosed();
+ trimToSize();
+ journalWriter.flush();
+ }
+
+ /** Closes this cache. Stored values will remain on the filesystem. */
+ public synchronized void close() throws IOException {
+ if (journalWriter == null) {
+ return; // Already closed.
+ }
+ for (Entry entry : new ArrayList(lruEntries.values())) {
+ if (entry.currentEditor != null) {
+ entry.currentEditor.abort();
+ }
+ }
+ trimToSize();
+ journalWriter.close();
+ journalWriter = null;
+ }
+
+ private void trimToSize() throws IOException {
+ while (size > maxSize) {
+ Map.Entry toEvict = lruEntries.entrySet().iterator().next();
+ remove(toEvict.getKey());
+ }
+ }
+
+ /**
+ * Closes the cache and deletes all of its stored values. This will delete
+ * all files in the cache directory including files that weren't created by
+ * the cache.
+ */
+ public void delete() throws IOException {
+ close();
+ Util.deleteContents(directory);
+ }
+
+ private void validateKey(String key) {
+ Matcher matcher = LEGAL_KEY_PATTERN.matcher(key);
+ if (!matcher.matches()) {
+ throw new IllegalArgumentException("keys must match regex "
+ + STRING_KEY_PATTERN + ": \"" + key + "\"");
+ }
+ }
+
+ private static String inputStreamToString(InputStream in) throws IOException {
+ return Util.readFully(new InputStreamReader(in, Util.UTF_8));
+ }
+
+ /** A snapshot of the values for an entry. */
+ public final class Snapshot implements Closeable {
+ private final String key;
+ private final long sequenceNumber;
+ private final InputStream[] ins;
+ private final long[] lengths;
+
+ private Snapshot(String key, long sequenceNumber, InputStream[] ins, long[] lengths) {
+ this.key = key;
+ this.sequenceNumber = sequenceNumber;
+ this.ins = ins;
+ this.lengths = lengths;
+ }
+
+ /**
+ * Returns an editor for this snapshot's entry, or null if either the
+ * entry has changed since this snapshot was created or if another edit
+ * is in progress.
+ */
+ public Editor edit() throws IOException {
+ return DiskLruCache.this.edit(key, sequenceNumber);
+ }
+
+ /** Returns the unbuffered stream with the value for {@code index}. */
+ public InputStream getInputStream(int index) {
+ return ins[index];
+ }
+
+ /** Returns the string value for {@code index}. */
+ public String getString(int index) throws IOException {
+ return inputStreamToString(getInputStream(index));
+ }
+
+ /** Returns the byte length of the value for {@code index}. */
+ public long getLength(int index) {
+ return lengths[index];
+ }
+
+ public void close() {
+ for (InputStream in : ins) {
+ Util.closeQuietly(in);
+ }
+ }
+ }
+
+ private static final OutputStream NULL_OUTPUT_STREAM = new OutputStream() {
+ @Override
+ public void write(int b) throws IOException {
+ // Eat all writes silently. Nom nom.
+ }
+ };
+
+ /** Edits the values for an entry. */
+ public final class Editor {
+ private final Entry entry;
+ private final boolean[] written;
+ private boolean hasErrors;
+ private boolean committed;
+
+ private Editor(Entry entry) {
+ this.entry = entry;
+ this.written = (entry.readable) ? null : new boolean[valueCount];
+ }
+
+ /**
+ * Returns an unbuffered input stream to read the last committed value,
+ * or null if no value has been committed.
+ */
+ public InputStream newInputStream(int index) throws IOException {
+ synchronized (DiskLruCache.this) {
+ if (entry.currentEditor != this) {
+ throw new IllegalStateException();
+ }
+ if (!entry.readable) {
+ return null;
+ }
+ try {
+ return new FileInputStream(entry.getCleanFile(index));
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Returns the last committed value as a string, or null if no value
+ * has been committed.
+ */
+ public String getString(int index) throws IOException {
+ InputStream in = newInputStream(index);
+ return in != null ? inputStreamToString(in) : null;
+ }
+
+ /**
+ * Returns a new unbuffered output stream to write the value at
+ * {@code index}. If the underlying output stream encounters errors
+ * when writing to the filesystem, this edit will be aborted when
+ * {@link #commit} is called. The returned output stream does not throw
+ * IOExceptions.
+ */
+ public OutputStream newOutputStream(int index) throws IOException {
+ if (index < 0 || index >= valueCount) {
+ throw new IllegalArgumentException("Expected index " + index + " to "
+ + "be greater than 0 and less than the maximum value count "
+ + "of " + valueCount);
+ }
+ synchronized (DiskLruCache.this) {
+ if (entry.currentEditor != this) {
+ throw new IllegalStateException();
+ }
+ if (!entry.readable) {
+ written[index] = true;
+ }
+ File dirtyFile = entry.getDirtyFile(index);
+ FileOutputStream outputStream;
+ try {
+ outputStream = new FileOutputStream(dirtyFile);
+ } catch (FileNotFoundException e) {
+ // Attempt to recreate the cache directory.
+ directory.mkdirs();
+ try {
+ outputStream = new FileOutputStream(dirtyFile);
+ } catch (FileNotFoundException e2) {
+ // We are unable to recover. Silently eat the writes.
+ return NULL_OUTPUT_STREAM;
+ }
+ }
+ return new FaultHidingOutputStream(outputStream);
+ }
+ }
+
+ /** Sets the value at {@code index} to {@code value}. */
+ public void set(int index, String value) throws IOException {
+ Writer writer = null;
+ try {
+ writer = new OutputStreamWriter(newOutputStream(index), Util.UTF_8);
+ writer.write(value);
+ } finally {
+ Util.closeQuietly(writer);
+ }
+ }
+
+ /**
+ * Commits this edit so it is visible to readers. This releases the
+ * edit lock so another edit may be started on the same key.
+ */
+ public void commit() throws IOException {
+ if (hasErrors) {
+ completeEdit(this, false);
+ remove(entry.key); // The previous entry is stale.
+ } else {
+ completeEdit(this, true);
+ }
+ committed = true;
+ }
+
+ /**
+ * Aborts this edit. This releases the edit lock so another edit may be
+ * started on the same key.
+ */
+ public void abort() throws IOException {
+ completeEdit(this, false);
+ }
+
+ public void abortUnlessCommitted() {
+ if (!committed) {
+ try {
+ abort();
+ } catch (IOException ignored) {
+ }
+ }
+ }
+
+ private class FaultHidingOutputStream extends FilterOutputStream {
+ private FaultHidingOutputStream(OutputStream out) {
+ super(out);
+ }
+
+ @Override public void write(int oneByte) {
+ try {
+ out.write(oneByte);
+ } catch (IOException e) {
+ hasErrors = true;
+ }
+ }
+
+ @Override public void write(byte[] buffer, int offset, int length) {
+ try {
+ out.write(buffer, offset, length);
+ } catch (IOException e) {
+ hasErrors = true;
+ }
+ }
+
+ @Override public void close() {
+ try {
+ out.close();
+ } catch (IOException e) {
+ hasErrors = true;
+ }
+ }
+
+ @Override public void flush() {
+ try {
+ out.flush();
+ } catch (IOException e) {
+ hasErrors = true;
+ }
+ }
+ }
+ }
+
+ private final class Entry {
+ private final String key;
+
+ /** Lengths of this entry's files. */
+ private final long[] lengths;
+
+ /** True if this entry has ever been published. */
+ private boolean readable;
+
+ /** The ongoing edit or null if this entry is not being edited. */
+ private Editor currentEditor;
+
+ /** The sequence number of the most recently committed edit to this entry. */
+ private long sequenceNumber;
+
+ private Entry(String key) {
+ this.key = key;
+ this.lengths = new long[valueCount];
+ }
+
+ public String getLengths() throws IOException {
+ StringBuilder result = new StringBuilder();
+ for (long size : lengths) {
+ result.append(' ').append(size);
+ }
+ return result.toString();
+ }
+
+ /** Set lengths using decimal numbers like "10123". */
+ private void setLengths(String[] strings) throws IOException {
+ if (strings.length != valueCount) {
+ throw invalidLengths(strings);
+ }
+
+ try {
+ for (int i = 0; i < strings.length; i++) {
+ lengths[i] = Long.parseLong(strings[i]);
+ }
+ } catch (NumberFormatException e) {
+ throw invalidLengths(strings);
+ }
+ }
+
+ private IOException invalidLengths(String[] strings) throws IOException {
+ throw new IOException("unexpected journal line: " + java.util.Arrays.toString(strings));
+ }
+
+ public File getCleanFile(int i) {
+ return new File(directory, key + "." + i);
+ }
+
+ public File getDirtyFile(int i) {
+ return new File(directory, key + "." + i + ".tmp");
+ }
+ }
+}
diff --git a/app/src/main/java/com/tdkankan/libcore/io/StrictLineReader.java b/app/src/main/java/com/tdkankan/libcore/io/StrictLineReader.java
new file mode 100644
index 0000000..2175ede
--- /dev/null
+++ b/app/src/main/java/com/tdkankan/libcore/io/StrictLineReader.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.tdkankan.libcore.io;
+
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+
+/**
+ * Buffers input from an {@link InputStream} for reading lines.
+ *
+ *
This class is used for buffered reading of lines. For purposes of this class, a line ends
+ * with "\n" or "\r\n". End of input is reported by throwing {@code EOFException}. Unterminated
+ * line at end of input is invalid and will be ignored, the caller may use {@code
+ * hasUnterminatedLine()} to detect it after catching the {@code EOFException}.
+ *
+ *
This class is intended for reading input that strictly consists of lines, such as line-based
+ * cache entries or cache journal. Unlike the {@link java.io.BufferedReader} which in conjunction
+ * with {@link java.io.InputStreamReader} provides similar functionality, this class uses different
+ * end-of-input reporting and a more restrictive definition of a line.
+ *
+ *
This class supports only charsets that encode '\r' and '\n' as a single byte with value 13
+ * and 10, respectively, and the representation of no other character contains these values.
+ * We currently check in constructor that the charset is one of US-ASCII, UTF-8 and ISO-8859-1.
+ * The default charset is US_ASCII.
+ */
+class StrictLineReader implements Closeable {
+ private static final byte CR = (byte) '\r';
+ private static final byte LF = (byte) '\n';
+
+ private final InputStream in;
+ private final Charset charset;
+
+ /*
+ * Buffered data is stored in {@code buf}. As long as no exception occurs, 0 <= pos <= end
+ * and the data in the range [pos, end) is buffered for reading. At end of input, if there is
+ * an unterminated line, we set end == -1, otherwise end == pos. If the underlying
+ * {@code InputStream} throws an {@code IOException}, end may remain as either pos or -1.
+ */
+ private byte[] buf;
+ private int pos;
+ private int end;
+
+ /**
+ * Constructs a new {@code LineReader} with the specified charset and the default capacity.
+ *
+ * @param in the {@code InputStream} to read data from.
+ * @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are
+ * supported.
+ * @throws NullPointerException if {@code in} or {@code charset} is null.
+ * @throws IllegalArgumentException if the specified charset is not supported.
+ */
+ public StrictLineReader(InputStream in, Charset charset) {
+ this(in, 8192, charset);
+ }
+
+ /**
+ * Constructs a new {@code LineReader} with the specified capacity and charset.
+ *
+ * @param in the {@code InputStream} to read data from.
+ * @param capacity the capacity of the buffer.
+ * @param charset the charset used to decode data. Only US-ASCII, UTF-8 and ISO-8859-1 are
+ * supported.
+ * @throws NullPointerException if {@code in} or {@code charset} is null.
+ * @throws IllegalArgumentException if {@code capacity} is negative or zero
+ * or the specified charset is not supported.
+ */
+ public StrictLineReader(InputStream in, int capacity, Charset charset) {
+ if (in == null || charset == null) {
+ throw new NullPointerException();
+ }
+ if (capacity < 0) {
+ throw new IllegalArgumentException("capacity <= 0");
+ }
+ if (!(charset.equals(Util.US_ASCII))) {
+ throw new IllegalArgumentException("Unsupported encoding");
+ }
+
+ this.in = in;
+ this.charset = charset;
+ buf = new byte[capacity];
+ }
+
+ /**
+ * Closes the reader by closing the underlying {@code InputStream} and
+ * marking this reader as closed.
+ *
+ * @throws IOException for errors when closing the underlying {@code InputStream}.
+ */
+ public void close() throws IOException {
+ synchronized (in) {
+ if (buf != null) {
+ buf = null;
+ in.close();
+ }
+ }
+ }
+
+ /**
+ * Reads the next line. A line ends with {@code "\n"} or {@code "\r\n"},
+ * this end of line marker is not included in the result.
+ *
+ * @return the next line from the input.
+ * @throws IOException for underlying {@code InputStream} errors.
+ * @throws EOFException for the end of source stream.
+ */
+ public String readLine() throws IOException {
+ synchronized (in) {
+ if (buf == null) {
+ throw new IOException("LineReader is closed");
+ }
+
+ // Read more data if we are at the end of the buffered data.
+ // Though it's an error to read after an exception, we will let {@code fillBuf()}
+ // throw again if that happens; thus we need to handle end == -1 as well as end == pos.
+ if (pos >= end) {
+ fillBuf();
+ }
+ // Try to find LF in the buffered data and return the line if successful.
+ for (int i = pos; i != end; ++i) {
+ if (buf[i] == LF) {
+ int lineEnd = (i != pos && buf[i - 1] == CR) ? i - 1 : i;
+ String res = new String(buf, pos, lineEnd - pos, charset.name());
+ pos = i + 1;
+ return res;
+ }
+ }
+
+ // Let's anticipate up to 80 characters on top of those already read.
+ ByteArrayOutputStream out = new ByteArrayOutputStream(end - pos + 80) {
+ @Override
+ public String toString() {
+ int length = (count > 0 && buf[count - 1] == CR) ? count - 1 : count;
+ try {
+ return new String(buf, 0, length, charset.name());
+ } catch (UnsupportedEncodingException e) {
+ throw new AssertionError(e); // Since we control the charset this will never happen.
+ }
+ }
+ };
+
+ while (true) {
+ out.write(buf, pos, end - pos);
+ // Mark unterminated line in case fillBuf throws EOFException or IOException.
+ end = -1;
+ fillBuf();
+ // Try to find LF in the buffered data and return the line if successful.
+ for (int i = pos; i != end; ++i) {
+ if (buf[i] == LF) {
+ if (i != pos) {
+ out.write(buf, pos, i - pos);
+ }
+ pos = i + 1;
+ return out.toString();
+ }
+ }
+ }
+ }
+ }
+
+ public boolean hasUnterminatedLine() {
+ return end == -1;
+ }
+
+ /**
+ * Reads new input data into the buffer. Call only with pos == end or end == -1,
+ * depending on the desired outcome if the function throws.
+ */
+ private void fillBuf() throws IOException {
+ int result = in.read(buf, 0, buf.length);
+ if (result == -1) {
+ throw new EOFException();
+ }
+ pos = 0;
+ end = result;
+ }
+}
+
diff --git a/app/src/main/java/com/tdkankan/libcore/io/Util.java b/app/src/main/java/com/tdkankan/libcore/io/Util.java
new file mode 100644
index 0000000..1ce8457
--- /dev/null
+++ b/app/src/main/java/com/tdkankan/libcore/io/Util.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.tdkankan.libcore.io;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.nio.charset.Charset;
+
+/** Junk drawer of utility methods. */
+final class Util {
+ static final Charset US_ASCII = Charset.forName("US-ASCII");
+ static final Charset UTF_8 = Charset.forName("UTF-8");
+
+ private Util() {
+ }
+
+ static String readFully(Reader reader) throws IOException {
+ try {
+ StringWriter writer = new StringWriter();
+ char[] buffer = new char[1024];
+ int count;
+ while ((count = reader.read(buffer)) != -1) {
+ writer.write(buffer, 0, count);
+ }
+ return writer.toString();
+ } finally {
+ reader.close();
+ }
+ }
+
+ /**
+ * Deletes the contents of {@code dir}. Throws an IOException if any file
+ * could not be deleted, or if {@code dir} is not a readable directory.
+ */
+ static void deleteContents(File dir) throws IOException {
+ File[] files = dir.listFiles();
+ if (files == null) {
+ throw new IOException("not a readable directory: " + dir);
+ }
+ for (File file : files) {
+ if (file.isDirectory()) {
+ deleteContents(file);
+ }
+ if (!file.delete()) {
+ throw new IOException("failed to delete file: " + file);
+ }
+ }
+ }
+
+ static void closeQuietly(/*Auto*/Closeable closeable) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (RuntimeException rethrown) {
+ throw rethrown;
+ } catch (Exception ignored) {
+ }
+ }
+ }
+}
diff --git a/app/src/main/res/anim/fade_in.xml b/app/src/main/res/anim/fade_in.xml
new file mode 100644
index 0000000..14c3d3d
--- /dev/null
+++ b/app/src/main/res/anim/fade_in.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/anim/fade_out.xml b/app/src/main/res/anim/fade_out.xml
new file mode 100644
index 0000000..3e7d894
--- /dev/null
+++ b/app/src/main/res/anim/fade_out.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/anim/fading_in.xml b/app/src/main/res/anim/fading_in.xml
new file mode 100644
index 0000000..3730083
--- /dev/null
+++ b/app/src/main/res/anim/fading_in.xml
@@ -0,0 +1,9 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/anim/fading_out.xml b/app/src/main/res/anim/fading_out.xml
new file mode 100644
index 0000000..e063f1a
--- /dev/null
+++ b/app/src/main/res/anim/fading_out.xml
@@ -0,0 +1,9 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-anydpi/ic_action_add_icon.xml b/app/src/main/res/drawable-anydpi/ic_action_add_icon.xml
new file mode 100644
index 0000000..fa79705
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_action_add_icon.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/app/src/main/res/drawable-anydpi/ic_action_bookmark.xml b/app/src/main/res/drawable-anydpi/ic_action_bookmark.xml
new file mode 100644
index 0000000..530f464
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_action_bookmark.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/app/src/main/res/drawable-anydpi/ic_action_play_arrow.xml b/app/src/main/res/drawable-anydpi/ic_action_play_arrow.xml
new file mode 100644
index 0000000..420941a
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_action_play_arrow.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/app/src/main/res/drawable-anydpi/ic_action_search_icon.xml b/app/src/main/res/drawable-anydpi/ic_action_search_icon.xml
new file mode 100644
index 0000000..afb0429
--- /dev/null
+++ b/app/src/main/res/drawable-anydpi/ic_action_search_icon.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/app/src/main/res/drawable-hdpi/ic_action_add_icon.png b/app/src/main/res/drawable-hdpi/ic_action_add_icon.png
new file mode 100644
index 0000000..2b125f5
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_add_icon.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_bookmark.png b/app/src/main/res/drawable-hdpi/ic_action_bookmark.png
new file mode 100644
index 0000000..702b318
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_bookmark.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_play_arrow.png b/app/src/main/res/drawable-hdpi/ic_action_play_arrow.png
new file mode 100644
index 0000000..556c872
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_play_arrow.png differ
diff --git a/app/src/main/res/drawable-hdpi/ic_action_search_icon.png b/app/src/main/res/drawable-hdpi/ic_action_search_icon.png
new file mode 100644
index 0000000..f9c0afe
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_search_icon.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_add_icon.png b/app/src/main/res/drawable-mdpi/ic_action_add_icon.png
new file mode 100644
index 0000000..a69d739
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_add_icon.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_bookmark.png b/app/src/main/res/drawable-mdpi/ic_action_bookmark.png
new file mode 100644
index 0000000..de05c1e
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_bookmark.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_play_arrow.png b/app/src/main/res/drawable-mdpi/ic_action_play_arrow.png
new file mode 100644
index 0000000..2b42577
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_play_arrow.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_action_search_icon.png b/app/src/main/res/drawable-mdpi/ic_action_search_icon.png
new file mode 100644
index 0000000..6fd5a13
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_search_icon.png differ
diff --git a/app/src/main/res/drawable-mdpi/radius_30_white.xml b/app/src/main/res/drawable-mdpi/radius_30_white.xml
new file mode 100644
index 0000000..dee1290
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/radius_30_white.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..1f6bb29
--- /dev/null
+++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_add_icon.png b/app/src/main/res/drawable-xhdpi/ic_action_add_icon.png
new file mode 100644
index 0000000..4423088
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_add_icon.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_bookmark.png b/app/src/main/res/drawable-xhdpi/ic_action_bookmark.png
new file mode 100644
index 0000000..981273d
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_bookmark.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_play_arrow.png b/app/src/main/res/drawable-xhdpi/ic_action_play_arrow.png
new file mode 100644
index 0000000..7770ab2
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_play_arrow.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_action_search_icon.png b/app/src/main/res/drawable-xhdpi/ic_action_search_icon.png
new file mode 100644
index 0000000..aad212c
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_search_icon.png differ
diff --git a/app/src/main/res/drawable-xhdpi/setting_blue_deep_style_btn_bg.xml b/app/src/main/res/drawable-xhdpi/setting_blue_deep_style_btn_bg.xml
new file mode 100644
index 0000000..03044ce
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/setting_blue_deep_style_btn_bg.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-xhdpi/setting_breen_style_btn_bg.xml b/app/src/main/res/drawable-xhdpi/setting_breen_style_btn_bg.xml
new file mode 100644
index 0000000..23df42d
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/setting_breen_style_btn_bg.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-xhdpi/setting_common_style_btn_bg.xml b/app/src/main/res/drawable-xhdpi/setting_common_style_btn_bg.xml
new file mode 100644
index 0000000..a055eb1
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/setting_common_style_btn_bg.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-xhdpi/setting_leather_style_btn_bg.xml b/app/src/main/res/drawable-xhdpi/setting_leather_style_btn_bg.xml
new file mode 100644
index 0000000..affb285
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/setting_leather_style_btn_bg.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-xhdpi/setting_protect_eye_style_btn_bg.xml b/app/src/main/res/drawable-xhdpi/setting_protect_eye_style_btn_bg.xml
new file mode 100644
index 0000000..37c9226
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/setting_protect_eye_style_btn_bg.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-xhdpi/setting_style_blue_deep_circle.xml b/app/src/main/res/drawable-xhdpi/setting_style_blue_deep_circle.xml
new file mode 100644
index 0000000..2825928
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/setting_style_blue_deep_circle.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-xhdpi/setting_style_blue_deep_circle_selected.xml b/app/src/main/res/drawable-xhdpi/setting_style_blue_deep_circle_selected.xml
new file mode 100644
index 0000000..4b170f2
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/setting_style_blue_deep_circle_selected.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-xhdpi/setting_style_breen_circle.xml b/app/src/main/res/drawable-xhdpi/setting_style_breen_circle.xml
new file mode 100644
index 0000000..6c0b696
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/setting_style_breen_circle.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-xhdpi/setting_style_breen_circle_selected.xml b/app/src/main/res/drawable-xhdpi/setting_style_breen_circle_selected.xml
new file mode 100644
index 0000000..d5db6a4
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/setting_style_breen_circle_selected.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-xhdpi/setting_style_common_circle.xml b/app/src/main/res/drawable-xhdpi/setting_style_common_circle.xml
new file mode 100644
index 0000000..e23c3f0
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/setting_style_common_circle.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-xhdpi/setting_style_common_circle_selected.xml b/app/src/main/res/drawable-xhdpi/setting_style_common_circle_selected.xml
new file mode 100644
index 0000000..b6a6165
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/setting_style_common_circle_selected.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-xhdpi/setting_style_leather_circle.xml b/app/src/main/res/drawable-xhdpi/setting_style_leather_circle.xml
new file mode 100644
index 0000000..f300a09
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/setting_style_leather_circle.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-xhdpi/setting_style_leather_circle_selected.xml b/app/src/main/res/drawable-xhdpi/setting_style_leather_circle_selected.xml
new file mode 100644
index 0000000..a75c56a
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/setting_style_leather_circle_selected.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-xhdpi/setting_style_protect_eye_circle.xml b/app/src/main/res/drawable-xhdpi/setting_style_protect_eye_circle.xml
new file mode 100644
index 0000000..607998f
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/setting_style_protect_eye_circle.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-xhdpi/setting_style_protect_eye_circle_selected.xml b/app/src/main/res/drawable-xhdpi/setting_style_protect_eye_circle_selected.xml
new file mode 100644
index 0000000..75e89fe
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/setting_style_protect_eye_circle_selected.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_add_icon.png b/app/src/main/res/drawable-xxhdpi/ic_action_add_icon.png
new file mode 100644
index 0000000..94b04c8
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_add_icon.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_bookmark.png b/app/src/main/res/drawable-xxhdpi/ic_action_bookmark.png
new file mode 100644
index 0000000..25708bb
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_bookmark.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_play_arrow.png b/app/src/main/res/drawable-xxhdpi/ic_action_play_arrow.png
new file mode 100644
index 0000000..0e5abef
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_play_arrow.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_search_icon.png b/app/src/main/res/drawable-xxhdpi/ic_action_search_icon.png
new file mode 100644
index 0000000..64b54ca
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_search_icon.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/setting_btn_bg.xml b/app/src/main/res/drawable-xxxhdpi/setting_btn_bg.xml
new file mode 100644
index 0000000..4cef809
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/setting_btn_bg.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-xxxhdpi/setting_btn_bg1.xml b/app/src/main/res/drawable-xxxhdpi/setting_btn_bg1.xml
new file mode 100644
index 0000000..3348b73
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/setting_btn_bg1.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-xxxhdpi/setting_btn_bg2.xml b/app/src/main/res/drawable-xxxhdpi/setting_btn_bg2.xml
new file mode 100644
index 0000000..2d2ea5c
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/setting_btn_bg2.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-xxxhdpi/setting_btn_bg_color.xml b/app/src/main/res/drawable-xxxhdpi/setting_btn_bg_color.xml
new file mode 100644
index 0000000..cec4b6e
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/setting_btn_bg_color.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-xxxhdpi/setting_circle.xml b/app/src/main/res/drawable-xxxhdpi/setting_circle.xml
new file mode 100644
index 0000000..128b945
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/setting_circle.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/filled_activity_bg.xml b/app/src/main/res/drawable/filled_activity_bg.xml
new file mode 100644
index 0000000..0291bd6
--- /dev/null
+++ b/app/src/main/res/drawable/filled_activity_bg.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/filled_box.xml b/app/src/main/res/drawable/filled_box.xml
new file mode 100644
index 0000000..e776cc0
--- /dev/null
+++ b/app/src/main/res/drawable/filled_box.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..0d025f9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/jay_icon.JPG b/app/src/main/res/drawable/jay_icon.JPG
new file mode 100644
index 0000000..ce32488
Binary files /dev/null and b/app/src/main/res/drawable/jay_icon.JPG differ
diff --git a/app/src/main/res/drawable/nonepic.jpg b/app/src/main/res/drawable/nonepic.jpg
new file mode 100644
index 0000000..6ef89d6
Binary files /dev/null and b/app/src/main/res/drawable/nonepic.jpg differ
diff --git a/app/src/main/res/drawable/openbp.jpg b/app/src/main/res/drawable/openbp.jpg
new file mode 100644
index 0000000..af360f9
Binary files /dev/null and b/app/src/main/res/drawable/openbp.jpg differ
diff --git a/app/src/main/res/drawable/openbp2.jpg b/app/src/main/res/drawable/openbp2.jpg
new file mode 100644
index 0000000..ad3f82e
Binary files /dev/null and b/app/src/main/res/drawable/openbp2.jpg differ
diff --git a/app/src/main/res/drawable/radius_30_book_bg.xml b/app/src/main/res/drawable/radius_30_book_bg.xml
new file mode 100644
index 0000000..385eda0
--- /dev/null
+++ b/app/src/main/res/drawable/radius_30_book_bg.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/seekbar_bg.xml b/app/src/main/res/drawable/seekbar_bg.xml
new file mode 100644
index 0000000..17b4047
--- /dev/null
+++ b/app/src/main/res/drawable/seekbar_bg.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/shape_label_orange.xml b/app/src/main/res/drawable/shape_label_orange.xml
new file mode 100644
index 0000000..90196fa
--- /dev/null
+++ b/app/src/main/res/drawable/shape_label_orange.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_bookcity.xml b/app/src/main/res/layout/activity_bookcity.xml
new file mode 100644
index 0000000..0276e5c
--- /dev/null
+++ b/app/src/main/res/layout/activity_bookcity.xml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_bookcity_topfragment.xml b/app/src/main/res/layout/activity_bookcity_topfragment.xml
new file mode 100644
index 0000000..2a0c0ae
--- /dev/null
+++ b/app/src/main/res/layout/activity_bookcity_topfragment.xml
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_bookcity_typefragment.xml b/app/src/main/res/layout/activity_bookcity_typefragment.xml
new file mode 100644
index 0000000..b14ecaa
--- /dev/null
+++ b/app/src/main/res/layout/activity_bookcity_typefragment.xml
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_bookinfodetail.xml b/app/src/main/res/layout/activity_bookinfodetail.xml
new file mode 100644
index 0000000..9d4890c
--- /dev/null
+++ b/app/src/main/res/layout/activity_bookinfodetail.xml
@@ -0,0 +1,183 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_bookshelf.xml b/app/src/main/res/layout/activity_bookshelf.xml
new file mode 100644
index 0000000..af455e6
--- /dev/null
+++ b/app/src/main/res/layout/activity_bookshelf.xml
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..4fc2444
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_mainview.xml b/app/src/main/res/layout/activity_mainview.xml
new file mode 100644
index 0000000..91991bb
--- /dev/null
+++ b/app/src/main/res/layout/activity_mainview.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_read.xml b/app/src/main/res/layout/activity_read.xml
new file mode 100644
index 0000000..7ebf427
--- /dev/null
+++ b/app/src/main/res/layout/activity_read.xml
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml
new file mode 100644
index 0000000..d795a0d
--- /dev/null
+++ b/app/src/main/res/layout/activity_search.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_setting.xml b/app/src/main/res/layout/activity_setting.xml
new file mode 100644
index 0000000..71b8d98
--- /dev/null
+++ b/app/src/main/res/layout/activity_setting.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_splash_screen.xml b/app/src/main/res/layout/activity_splash_screen.xml
new file mode 100644
index 0000000..acd0db2
--- /dev/null
+++ b/app/src/main/res/layout/activity_splash_screen.xml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/bookitem.xml b/app/src/main/res/layout/bookitem.xml
new file mode 100644
index 0000000..a96ef66
--- /dev/null
+++ b/app/src/main/res/layout/bookitem.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/bookitem2.xml b/app/src/main/res/layout/bookitem2.xml
new file mode 100644
index 0000000..c70398d
--- /dev/null
+++ b/app/src/main/res/layout/bookitem2.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/bookitem3.xml b/app/src/main/res/layout/bookitem3.xml
new file mode 100644
index 0000000..13b30d2
--- /dev/null
+++ b/app/src/main/res/layout/bookitem3.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_read_setting.xml b/app/src/main/res/layout/dialog_read_setting.xml
new file mode 100644
index 0000000..b2289c3
--- /dev/null
+++ b/app/src/main/res/layout/dialog_read_setting.xml
@@ -0,0 +1,216 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_read_setting_detail.xml b/app/src/main/res/layout/dialog_read_setting_detail.xml
new file mode 100644
index 0000000..a82b271
--- /dev/null
+++ b/app/src/main/res/layout/dialog_read_setting_detail.xml
@@ -0,0 +1,254 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/listview_bookitem.xml b/app/src/main/res/layout/listview_bookitem.xml
new file mode 100644
index 0000000..30e20e5
--- /dev/null
+++ b/app/src/main/res/layout/listview_bookitem.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/listview_bookitem2.xml b/app/src/main/res/layout/listview_bookitem2.xml
new file mode 100644
index 0000000..891c8a0
--- /dev/null
+++ b/app/src/main/res/layout/listview_bookitem2.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/listview_bookshelf.xml b/app/src/main/res/layout/listview_bookshelf.xml
new file mode 100644
index 0000000..ad58547
--- /dev/null
+++ b/app/src/main/res/layout/listview_bookshelf.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/listview_chapter_list.xml b/app/src/main/res/layout/listview_chapter_list.xml
new file mode 100644
index 0000000..ac80d91
--- /dev/null
+++ b/app/src/main/res/layout/listview_chapter_list.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/listview_topfragment.xml b/app/src/main/res/layout/listview_topfragment.xml
new file mode 100644
index 0000000..3a5ea50
--- /dev/null
+++ b/app/src/main/res/layout/listview_topfragment.xml
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/view_table.xml b/app/src/main/res/layout/view_table.xml
new file mode 100644
index 0000000..f093dcb
--- /dev/null
+++ b/app/src/main/res/layout/view_table.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..898f3ed
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_bookcity.png b/app/src/main/res/mipmap-hdpi/ic_launcher_bookcity.png
new file mode 100644
index 0000000..db1fae7
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_bookcity.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_bookcity_select.png b/app/src/main/res/mipmap-hdpi/ic_launcher_bookcity_select.png
new file mode 100644
index 0000000..b4982b8
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_bookcity_select.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..dffca36
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_setting.png b/app/src/main/res/mipmap-hdpi/ic_launcher_setting.png
new file mode 100644
index 0000000..d40cc15
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_setting.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_setting_select.png b/app/src/main/res/mipmap-hdpi/ic_launcher_setting_select.png
new file mode 100644
index 0000000..ca489c2
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_setting_select.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_shujia.png b/app/src/main/res/mipmap-hdpi/ic_launcher_shujia.png
new file mode 100644
index 0000000..e39a44e
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_shujia.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_shujia_select.png b/app/src/main/res/mipmap-hdpi/ic_launcher_shujia_select.png
new file mode 100644
index 0000000..e6e0ee9
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_shujia_select.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..64ba76f
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_bookcity.png b/app/src/main/res/mipmap-mdpi/ic_launcher_bookcity.png
new file mode 100644
index 0000000..aecbf06
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_bookcity.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_bookcity_select.png b/app/src/main/res/mipmap-mdpi/ic_launcher_bookcity_select.png
new file mode 100644
index 0000000..d3a986f
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_bookcity_select.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..dae5e08
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_setting.png b/app/src/main/res/mipmap-mdpi/ic_launcher_setting.png
new file mode 100644
index 0000000..59bbcc3
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_setting.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_setting_select.png b/app/src/main/res/mipmap-mdpi/ic_launcher_setting_select.png
new file mode 100644
index 0000000..567e61e
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_setting_select.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_shujia.png b/app/src/main/res/mipmap-mdpi/ic_launcher_shujia.png
new file mode 100644
index 0000000..a415377
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_shujia.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_shujia_select.png b/app/src/main/res/mipmap-mdpi/ic_launcher_shujia_select.png
new file mode 100644
index 0000000..0583e7c
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_shujia_select.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/back.png b/app/src/main/res/mipmap-xhdpi/back.png
new file mode 100644
index 0000000..3d0818e
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/back.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/bd.png b/app/src/main/res/mipmap-xhdpi/bd.png
new file mode 100644
index 0000000..99712d1
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/bd.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/catalogue.png b/app/src/main/res/mipmap-xhdpi/catalogue.png
new file mode 100644
index 0000000..ce757e6
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/catalogue.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/darkmoon.png b/app/src/main/res/mipmap-xhdpi/darkmoon.png
new file mode 100644
index 0000000..c790108
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/darkmoon.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/daylight.png b/app/src/main/res/mipmap-xhdpi/daylight.png
new file mode 100644
index 0000000..2ab6ff4
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/daylight.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/download.png b/app/src/main/res/mipmap-xhdpi/download.png
new file mode 100644
index 0000000..206a76c
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/download.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..e5ed465
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_bookcity.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_bookcity.png
new file mode 100644
index 0000000..bc78766
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_bookcity.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_bookcity_select.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_bookcity_select.png
new file mode 100644
index 0000000..45c9987
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_bookcity_select.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..14ed0af
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_setting.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_setting.png
new file mode 100644
index 0000000..d9018d7
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_setting.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_setting_select.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_setting_select.png
new file mode 100644
index 0000000..0296957
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_setting_select.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_shujia.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_shujia.png
new file mode 100644
index 0000000..e2a078a
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_shujia.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_shujia_select.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_shujia_select.png
new file mode 100644
index 0000000..a6707ec
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_shujia_select.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/light.png b/app/src/main/res/mipmap-xhdpi/light.png
new file mode 100644
index 0000000..a186881
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/light.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/lightbig.png b/app/src/main/res/mipmap-xhdpi/lightbig.png
new file mode 100644
index 0000000..81b8438
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/lightbig.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/setdetail.png b/app/src/main/res/mipmap-xhdpi/setdetail.png
new file mode 100644
index 0000000..fbb52b2
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/setdetail.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/t8.png b/app/src/main/res/mipmap-xhdpi/t8.png
new file mode 100644
index 0000000..b229c98
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/t8.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/thumb.png b/app/src/main/res/mipmap-xhdpi/thumb.png
new file mode 100644
index 0000000..3bc3486
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/thumb.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..b0907ca
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_bookcity.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_bookcity.png
new file mode 100644
index 0000000..e702b79
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_bookcity.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_bookcity_select.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_bookcity_select.png
new file mode 100644
index 0000000..83f19f4
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_bookcity_select.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..d8ae031
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_setting.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_setting.png
new file mode 100644
index 0000000..ff4de5b
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_setting.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_setting_select.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_setting_select.png
new file mode 100644
index 0000000..5f0443d
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_setting_select.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_shujia.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_shujia.png
new file mode 100644
index 0000000..afbc97f
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_shujia.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_shujia_select.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_shujia_select.png
new file mode 100644
index 0000000..ddd5257
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_shujia_select.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..2c18de9
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_bookcity.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_bookcity.png
new file mode 100644
index 0000000..59b7e25
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_bookcity.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_bookcity_select.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_bookcity_select.png
new file mode 100644
index 0000000..7ba19d5
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_bookcity_select.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..beed3cd
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_setting.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_setting.png
new file mode 100644
index 0000000..c766be8
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_setting.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_setting_select.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_setting_select.png
new file mode 100644
index 0000000..ed8e285
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_setting_select.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_shujia.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_shujia.png
new file mode 100644
index 0000000..68a2cd5
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_shujia.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_shujia_select.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_shujia_select.png
new file mode 100644
index 0000000..ff64a99
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_shujia_select.png differ
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..d67ce8f
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,208 @@
+
+
+ #008577
+ #00574B
+ #D81B60
+
+ #FF9800
+ #9C27B0
+ #03A9F4
+ #F44336
+ #8BC34A
+ #F8F8FF
+ #000000
+ #40E0D0
+ #E0FFFF
+ #D3D3D3
+ #40E0D0
+ #339900
+ #FFA500
+ #A9A9A9
+ #87CEFA
+ #87CEEB
+ #00BFFF
+ #808080
+
+
+ #00000000
+
+ #556495ED
+
+ #f5f4f0
+
+ #001c29
+ #637079
+
+ #b5bd9a
+ #313031
+
+ #ceebce
+ #313031
+
+ #292c29
+ #393839
+ #636563
+ #e8554d
+ #d6d7d6
+
+
+ #696969
+ #808080
+
+
+ #e6dbbf
+ #141820
+
+
+ #FF000000
+ #FFFFFFFF
+ #fffff0
+ #ffffe0
+ #ffff00
+ #fffafa
+ #fffaf0
+ #fffacd
+ #fff8dc
+ #fff5ee
+ #fff0f5
+ #ffefd5
+ #ffebcd
+ #ffe4e1
+ #ffe4c4
+ #ffe4b5
+ #ffdead
+ #ffdab9
+ #ffd700
+ #ffc0cb
+ #ffb6c1
+ #ffa500
+ #ffa07a
+ #ff8c00
+ #ff7f50
+ #ff69b4
+ #ff6347
+ #ff4500
+ #ff1493
+ #ff00ff
+ #ff00ff
+ #ff0000
+ #fdf5e6
+ #fafad2
+ #faf0e6
+ #faebd7
+ #fa8072
+ #f8f8ff
+ #f5fffa
+ #f5f5f5
+ #f5f5dc
+ #f5deb3
+ #f4a460
+ #f0ffff
+ #f0fff0
+ #f0f8ff
+ #f0e68c
+ #f08080
+ #eee8aa
+ #ee82ee
+ #e9967a
+ #e6e6fa
+ #e0ffff
+ #deb887
+ #dda0dd
+ #dcdcdc
+ #dc143c
+ #db7093
+ #daa520
+ #da70d6
+ #d8bfd8
+ #d3d3d3
+ #d3d3d3
+ #d2b48c
+ #d2691e
+ #cd853f
+ #cd5c5c
+ #c71585
+ #c0c0c0
+ #bdb76b
+ #bc8f8f
+ #ba55d3
+ #b8860b
+ #b22222
+ #b0e0e6
+ #b0c4de
+ #afeeee
+ #adff2f
+ #add8e6
+ #a9a9a9
+ #a9a9a9
+ #a52a2a
+ #a0522d
+ #9932cc
+ #98fb98
+ #9400d3
+ #9370db
+ #90ee90
+ #8fbc8f
+ #8b4513
+ #8b008b
+ #8b0000
+ #8a2be2
+ #87cefa
+ #87ceeb
+ #808080
+ #808080
+ #808000
+ #800080
+ #800000
+ #7fffd4
+ #7fff00
+ #7cfc00
+ #7b68ee
+ #778899
+ #778899
+ #708090
+ #708090
+ #6b8e23
+ #6a5acd
+ #696969
+ #696969
+ #66cdaa
+ #6495ed
+ #5f9ea0
+ #556b2f
+ #4b0082
+ #48d1cc
+ #483d8b
+ #4682b4
+ #4169e1
+ #40e0d0
+ #3cb371
+ #32cd32
+ #2f4f4f
+ #2f4f4f
+ #2e8b57
+ #228b22
+ #20b2aa
+ #1e90ff
+ #191970
+ #00ffff
+ #00ffff
+ #00ff7f
+ #00ff00
+ #00fa9a
+ #00ced1
+ #00bfff
+ #008b8b
+ #008080
+ #008000
+ #006400
+ #0000ff
+ #0000cd
+ #00008b
+ #000080
+ #81d4fa
+ #1565c0
+ #0d47a1
+ #008CFF
+ #33B5E5
+
diff --git a/app/src/main/res/values/dialog_animation.xml b/app/src/main/res/values/dialog_animation.xml
new file mode 100644
index 0000000..dc94a3c
--- /dev/null
+++ b/app/src/main/res/values/dialog_animation.xml
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..e26ac4a
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+
+ 土豆看书
+ 书籍封面
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..3c74ad8
--- /dev/null
+++ b/app/src/main/res/values/styles.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/test/java/com/tdkankan/ExampleUnitTest.java b/app/src/test/java/com/tdkankan/ExampleUnitTest.java
new file mode 100644
index 0000000..a1b6ba4
--- /dev/null
+++ b/app/src/test/java/com/tdkankan/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.tdkankan;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..6a1c15e
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,29 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ google()
+ jcenter()
+
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.5.3'
+ classpath 'org.greenrobot:greendao-gradle-plugin:3.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ maven { url 'https://jitpack.io' }
+ mavenCentral()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..199d16e
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,20 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..f6b961f
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..ab5a9d8
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue Dec 08 20:30:47 CST 2020
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000..cccdd3d
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..f955316
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..f5f8a1e
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,2 @@
+include ':app'
+rootProject.name='TDkankan'