summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics2/Application.java17
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ByteArrayImageDownloader.java29
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics2/CbzComicArchive.java92
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ComicArchive.java29
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ComicFragment.java256
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ComicListFragment.java461
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ComicPager.java198
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics2/CommonActivity.java605
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics2/DatabaseHelper.java68
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics2/DirectoryPicker.java169
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics2/MainActivity.java228
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics2/NaturalOrderComparator.java188
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics2/PreferencesActivity.java156
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics2/SyncClient.java161
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ViewComicActivity.java321
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ViewPager.java38
16 files changed, 3016 insertions, 0 deletions
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/Application.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/Application.java
new file mode 100644
index 0000000..fa2b15d
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/Application.java
@@ -0,0 +1,17 @@
+package org.fox.ttcomics2;
+
+import org.acra.ACRA;
+import org.acra.ReportingInteractionMode;
+import org.acra.annotation.ReportsCrashes;
+
+@ReportsCrashes(formKey = "", mode = ReportingInteractionMode.DIALOG,
+ resDialogText = R.string.crash_dialog_text,
+ formUri = "http://tt-rss.org/acra/submit/")
+public class Application extends android.app.Application {
+ @Override
+ public final void onCreate() {
+ super.onCreate();
+ ACRA.init(this);
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ByteArrayImageDownloader.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ByteArrayImageDownloader.java
new file mode 100644
index 0000000..9b7f920
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ByteArrayImageDownloader.java
@@ -0,0 +1,29 @@
+package org.fox.ttcomics2;
+
+import android.content.Context;
+
+import com.nostra13.universalimageloader.core.download.BaseImageDownloader;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ByteArrayImageDownloader extends BaseImageDownloader {
+ private static final String SCHEME_STREAM = "stream";
+ private static final String STREAM_URI_PREFIX = SCHEME_STREAM + "://";
+
+ public ByteArrayImageDownloader(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected InputStream getStreamFromOtherSource(String imageUri, Object extra) throws IOException {
+ if (imageUri.startsWith(STREAM_URI_PREFIX)) {
+ InputStream is = new ByteArrayInputStream((byte[])extra);
+
+ return is;
+ } else {
+ return super.getStreamFromOtherSource(imageUri, extra);
+ }
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/CbzComicArchive.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/CbzComicArchive.java
new file mode 100644
index 0000000..e533ff3
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/CbzComicArchive.java
@@ -0,0 +1,92 @@
+package org.fox.ttcomics2;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+public class CbzComicArchive extends ComicArchive {
+ private final String TAG = this.getClass().getSimpleName();
+
+ private String m_fileName;
+ private ZipFile m_zipFile;
+ private int m_count;
+ private ArrayList<ZipEntry> m_entries = new ArrayList<ZipEntry>();
+
+ @Override
+ public int getCount() {
+ return m_count;
+ }
+
+ @Override
+ public InputStream getItem(int index) throws IOException {
+ return m_zipFile.getInputStream(m_entries.get(index));
+ }
+
+ protected void initialize() throws IOException {
+ m_zipFile = new ZipFile(m_fileName);
+
+ Enumeration<? extends ZipEntry> e = m_zipFile.entries();
+
+ while (e.hasMoreElements()) {
+ ZipEntry ze = e.nextElement();
+ if (!ze.isDirectory() && isValidComic(ze.getName())) {
+ m_entries.add(ze);
+ m_count++;
+ }
+ }
+
+ Collections.sort(m_entries, new NaturalOrderComparator());
+ }
+
+ public CbzComicArchive(String fileName) throws IOException {
+ m_fileName = fileName;
+
+ initialize();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(m_fileName);
+ }
+
+ public void readFromParcel(Parcel in) {
+ m_fileName = in.readString();
+ }
+
+ public CbzComicArchive(Parcel in) throws IOException {
+ readFromParcel(in);
+
+ initialize();
+ }
+
+ @SuppressWarnings("rawtypes")
+ public static final Parcelable.Creator CREATOR =
+ new Parcelable.Creator() {
+ public CbzComicArchive createFromParcel(Parcel in) {
+ try {
+ return new CbzComicArchive(in);
+ } catch (IOException e) {
+ e.printStackTrace();
+
+ return null;
+ }
+ }
+
+ public CbzComicArchive[] newArray(int size) {
+ return new CbzComicArchive[size];
+ }
+ };
+
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ComicArchive.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ComicArchive.java
new file mode 100644
index 0000000..f85263c
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ComicArchive.java
@@ -0,0 +1,29 @@
+package org.fox.ttcomics2;
+
+import android.os.Parcelable;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public abstract class ComicArchive implements Parcelable {
+ public abstract int getCount();
+ public abstract InputStream getItem(int index) throws IOException;
+ public boolean isValidComic(String fileName) {
+ return fileName.toLowerCase().matches(".*\\.(jpe?g|bmp|gif|png)$");
+ }
+ public byte[] getByteArray(int m_page) throws IOException {
+ InputStream is = getItem(m_page);
+
+ int size = 16384;
+ byte[] buf = new byte[size];
+ int len = 0;
+
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ buf = new byte[size];
+ while ((len = is.read(buf, 0, size)) != -1)
+ bos.write(buf, 0, len);
+
+ return bos.toByteArray();
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ComicFragment.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ComicFragment.java
new file mode 100644
index 0000000..64a88a5
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ComicFragment.java
@@ -0,0 +1,256 @@
+package org.fox.ttcomics2;
+
+
+import android.app.Activity;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.support.v4.app.Fragment;
+import android.support.v7.app.ActionBar;
+import android.view.GestureDetector;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.nostra13.universalimageloader.core.DisplayImageOptions;
+import com.nostra13.universalimageloader.core.ImageLoader;
+
+import java.io.IOException;
+
+import it.sephiroth.android.library.imagezoom.ImageViewTouch;
+
+public class ComicFragment extends Fragment implements GestureDetector.OnDoubleTapListener {
+ private final String TAG = this.getClass().getSimpleName();
+
+ private SharedPreferences m_prefs;
+ private int m_page;
+ private ViewComicActivity m_activity;
+ private GestureDetector m_detector;
+ private boolean m_thumbnail = false;
+ private ComicArchive m_archive;
+
+ public ComicFragment() {
+ super();
+ }
+
+ public void initialize(ComicArchive archive, int page) {
+ m_archive = archive;
+ m_page = page;
+ }
+
+ public void setPage(int page) {
+ m_page = page;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+
+ View view = inflater.inflate(R.layout.fragment_comic, container, false);
+
+ final ImageViewTouch image = (ImageViewTouch) view.findViewById(R.id.comic_image);
+
+ if (savedInstanceState != null) {
+ m_page = savedInstanceState.getInt("page");
+ m_thumbnail = savedInstanceState.getBoolean("thumbnail");
+ m_archive = savedInstanceState.getParcelable("archive");
+ }
+
+ image.setFitToScreen(true);
+
+ ImageLoader imageLoader = ImageLoader.getInstance();
+
+ if (m_prefs.getBoolean("fit_to_width", false)) {
+ image.setFitToWidth(true);
+ }
+
+ try {
+ byte[] buf = m_archive.getByteArray(m_page);
+
+ DisplayImageOptions options = new DisplayImageOptions.Builder()
+ .showImageOnFail(R.drawable.badimage)
+ .extraForDownloader(buf)
+ .build();
+
+ imageLoader.displayImage("stream://" + m_page, image, options);
+
+ } catch (IOException e) {
+ image.setImageResource(R.drawable.badimage);
+ e.printStackTrace();
+ }
+
+ image.setOnScaleChangedListener(new ImageViewTouch.OnScaleChangedListener() {
+ @Override
+ public void onScaleChanged(float scale) {
+ // TODO: shared scale change?
+ }
+ });
+
+ image.setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View view, MotionEvent event) {
+ return m_detector.onTouchEvent(event);
+ }
+ });
+
+ //}
+
+ /* TextView page = (TextView) view.findViewById(R.id.comic_page);
+
+ if (page != null) {
+ page.setText(String.valueOf(m_page+1));
+ } */
+
+ return view;
+
+ }
+
+ private void onLeftSideTapped() {
+ ImageViewTouch image = (ImageViewTouch) getView().findViewById(R.id.comic_image);
+
+ if (image != null) {
+ boolean atLeftEdge = !image.canScroll(1);
+
+ if (atLeftEdge) {
+ m_activity.selectPreviousComic();
+ }
+ }
+ }
+
+ public boolean canScroll(int direction) {
+ ImageViewTouch image = (ImageViewTouch) getView().findViewById(R.id.comic_image);
+
+ if (image != null) {
+ return image.canScroll(direction);
+ } else {
+ return false;
+ }
+ }
+
+ private void onRightSideTapped() {
+ ImageViewTouch image = (ImageViewTouch) getView().findViewById(R.id.comic_image);
+
+ if (image != null) {
+ boolean atRightEdge = !image.canScroll(-1);
+
+ if (atRightEdge) {
+ m_activity.selectNextComic();
+ }
+ }
+ }
+
+ @Override
+ public void setUserVisibleHint(boolean isVisibleToUser) {
+ super.setUserVisibleHint(isVisibleToUser);
+
+ //setThumbnail(!isVisibleToUser); disabled
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+
+ m_prefs = PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext());
+ m_activity = (ViewComicActivity) activity;
+
+ m_detector = new GestureDetector(m_activity, new GestureDetector.OnGestureListener() {
+
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public void onShowPress(MotionEvent e) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
+ float distanceY) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public void onLongPress(MotionEvent e) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+ float velocityY) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean onDown(MotionEvent e) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+ });
+
+ m_detector.setOnDoubleTapListener(this);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle out) {
+ super.onSaveInstanceState(out);
+ out.putInt("page", m_page);
+ out.putBoolean("thumbnail", m_thumbnail);
+ out.putParcelable("archive", m_archive);
+ }
+
+ @Override
+ public boolean onDoubleTap(MotionEvent e) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean onDoubleTapEvent(MotionEvent e) {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ @Override
+ public boolean onSingleTapConfirmed(MotionEvent e) {
+
+ int width = getView().getWidth();
+
+ int x = Math.round(e.getX());
+
+ if (x <= width/10) {
+ onLeftSideTapped();
+ } else if (x >= width-(width/10)) {
+ onRightSideTapped();
+ } else {
+ ActionBar bar = m_activity.getSupportActionBar();
+
+ if (bar.isShowing()) {
+ bar.hide();
+ m_activity.hideSeekBar(true);
+
+ if (m_prefs.getBoolean("use_full_screen", false)) {
+ m_activity.hideSystemUI(true);
+ }
+
+ } else {
+ m_activity.hideSeekBar(false);
+
+ if (m_prefs.getBoolean("use_full_screen", false)) {
+ m_activity.hideSystemUI(false);
+ }
+
+ bar.show();
+ }
+ }
+
+ return false;
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ComicListFragment.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ComicListFragment.java
new file mode 100644
index 0000000..9b766f7
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ComicListFragment.java
@@ -0,0 +1,461 @@
+package org.fox.ttcomics2;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.provider.BaseColumns;
+import android.support.v4.app.Fragment;
+import android.support.v4.widget.SimpleCursorAdapter;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.GridView;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.nostra13.universalimageloader.core.ImageLoader;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ComicListFragment extends Fragment implements OnItemClickListener {
+ private final String TAG = this.getClass().getSimpleName();
+
+ private final static int SIZE_DIR = -100;
+
+ // corresponds to tab indexes
+ private final static int MODE_ALL = 0;
+ private final static int MODE_UNREAD = 1;
+ private final static int MODE_UNFINISHED = 2;
+ private final static int MODE_READ = 3;
+
+ private CommonActivity m_activity;
+ private SharedPreferences m_prefs;
+ private ComicsListAdapter m_adapter;
+ private int m_mode = 0;
+ private String m_baseDirectory = "";
+ private SwipeRefreshLayout m_swipeLayout;
+
+ public ComicListFragment() {
+ super();
+ }
+
+ public void setBaseDirectory(String baseDirectory) {
+ m_baseDirectory = baseDirectory;
+ }
+
+ public void setMode(int mode) {
+ m_mode = mode;
+ }
+
+ private class ComicsListAdapter extends SimpleCursorAdapter {
+ public ComicsListAdapter(Context context, int layout, Cursor c,
+ String[] from, int[] to, int flags) {
+ super(context, layout, c, from, to, flags);
+ // TODO Auto-generated constructor stub
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View v = convertView;
+
+ Cursor c = (Cursor) getItem(position);
+
+ String filePath = c.getString(c.getColumnIndex("path"));
+ String fileBaseName = c.getString(c.getColumnIndex("filename"));
+ String firstChild = c.getString(c.getColumnIndex("firstchild"));
+
+ int lastPos = m_activity.getLastPosition(filePath + "/" + fileBaseName);
+ int size = m_activity.getSize(filePath + "/" + fileBaseName);
+
+ if (v == null) {
+ LayoutInflater vi = (LayoutInflater)getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ v = vi.inflate(R.layout.comics_grid_row, null);
+ }
+
+ TextView name = (TextView) v.findViewById(R.id.file_name);
+
+ if (name != null) {
+ name.setText(fileBaseName);
+ }
+
+ TextView info = (TextView) v.findViewById(R.id.file_progress_info);
+
+ if (info != null) {
+ if (size != -1 && size != SIZE_DIR) {
+ if (lastPos == size - 1) {
+ info.setText(getString(R.string.file_finished));
+ } else if (lastPos > 0) {
+ info.setText(getString(R.string.file_progress_info, lastPos + 1, size, (int) (lastPos / (float) size * 100f)));
+ } else {
+ info.setText(getString(R.string.file_unread, size));
+ }
+ info.setVisibility(View.VISIBLE);
+ } else if (size == SIZE_DIR) {
+ info.setText(getString(R.string.list_type_directory));
+ info.setVisibility(View.VISIBLE);
+ } else {
+ info.setText(getString(R.string.list_type_unknown));
+ info.setVisibility(View.VISIBLE);
+ }
+ }
+
+ ProgressBar progressBar = (ProgressBar) v.findViewById(R.id.file_progress_bar);
+
+ if (progressBar != null) {
+ if (size != -1 && size != SIZE_DIR) {
+ progressBar.setMax(size - 1);
+ progressBar.setProgress(lastPos);
+ progressBar.setEnabled(true);
+ progressBar.setVisibility(View.VISIBLE);
+ } else {
+ progressBar.setProgress(0);
+ progressBar.setMax(0);
+ progressBar.setEnabled(false);
+ progressBar.setVisibility(View.VISIBLE);
+ }
+ }
+
+ /* ImageView overflow = (ImageView) v.findViewById(R.id.comic_overflow);
+
+ if (overflow != null) {
+ if (size == SIZE_DIR) {
+ overflow.setVisibility(View.GONE);
+ } else {
+ overflow.setOnClickListener(new View.OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ getActivity().openContextMenu(v);
+ }
+ });
+ }
+ } */
+
+ File thumbnailFile = new File(m_activity.getCacheFileName(firstChild != null ? firstChild : filePath + "/" + fileBaseName));
+
+ ImageView thumbnail = (ImageView) v.findViewById(R.id.thumbnail);
+
+ if (thumbnail != null && thumbnailFile != null) {
+
+ ImageLoader imageLoader = ImageLoader.getInstance();
+ imageLoader.displayImage("file://" + thumbnailFile.getAbsolutePath(), thumbnail);
+ }
+
+ return v;
+ }
+ }
+
+ public int dpToPx(int dp) {
+ DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
+ int px = Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
+ return px;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+
+ View view = inflater.inflate(R.layout.fragment_comics_list, container, false);
+
+ if (savedInstanceState != null) {
+ m_mode = savedInstanceState.getInt("mode");
+ m_baseDirectory = savedInstanceState.getString("baseDir");
+ }
+
+ m_swipeLayout = (SwipeRefreshLayout) view.findViewById(R.id.comics_swipe_container);
+
+ m_swipeLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
+ @Override
+ public void onRefresh() {
+ rescan(true);
+ }
+ });
+
+ m_swipeLayout.setColorScheme(android.R.color.holo_green_dark,
+ android.R.color.holo_red_dark,
+ android.R.color.holo_blue_dark,
+ android.R.color.holo_orange_dark);
+
+ m_adapter = new ComicsListAdapter(getActivity(), R.layout.comics_grid_row, createCursor(),
+ new String[] { "filename" }, new int[] { R.id.file_name }, 0);
+
+ if (view.findViewById(R.id.comics_list) != null) {
+ ListView list = (ListView) view.findViewById(R.id.comics_list);
+ list.setAdapter(m_adapter);
+ list.setEmptyView(view.findViewById(R.id.no_comics));
+ list.setOnItemClickListener(this);
+
+ registerForContextMenu(list);
+
+ } else {
+ GridView grid = (GridView) view.findViewById(R.id.comics_grid);
+ grid.setAdapter(m_adapter);
+ grid.setEmptyView(view.findViewById(R.id.no_comics));
+ grid.setOnItemClickListener(this);
+
+ registerForContextMenu(grid);
+
+ }
+
+ return view;
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenuInfo menuInfo) {
+
+ getActivity().getMenuInflater().inflate(R.menu.comic_archive_context, menu);
+
+ AdapterContextMenuInfo info = (AdapterContextMenuInfo)menuInfo;
+
+ Cursor c = (Cursor) m_adapter.getItem(info.position);
+
+ if (c != null) {
+ menu.setHeaderTitle(c.getString(c.getColumnIndex("filename")));
+ }
+
+ super.onCreateContextMenu(menu, v, menuInfo);
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ AdapterContextMenuInfo info = (AdapterContextMenuInfo) item
+ .getMenuInfo();
+
+ Cursor c = (Cursor) m_adapter.getItem(info.position);
+ String fileName = c.getString(c.getColumnIndex("path")) + "/" + c.getString(c.getColumnIndex("filename"));
+
+ switch (item.getItemId()) {
+ case R.id.menu_reset_progress:
+ if (fileName != null) {
+ m_activity.resetProgress(fileName);
+ m_adapter.notifyDataSetChanged();
+ }
+ return true;
+ case R.id.menu_mark_as_read:
+
+ if (fileName != null) {
+ m_activity.setLastPosition(fileName, m_activity.getSize(fileName)-1);
+ m_adapter.notifyDataSetChanged();
+ }
+
+ return true;
+ default:
+ Log.d(TAG, "onContextItemSelected, unhandled id=" + item.getItemId());
+ return super.onContextItemSelected(item);
+ }
+ }
+
+ private Cursor createCursor() {
+ String baseDir = m_baseDirectory.length() == 0 ? m_prefs.getString("comics_directory", "") : m_baseDirectory;
+
+ String selection;
+ String selectionArgs[];
+
+ switch (m_mode) {
+ case MODE_READ:
+ selection = "path = ? AND position = size - 1";
+ selectionArgs = new String[] { baseDir };
+ break;
+ case MODE_UNFINISHED:
+ selection = "path = ? AND position < size AND position > 0 AND position != size - 1";
+ selectionArgs = new String[] { baseDir };
+ break;
+ case MODE_UNREAD:
+ selection = "path = ? AND position = 0 AND size != ?";
+ selectionArgs = new String[] { baseDir, String.valueOf(SIZE_DIR) };
+ break;
+ default:
+ selection = "path = ?";
+ selectionArgs = new String[] { baseDir };
+ }
+
+ if (!m_prefs.getBoolean("enable_rar", false)) {
+ selection += " AND (UPPER(filename) NOT LIKE '%.CBR' AND UPPER(filename) NOT LIKE '%.RAR')";
+ }
+
+ return m_activity.getReadableDb().query("comics_cache", new String[] { BaseColumns._ID, "filename", "path",
+ "(SELECT path || '/' || filename FROM comics_cache AS t2 WHERE t2.path = comics_cache.path || '/' || comics_cache.filename AND filename != '' ORDER BY filename LIMIT 1) AS firstchild" },
+ selection, selectionArgs, null, null, "size != " + SIZE_DIR + ", filename, size = " + SIZE_DIR + ", filename");
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+
+ m_activity = (CommonActivity)activity;
+
+ m_prefs = PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext());
+ }
+
+ protected void rescan(final boolean fullRescan) {
+ AsyncTask<String, Integer, Integer> rescanTask = new AsyncTask<String, Integer, Integer>() {
+
+ @Override
+ protected void onProgressUpdate(Integer... progress) {
+ if (isAdded()) {
+ m_activity.setProgress(Math.round(((float)progress[0] / (float)progress[1]) * 10000));
+ }
+ }
+
+ @Override
+ protected Integer doInBackground(String... params) {
+ String comicsDir = params[0];
+
+ File dir = new File(comicsDir);
+
+ int fileIndex = 0;
+
+ if (dir.isDirectory()) {
+ File archives[] = dir.listFiles();
+
+ java.util.Arrays.sort(archives);
+
+ for (File archive : archives) {
+ String filePath = archive.getAbsolutePath();
+
+ if (archive.isDirectory()) {
+ m_activity.setSize(filePath, SIZE_DIR);
+
+ } else if (archive.getName().toLowerCase().matches(".*\\.(cbz|zip|cbr|rar)") && isAdded() && m_activity != null &&
+ m_activity.getWritableDb() != null && m_activity.getWritableDb().isOpen()) {
+ try {
+ int size = m_activity.getSize(filePath);
+
+ if (size == -1 || fullRescan) {
+
+ ComicArchive cba = null;
+
+ if (archive.getName().toLowerCase().matches(".*\\.(cbz|zip)")) {
+ cba = new CbzComicArchive(filePath);
+ }
+
+ if (cba != null && cba.getCount() > 0) {
+ // Get cover
+
+ try {
+ File thumbnailFile = new File(m_activity.getCacheFileName(filePath));
+
+ if (m_activity.isStorageWritable() && (!thumbnailFile.exists() || fullRescan)) {
+ InputStream is = cba.getItem(0);
+
+ if (is != null) {
+ FileOutputStream fos = new FileOutputStream(thumbnailFile);
+
+ byte[] buffer = new byte[1024];
+ int len;
+ while ((len = is.read(buffer)) != -1) {
+ fos.write(buffer, 0, len);
+ }
+
+ fos.close();
+ is.close();
+ }
+ }
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ size = cba.getCount();
+
+ m_activity.setSize(filePath, size);
+ }
+ }
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+
+ ++fileIndex;
+
+ publishProgress(Integer.valueOf(fileIndex), Integer.valueOf(archives.length));
+ }
+ }
+
+ if (isAdded() && m_activity != null) {
+ m_activity.cleanupCache(false);
+ m_activity.cleanupSqliteCache(comicsDir);
+ }
+
+ return fileIndex; //m_files.size();
+ }
+
+ @Override
+ protected void onPostExecute(Integer result) {
+ if (isAdded() && m_adapter != null) {
+ m_adapter.changeCursor(createCursor());
+ m_adapter.notifyDataSetChanged();
+
+ if (m_swipeLayout != null) m_swipeLayout.setRefreshing(false);
+ }
+ }
+ };
+
+ String comicsDir = m_prefs.getString("comics_directory", null);
+
+ if (comicsDir != null && m_activity.isStorageAvailable()) {
+ if (m_swipeLayout != null) m_swipeLayout.setRefreshing(true);
+
+ rescanTask.execute(m_baseDirectory.length() > 0 ? m_baseDirectory : comicsDir);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ m_adapter.notifyDataSetChanged();
+
+ String comicsDir = m_prefs.getString("comics_directory", "");
+
+ if (m_activity.getCachedItemCount(m_baseDirectory.length() > 0 ? m_baseDirectory : comicsDir) == 0) {
+ rescan(false);
+ } else {
+ m_adapter.notifyDataSetChanged();
+ }
+ }
+
+ public void onItemClick(AdapterView<?> av, View view, int position, long id) {
+ //Log.d(TAG, "onItemClick position=" + position);
+
+ Cursor c = (Cursor) m_adapter.getItem(position);
+ String fileName = c.getString(c.getColumnIndex("path")) + "/" + c.getString(c.getColumnIndex("filename"));
+
+ if (fileName != null) {
+ m_activity.onComicArchiveSelected(fileName);
+ }
+ }
+
+
+ @Override
+ public void onSaveInstanceState(Bundle out) {
+ super.onSaveInstanceState(out);
+
+ out.putInt("mode", m_mode);
+ out.putString("baseDir", m_baseDirectory);
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ComicPager.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ComicPager.java
new file mode 100644
index 0000000..8df3cf0
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ComicPager.java
@@ -0,0 +1,198 @@
+package org.fox.ttcomics2;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentStatePagerAdapter;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import java.io.IOException;
+
+public class ComicPager extends Fragment {
+ private String m_fileName;
+ private SharedPreferences m_prefs;
+ private final String TAG = this.getClass().getSimpleName();
+ private ComicArchive m_archive;
+ private CommonActivity m_activity;
+ private SeekBar m_seekBar;
+ private TextView m_pageView;
+
+ public void hideSeekBar(boolean hide) {
+ m_seekBar.setVisibility(hide ? View.GONE : View.VISIBLE);
+ m_pageView.setVisibility(hide ? View.GONE : View.VISIBLE);
+ }
+
+ private class PagerAdapter extends FragmentStatePagerAdapter {
+ public PagerAdapter(FragmentManager fm) {
+ super(fm);
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ ComicFragment cf = new ComicFragment();
+ cf.initialize(m_archive, position);
+
+ return cf;
+ }
+
+ @Override
+ public int getCount() {
+ return m_archive.getCount();
+ }
+ }
+
+ private PagerAdapter m_adapter;
+
+ public ComicPager() {
+ super();
+ }
+
+ public ComicArchive getArchive() {
+ return m_archive;
+ }
+
+ public int getCount() {
+ return m_adapter.getCount();
+ }
+
+ public int getPosition() {
+ ViewPager pager = (ViewPager) getView().findViewById(R.id.comics_pager);
+
+ if (pager != null) {
+ return pager.getCurrentItem();
+ }
+
+ return 0;
+ }
+
+ public String getFileName() {
+ return m_fileName;
+ }
+
+ public void setCurrentItem(int item) {
+ ViewPager pager = (ViewPager) getView().findViewById(R.id.comics_pager);
+
+ if (pager != null) {
+ try {
+ pager.setCurrentItem(item);
+ m_pageView.setText(String.valueOf(item+1));
+ } catch (IndexOutOfBoundsException e) {
+ e.printStackTrace();
+ }
+ }
+
+ }
+
+ public void setFileName(String fileName) {
+ m_fileName = fileName;
+ }
+
+ @SuppressLint("NewApi")
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+
+ final View view = inflater.inflate(R.layout.fragment_comics_pager, container, false);
+
+ m_adapter = new PagerAdapter(getActivity().getSupportFragmentManager());
+
+ final ViewPager pager = (ViewPager) view.findViewById(R.id.comics_pager);
+
+ if (savedInstanceState != null) {
+ m_fileName = savedInstanceState.getString("fileName");
+ }
+
+ try {
+ if (m_fileName.toLowerCase().matches(".*\\.(cbz|zip)")) {
+ m_archive = new CbzComicArchive(m_fileName);
+ }
+
+ final int position = m_activity.getLastPosition(m_fileName);
+
+ m_pageView = (TextView) view.findViewById(R.id.comics_page);
+ m_pageView.setText(String.valueOf(position+1));
+
+ m_seekBar = (SeekBar) view.findViewById(R.id.comics_seek_bar);
+ m_seekBar.setMax(m_archive.getCount()-1);
+ m_seekBar.setProgress(position);
+ m_seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (fromUser) {
+ setCurrentItem(progress);
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+
+ }
+ });
+
+ pager.setAdapter(m_adapter);
+ pager.setCurrentItem(position);
+
+ m_activity.onComicSelected(m_fileName, position);
+
+ if (m_prefs.getBoolean("use_full_screen", false)) {
+ m_activity.hideSystemUI(true);
+ hideSeekBar(true);
+ }
+
+ } catch (IOException e) {
+ m_activity.toast(R.string.error_could_not_open_comic_archive);
+ e.printStackTrace();
+ }
+
+ pager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
+ public void onPageSelected(int position) {
+
+ m_activity.onComicSelected(m_fileName, position);
+ m_seekBar.setProgress(position);
+
+ if (m_prefs.getBoolean("use_full_screen", false)) {
+ m_activity.hideSystemUI(true);
+ }
+ }
+
+ public void onPageScrolled(int arg0, float arg1, int arg2) {
+ // TODO Auto-generated method stub
+ }
+
+ public void onPageScrollStateChanged(int arg0) {
+ // TODO Auto-generated method stub
+ }
+ });
+
+ return view;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+
+ m_activity = (CommonActivity) activity;
+
+ m_prefs = PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext());
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle out) {
+ super.onSaveInstanceState(out);
+
+ out.putString("fileName", m_fileName);
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/CommonActivity.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/CommonActivity.java
new file mode 100644
index 0000000..2095d87
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/CommonActivity.java
@@ -0,0 +1,605 @@
+package org.fox.ttcomics2;
+
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteStatement;
+import android.graphics.BitmapFactory;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.preference.PreferenceManager;
+import android.provider.BaseColumns;
+import android.support.v7.app.ActionBarActivity;
+import android.util.Log;
+import android.view.Display;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Toast;
+
+import com.nostra13.universalimageloader.core.DisplayImageOptions;
+import com.nostra13.universalimageloader.core.ImageLoader;
+import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
+import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer;
+
+import java.io.File;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Date;
+
+public class CommonActivity extends ActionBarActivity {
+ private final String TAG = this.getClass().getSimpleName();
+
+ protected static final String FRAG_COMICS_PAGER = "comic_pager";
+ protected static final String FRAG_COMICS_LIST = "comics_list";
+
+ protected final static int REQUEST_SHARE = 1;
+ protected static final int REQUEST_VIEWCOMIC = 2;
+
+ protected SharedPreferences m_prefs;
+ protected SyncClient m_syncClient = new SyncClient();
+
+ private boolean m_smallScreenMode = true;
+
+ private boolean m_storageAvailable;
+ private boolean m_storageWritable;
+
+ private SQLiteDatabase m_readableDb;
+ private SQLiteDatabase m_writableDb;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (!ImageLoader.getInstance().isInited()) {
+
+ DisplayImageOptions options = new DisplayImageOptions.Builder()
+ .cacheInMemory(true)
+ .resetViewBeforeLoading(true)
+ .cacheOnDisk(false)
+ .displayer(new FadeInBitmapDisplayer(250))
+ .build();
+
+ ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(this)
+ .defaultDisplayImageOptions(options)
+ .imageDownloader(new ByteArrayImageDownloader(this))
+ .build();
+
+ ImageLoader.getInstance().init(config);
+ }
+
+ m_prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+
+ initDatabase();
+ initSync();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ String state = Environment.getExternalStorageState();
+
+ if (Environment.MEDIA_MOUNTED.equals(state)) {
+ m_storageAvailable = true;
+ m_storageWritable = true;
+ } else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
+ m_storageAvailable = true;
+ m_storageWritable = false;
+ } else {
+ m_storageAvailable = false;
+ m_storageWritable = false;
+ }
+
+ initSync();
+ }
+
+ private void initSync() {
+ if (m_prefs.getBoolean("use_position_sync", false)) {
+ String googleAccount = getGoogleAccount();
+
+ if (googleAccount != null) {
+ m_syncClient.setOwner(googleAccount);
+ } else {
+ if (Build.HARDWARE.contains("goldfish")) {
+ m_syncClient.setOwner("TEST-ACCOUNT");
+
+ //toast(R.string.sync_running_in_test_mode);
+ } else {
+ m_syncClient.setOwner(null);
+ toast(R.string.error_sync_no_account);
+
+ SharedPreferences.Editor editor = m_prefs.edit();
+ editor.putBoolean("use_position_sync", false);
+ editor.commit();
+ }
+ }
+ } else {
+ m_syncClient.setOwner(null);
+ }
+ }
+
+ protected void setSmallScreen(boolean smallScreen) {
+ Log.d(TAG, "m_smallScreenMode=" + smallScreen);
+ m_smallScreenMode = smallScreen;
+ }
+
+ public boolean isSmallScreen() {
+ return m_smallScreenMode;
+ }
+
+
+ public void onComicArchiveSelected(String fileName) {
+ //
+ }
+
+ public Cursor findComicByFileName(String fileName) {
+ File file = new File(fileName);
+
+ Cursor c = getReadableDb().query("comics_cache", null,
+ "filename = ? AND path = ?",
+ new String[] { file.getName(), file.getParentFile().getAbsolutePath() }, null, null, null);
+
+ if (c.moveToFirst()) {
+ return c;
+ } else {
+ c.close();
+
+ SQLiteStatement stmt = getWritableDb().compileStatement("INSERT INTO comics_cache " +
+ "(filename, path, position, max_position, size, checksum) VALUES (?, ?, 0, 0, -1, '')");
+ stmt.bindString(1, file.getName());
+ stmt.bindString(2, file.getParentFile().getAbsolutePath());
+ stmt.execute();
+
+ c = getReadableDb().query("comics_cache", null,
+ "filename = ? AND path = ?",
+ new String[] { file.getName(), file.getParentFile().getAbsolutePath() }, null, null, null);
+
+ if (c.moveToFirst()) {
+ return c;
+ } else {
+ c.close();
+ }
+ }
+
+ return null;
+ }
+
+ public void setSize(String fileName, int size) {
+ Cursor c = findComicByFileName(fileName);
+
+ if (c != null) {
+ c.close();
+
+ File file = new File(fileName);
+
+ SQLiteStatement stmt = getWritableDb().compileStatement("UPDATE comics_cache SET size = ? WHERE filename = ? AND path = ?");
+ stmt.bindLong(1, size);
+ stmt.bindString(2, file.getName());
+ stmt.bindString(3, file.getParentFile().getAbsolutePath());
+ stmt.execute();
+ stmt.close();
+ }
+ }
+
+ public void setChecksum(String fileName, String checksum) {
+ Cursor c = findComicByFileName(fileName);
+
+ if (c != null) {
+ c.close();
+
+ File file = new File(fileName);
+
+ SQLiteStatement stmt = getWritableDb().compileStatement("UPDATE comics_cache SET checksum = ? WHERE filename = ? AND path = ?");
+ stmt.bindString(1, checksum);
+ stmt.bindString(2, file.getName());
+ stmt.bindString(3, file.getParentFile().getAbsolutePath());
+ stmt.execute();
+ stmt.close();
+ }
+ }
+
+ public void resetProgress(final String fileName) {
+
+ if (m_prefs.getBoolean("use_position_sync", false) && m_syncClient.hasOwner()) {
+ setLastPosition(fileName, 0);
+ setLastMaxPosition(fileName, 0);
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(CommonActivity.this);
+ builder.setMessage(R.string.reset_remove_synced_progress)
+ .setCancelable(true)
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ m_syncClient.clearData(sha1(new File(fileName).getName()));
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.cancel();
+ }
+ });
+ AlertDialog alert = builder.create();
+ alert.show();
+
+ }
+
+ }
+
+ public void setLastPosition(String fileName, int position) {
+ int lastPosition = getLastPosition(fileName);
+
+ Cursor c = findComicByFileName(fileName);
+
+ if (c != null) {
+ c.close();
+
+ File file = new File(fileName);
+
+ SQLiteStatement stmt = getWritableDb().compileStatement("UPDATE comics_cache SET position = ?, max_position = ? WHERE filename = ? AND path = ?");
+ stmt.bindLong(1, position);
+ stmt.bindLong(2, Math.max(position, lastPosition));
+ stmt.bindString(3, file.getName());
+ stmt.bindString(4, file.getParentFile().getAbsolutePath());
+ stmt.execute();
+ stmt.close();
+ }
+
+ }
+
+ public void setLastMaxPosition(String fileName, int position) {
+
+ Cursor c = findComicByFileName(fileName);
+
+ if (c != null) {
+ c.close();
+
+ File file = new File(fileName);
+
+ SQLiteStatement stmt = getWritableDb().compileStatement("UPDATE comics_cache SET max_position = ? WHERE filename = ? AND path = ?");
+ stmt.bindLong(1, position);
+ stmt.bindString(2, file.getName());
+ stmt.bindString(3, file.getParentFile().getAbsolutePath());
+ stmt.execute();
+ stmt.close();
+ }
+ }
+
+ public String getChecksum(String fileName) {
+ String checksum = null;
+
+ File file = new File(fileName);
+
+ Cursor c = getReadableDb().query("comics_cache", new String[] { "checksum" },
+ "filename = ? AND path = ?",
+ new String[] { file.getName(), file.getParentFile().getAbsolutePath() }, null, null, null);
+
+ if (c.moveToFirst()) {
+ checksum = c.getString(c.getColumnIndex("checksum"));
+ }
+
+ c.close();
+
+ return checksum;
+
+ }
+
+
+ public int getLastPosition(String fileName) {
+ int position = 0;
+
+ File file = new File(fileName);
+
+ Cursor c = getReadableDb().query("comics_cache", new String[] { "position" },
+ "filename = ? AND path = ?",
+ new String[] { file.getName(), file.getParentFile().getAbsolutePath() }, null, null, null);
+
+ if (c.moveToFirst()) {
+ position = c.getInt(c.getColumnIndex("position"));
+ }
+
+ c.close();
+
+ return position;
+
+ }
+
+ public int getCachedItemCount(String baseDir) {
+ Cursor c = getReadableDb().query("comics_cache", new String[] { "COUNT(*)" },
+ "path = ?",
+ new String[] { baseDir }, null, null, null);
+
+ c.moveToFirst();
+ int count = c.getInt(0);
+ c.close();
+
+ //Log.d(TAG, "getCachedItemCount:" + baseDir + "=" + count);
+
+ return count;
+ }
+
+ public int getMaxPosition(String fileName) {
+ int position = 0;
+
+ File file = new File(fileName);
+
+ Cursor c = getReadableDb().query("comics_cache", new String[] { "max_position" },
+ "filename = ? AND path = ?",
+ new String[] { file.getName(), file.getParentFile().getAbsolutePath() }, null, null, null);
+
+ if (c.moveToFirst()) {
+ position = c.getInt(c.getColumnIndex("max_position"));
+ }
+
+ c.close();
+
+ return position;
+ }
+
+ public int getSize(String fileName) {
+ int size = -1;
+
+ File file = new File(fileName);
+
+ Cursor c = getReadableDb().query("comics_cache", new String[] { "size" },
+ "filename = ? AND path = ?",
+ new String[] { file.getName(), file.getParentFile().getAbsolutePath() }, null, null, null);
+
+ if (c.moveToFirst()) {
+ size = c.getInt(c.getColumnIndex("size"));
+ }
+
+ c.close();
+
+ //Log.d(TAG, "getSize:" + fileName + "=" + size);
+
+ return size;
+ }
+
+ public void onComicSelected(String fileName, int position) {
+ setLastPosition(fileName, position);
+ }
+
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_rescan:
+ ComicListFragment frag = (ComicListFragment) getSupportFragmentManager().findFragmentByTag(FRAG_COMICS_LIST);
+
+ if (frag != null && frag.isAdded()) {
+ frag.rescan(true);
+ }
+
+ return true;
+ case R.id.menu_settings:
+ Intent intent = new Intent(CommonActivity.this,
+ PreferencesActivity.class);
+ startActivityForResult(intent, 0);
+ return true;
+ default:
+ Log.d(TAG,
+ "onOptionsItemSelected, unhandled id=" + item.getItemId());
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ public boolean isPortrait() {
+ Display display = getWindowManager().getDefaultDisplay();
+
+ int width = display.getWidth();
+ int height = display.getHeight();
+
+ return width < height;
+ }
+
+ protected static String md5(String s) {
+ try {
+ MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
+ digest.update(s.getBytes());
+ byte messageDigest[] = digest.digest();
+
+ StringBuffer hexString = new StringBuffer();
+ for (int i=0; i<messageDigest.length; i++)
+ hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
+
+ return hexString.toString();
+
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ protected static String sha1(String s) {
+ if (s != null) {
+ try {
+ MessageDigest digest = java.security.MessageDigest.getInstance("SHA1");
+ digest.update(s.getBytes());
+ byte messageDigest[] = digest.digest();
+
+ StringBuffer hexString = new StringBuffer();
+ for (int i=0; i<messageDigest.length; i++)
+ hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
+
+ return hexString.toString();
+
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ }
+ }
+
+ return null;
+ }
+
+ public String getCacheFileName(String fileName) {
+ String hash = md5(fileName);
+
+ File file = new File(getExternalCacheDir().getAbsolutePath() + "/" + hash + ".png");
+
+ return file.getAbsolutePath();
+ }
+
+ public String getGoogleAccount() {
+ AccountManager manager = (AccountManager) getSystemService(ACCOUNT_SERVICE);
+ Account[] list = manager.getAccounts();
+
+ for (Account account: list) {
+ if (account.type.equalsIgnoreCase("com.google")) {
+ return account.name;
+ }
+ }
+ return null;
+ }
+
+ public void toast(int msgId) {
+ try {
+ Toast toast = Toast.makeText(CommonActivity.this, msgId, Toast.LENGTH_SHORT);
+ toast.show();
+ } catch (RuntimeException e) {
+ // might happen if UI lags
+ }
+ }
+
+ public void toast(String msg) {
+ try {
+ Toast toast = Toast.makeText(CommonActivity.this, msg, Toast.LENGTH_SHORT);
+ toast.show();
+ } catch (RuntimeException e) {
+ // might happen if UI lags
+ }
+ }
+
+ public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
+ // Raw height and width of image
+ final int height = options.outHeight;
+ final int width = options.outWidth;
+ int inSampleSize = 1;
+
+ if (height > reqHeight || width > reqWidth) {
+ if (width > height) {
+ inSampleSize = Math.round((float)height / (float)reqHeight);
+ } else {
+ inSampleSize = Math.round((float)width / (float)reqWidth);
+ }
+ }
+ return inSampleSize;
+ }
+
+
+ public void cleanupCache(boolean deleteAll) {
+ if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
+ File cachePath = getExternalCacheDir();
+
+ long now = new Date().getTime();
+
+ if (cachePath.isDirectory()) {
+ for (File file : cachePath.listFiles()) {
+ if (deleteAll || now - file.lastModified() > 1000*60*60*24*7) {
+ file.delete();
+ }
+ }
+ }
+ }
+ }
+
+ public boolean isStorageAvailable() {
+ return m_storageAvailable;
+ }
+
+ public boolean isStorageWritable() {
+ return m_storageWritable;
+ }
+
+ private void initDatabase() {
+ DatabaseHelper dh = new DatabaseHelper(getApplicationContext());
+
+ m_writableDb = dh.getWritableDatabase();
+ m_readableDb = dh.getReadableDatabase();
+ }
+
+ public synchronized SQLiteDatabase getReadableDb() {
+ return m_readableDb;
+ }
+
+ public synchronized SQLiteDatabase getWritableDb() {
+ return m_writableDb;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+
+ m_readableDb.close();
+ m_writableDb.close();
+ }
+
+ public void selectPreviousComic() {
+ ComicPager frag = (ComicPager) getSupportFragmentManager().findFragmentByTag(FRAG_COMICS_PAGER);
+
+ if (frag != null && frag.isAdded() && frag.getPosition() > 0) {
+ frag.setCurrentItem(frag.getPosition() - 1);
+ }
+ }
+
+ public void selectNextComic() {
+ ComicPager frag = (ComicPager) getSupportFragmentManager().findFragmentByTag(FRAG_COMICS_PAGER);
+
+ if (frag != null && frag.isAdded() && frag.getPosition() < frag.getCount()-1) {
+ frag.setCurrentItem(frag.getPosition() + 1);
+ }
+ }
+
+ public void cleanupSqliteCache(String baseDir) {
+ Cursor c = getReadableDb().query("comics_cache", null,
+ null, null, null, null, null);
+
+ if (c.moveToFirst()) {
+ while (!c.isAfterLast()) {
+ int id = c.getInt(c.getColumnIndex(BaseColumns._ID));
+ String fileName = c.getString(c.getColumnIndex("path")) + "/" + c.getString(c.getColumnIndex("filename"));
+
+ File file = new File(fileName);
+
+ if (!file.exists()) {
+ SQLiteStatement stmt = getWritableDb().compileStatement("DELETE FROM comics_cache WHERE " + BaseColumns._ID + " = ?");
+ stmt.bindLong(1, id);
+ stmt.execute();
+ }
+
+ c.moveToNext();
+ }
+ }
+
+ c.close();
+ }
+
+ public void hideSystemUI(boolean hide) {
+ View decorView = getWindow().getDecorView();
+
+ if (hide) {
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
+
+ decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_IMMERSIVE);
+
+ } else {
+ decorView.setSystemUiVisibility(View.STATUS_BAR_HIDDEN | View.SYSTEM_UI_FLAG_LOW_PROFILE);
+ }
+
+ getSupportActionBar().hide();
+
+ } else {
+ decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
+ }
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/DatabaseHelper.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/DatabaseHelper.java
new file mode 100644
index 0000000..bf66fec
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/DatabaseHelper.java
@@ -0,0 +1,68 @@
+package org.fox.ttcomics2;
+
+import java.io.File;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteStatement;
+import android.provider.BaseColumns;
+
+public class DatabaseHelper extends SQLiteOpenHelper {
+
+ public static final String DATABASE_NAME = "ComicsCache.db";
+ public static final int DATABASE_VERSION = 2;
+
+ public DatabaseHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("DROP TABLE IF EXISTS comics_cache;");
+
+ db.execSQL("CREATE TABLE IF NOT EXISTS comics_cache (" +
+ BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
+ "filename TEXT, " +
+ "path TEXT, " + //v2
+ "checksum TEXT, " + //v2
+ "size INTEGER, " +
+ "position INTEGER, " +
+ "max_position INTEGER" +
+ ");");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ if (oldVersion == 1 && newVersion == 2) {
+
+ db.execSQL("ALTER TABLE comics_cache ADD COLUMN path TEXT;");
+ db.execSQL("ALTER TABLE comics_cache ADD COLUMN checksum TEXT;");
+
+ Cursor c = db.query("comics_cache", null,
+ null, null, null, null, null);
+
+ if (c.moveToFirst()) {
+ while (!c.isAfterLast()) {
+ int id = c.getInt(c.getColumnIndex(BaseColumns._ID));
+ String fileName = c.getString(c.getColumnIndex("filename"));
+
+ File file = new File(fileName);
+
+ SQLiteStatement stmt = db.compileStatement("UPDATE comics_cache SET filename = ?, path = ? WHERE " + BaseColumns._ID + " = ?");
+ stmt.bindString(1, file.getName());
+ stmt.bindString(2, file.getParentFile().getAbsolutePath());
+ stmt.bindLong(3, id);
+ stmt.execute();
+
+ c.moveToNext();
+ }
+ }
+
+ c.close();
+ }
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/DirectoryPicker.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/DirectoryPicker.java
new file mode 100644
index 0000000..0c75f09
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/DirectoryPicker.java
@@ -0,0 +1,169 @@
+package org.fox.ttcomics2;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Environment;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.Toast;
+
+/**
+Copyright (C) 2011 by Brad Greco <[email protected]>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+ */
+
+public class DirectoryPicker extends ListActivity {
+
+ public static final String START_DIR = "startDir";
+ public static final String ONLY_DIRS = "onlyDirs";
+ public static final String SHOW_HIDDEN = "showHidden";
+ public static final String CHOSEN_DIRECTORY = "chosenDir";
+ public static final int PICK_DIRECTORY = 43522432;
+ private File dir;
+ private boolean showHidden = false;
+ private boolean onlyDirs = true ;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Bundle extras = getIntent().getExtras();
+ dir = Environment.getExternalStorageDirectory();
+ if (extras != null) {
+ String preferredStartDir = extras.getString(START_DIR);
+ showHidden = extras.getBoolean(SHOW_HIDDEN, false);
+ onlyDirs = extras.getBoolean(ONLY_DIRS, true);
+ if(preferredStartDir != null) {
+ File startDir = new File(preferredStartDir);
+ if(startDir.isDirectory()) {
+ dir = startDir;
+ }
+ }
+ }
+
+ setContentView(R.layout.chooser_list);
+ setTitle(dir.getAbsolutePath());
+ Button btnChoose = (Button) findViewById(R.id.btnChoose);
+ String name = dir.getName();
+ if(name.length() == 0)
+ name = "/";
+ btnChoose.setText(getString(R.string.picker_choose, name));
+ btnChoose.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ returnDir(dir.getAbsolutePath());
+ }
+ });
+
+ Button btnParent = (Button) findViewById(R.id.btnParent);
+ btnParent.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ Intent intent = new Intent(DirectoryPicker.this, DirectoryPicker.class);
+ intent.putExtra(DirectoryPicker.START_DIR, dir.getParent());
+ intent.putExtra(DirectoryPicker.SHOW_HIDDEN, showHidden);
+ intent.putExtra(DirectoryPicker.ONLY_DIRS, onlyDirs);
+ startActivityForResult(intent, PICK_DIRECTORY);
+ }
+ });
+
+ if (dir.getParent() == null) {
+ btnParent.setVisibility(View.GONE);
+ }
+
+ ListView lv = getListView();
+ lv.setTextFilterEnabled(true);
+
+ if(!dir.canRead()) {
+ Context context = getApplicationContext();
+ String msg = getString(R.string.error_could_not_read_folder_contents_);
+ Toast toast = Toast.makeText(context, msg, Toast.LENGTH_LONG);
+ toast.show();
+ return;
+ }
+
+ final ArrayList<File> files = filter(dir.listFiles(), onlyDirs, showHidden);
+ String[] names = names(files);
+ setListAdapter(new ArrayAdapter<String>(this, R.layout.list_item, names));
+
+
+ lv.setOnItemClickListener(new OnItemClickListener() {
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+ if(!files.get(position).isDirectory())
+ return;
+ String path = files.get(position).getAbsolutePath();
+ Intent intent = new Intent(DirectoryPicker.this, DirectoryPicker.class);
+ intent.putExtra(DirectoryPicker.START_DIR, path);
+ intent.putExtra(DirectoryPicker.SHOW_HIDDEN, showHidden);
+ intent.putExtra(DirectoryPicker.ONLY_DIRS, onlyDirs);
+ startActivityForResult(intent, PICK_DIRECTORY);
+ }
+ });
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if(requestCode == PICK_DIRECTORY && resultCode == RESULT_OK) {
+ Bundle extras = data.getExtras();
+ String path = (String) extras.get(DirectoryPicker.CHOSEN_DIRECTORY);
+ returnDir(path);
+ }
+ }
+
+ private void returnDir(String path) {
+ Intent result = new Intent();
+ result.putExtra(CHOSEN_DIRECTORY, path);
+ setResult(RESULT_OK, result);
+ finish();
+ }
+
+ public ArrayList<File> filter(File[] file_list, boolean onlyDirs, boolean showHidden) {
+ ArrayList<File> files = new ArrayList<File>();
+
+ for(File file: file_list) {
+ if(onlyDirs && !file.isDirectory())
+ continue;
+ if(!showHidden && file.isHidden())
+ continue;
+ files.add(file);
+ }
+ Collections.sort(files);
+
+ return files;
+ }
+
+ public String[] names(ArrayList<File> files) {
+ String[] names = new String[files.size()];
+ int i = 0;
+ for(File file: files) {
+ names[i] = file.getName();
+ i++;
+ }
+ return names;
+ }
+}
+
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/MainActivity.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/MainActivity.java
new file mode 100644
index 0000000..9dda29b
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/MainActivity.java
@@ -0,0 +1,228 @@
+package org.fox.ttcomics2;
+
+
+import android.annotation.SuppressLint;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v7.app.ActionBar;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+
+import java.io.File;
+
+import it.neokree.materialtabs.MaterialTab;
+import it.neokree.materialtabs.MaterialTabHost;
+import it.neokree.materialtabs.MaterialTabListener;
+
+public class MainActivity extends CommonActivity implements MaterialTabListener {
+ private final String TAG = this.getClass().getSimpleName();
+ private final static int TRIAL_DAYS = 7;
+
+ private int m_selectedTab;
+ private String m_baseDirectory = "";
+ private String m_fileName = "";
+ private MaterialTabHost tabHost;
+
+ @SuppressLint("NewApi")
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.activity_main);
+ setTitle(R.string.app_name);
+
+ tabHost = (MaterialTabHost) this.findViewById(R.id.materialTabHost);
+
+ if (savedInstanceState == null) {
+ m_selectedTab = getIntent().getIntExtra("selectedTab", 0);
+
+ Log.d(TAG, "selTab=" + m_selectedTab);
+
+ ComicListFragment frag = new ComicListFragment();
+ frag.setMode(m_selectedTab);
+
+ if (getIntent().getStringExtra("baseDir") != null) {
+ m_baseDirectory = getIntent().getStringExtra("baseDir");
+ frag.setBaseDirectory(m_baseDirectory);
+ }
+
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+ ft.replace(R.id.comics_list, frag, FRAG_COMICS_LIST);
+ ft.commit();
+
+ m_selectedTab = -1;
+ } else {
+ m_selectedTab = -1;
+ m_baseDirectory = savedInstanceState.getString("baseDir");
+ m_fileName = savedInstanceState.getString("fileName");
+ }
+
+ tabHost.addTab(tabHost.newTab()
+ .setText(getString(R.string.tab_all_comics))
+ .setTabListener(this));
+
+ tabHost.addTab(tabHost.newTab()
+ .setText(getString(R.string.tab_unread))
+ .setTabListener(this));
+
+ tabHost.addTab(tabHost.newTab()
+ .setText(getString(R.string.tab_unfinished))
+ .setTabListener(this));
+
+ tabHost.addTab(tabHost.newTab()
+ .setText(getString(R.string.tab_read))
+ .setTabListener(this));
+
+ if (savedInstanceState != null) {
+ m_selectedTab = savedInstanceState.getInt("selectedTab");
+ } else {
+ m_selectedTab = getIntent().getIntExtra("selectedTab", 0);
+ }
+
+ if (m_selectedTab != -1)
+ tabHost.setSelectedNavigationItem(m_selectedTab);
+
+ ActionBar actionBar = getSupportActionBar();
+ actionBar.setDisplayHomeAsUpEnabled(m_baseDirectory.length() > 0);
+
+ if (m_prefs.getString("comics_directory", null) == null) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setMessage(R.string.dialog_need_prefs_message)
+ .setCancelable(false)
+ .setPositiveButton(R.string.dialog_need_prefs_preferences, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ // launch preferences
+
+ Intent intent = new Intent(MainActivity.this,
+ PreferencesActivity.class);
+ startActivityForResult(intent, 0);
+ }
+ })
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.cancel();
+ }
+ });
+ AlertDialog alert = builder.create();
+ alert.show();
+ }
+
+ //((ViewGroup)findViewById(R.id.comics_list)).setLayoutTransition(new LayoutTransition());
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.activity_main, menu);
+
+ return true;
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle out) {
+ super.onSaveInstanceState(out);
+
+ out.putInt("selectedTab", m_selectedTab);
+ out.putString("baseDir", m_baseDirectory);
+ out.putString("fileName", m_fileName);
+ }
+
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ if (m_baseDirectory.length() > 0) {
+ finish();
+ }
+ return true;
+ default:
+ Log.d(TAG,
+ "onOptionsItemSelected, unhandled id=" + item.getItemId());
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
+ public void onComicArchiveSelected(String fileName) {
+ super.onComicArchiveSelected(fileName);
+
+ File file = new File(fileName);
+
+ if (file.isDirectory()) {
+ Intent intent = new Intent(MainActivity.this,
+ MainActivity.class);
+
+ intent.putExtra("baseDir", fileName);
+ intent.putExtra("selectedTab", m_selectedTab);
+
+ startActivityForResult(intent, 0);
+
+ } else if (file.canRead()) {
+ Intent intent = new Intent(MainActivity.this,
+ ViewComicActivity.class);
+
+ intent.putExtra("fileName", fileName);
+ m_fileName = fileName;
+
+ startActivityForResult(intent, REQUEST_VIEWCOMIC);
+ } else {
+ toast(getString(R.string.error_cant_open_file, fileName));
+
+ ComicListFragment frag = (ComicListFragment) getSupportFragmentManager().findFragmentByTag(FRAG_COMICS_LIST);
+
+ if (frag != null && frag.isAdded()) {
+ frag.rescan(true);
+ }
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
+ if (requestCode == REQUEST_VIEWCOMIC) {
+ Log.d(TAG, "finished viewing comic: " + m_fileName);
+
+ if (m_prefs.getBoolean("use_position_sync", false) && m_syncClient.hasOwner()) {
+ toast(R.string.sync_uploading);
+ m_syncClient.setPosition(sha1(new File(m_fileName).getName()), getLastPosition(m_fileName));
+ }
+ }
+
+ System.gc();
+
+ super.onActivityResult(requestCode, resultCode, intent);
+ }
+
+ @Override
+ public void onTabSelected(MaterialTab tab) {
+
+ tabHost.setSelectedNavigationItem(tab.getPosition());
+
+ FragmentTransaction sft = getSupportFragmentManager().beginTransaction();
+
+ if (m_selectedTab != tab.getPosition() && m_selectedTab != -1) {
+
+ ComicListFragment frag = new ComicListFragment();
+ frag.setMode(tab.getPosition());
+
+ frag.setBaseDirectory(m_baseDirectory);
+
+ sft.replace(R.id.comics_list, frag, FRAG_COMICS_LIST);
+ }
+
+ m_selectedTab = tab.getPosition();
+
+ sft.commit();
+ }
+
+ @Override
+ public void onTabReselected(MaterialTab materialTab) {
+
+ }
+
+ @Override
+ public void onTabUnselected(MaterialTab materialTab) {
+
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/NaturalOrderComparator.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/NaturalOrderComparator.java
new file mode 100644
index 0000000..d7522f9
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/NaturalOrderComparator.java
@@ -0,0 +1,188 @@
+package org.fox.ttcomics2;
+
+/*
+ NaturalOrderComparator.java -- Perform 'natural order' comparisons of strings in Java.
+ Copyright (C) 2003 by Pierre-Luc Paour <[email protected]>
+
+ Based on the C version by Martin Pool, of which this is more or less a straight conversion.
+ Copyright (C) 2000 by Martin Pool <[email protected]>
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+ */
+
+import java.util.*;
+
+public class NaturalOrderComparator implements Comparator
+{
+ int compareRight(String a, String b)
+ {
+ int bias = 0;
+ int ia = 0;
+ int ib = 0;
+
+ // The longest run of digits wins. That aside, the greatest
+ // value wins, but we can't know that it will until we've scanned
+ // both numbers to know that they have the same magnitude, so we
+ // remember it in BIAS.
+ for (;; ia++, ib++)
+ {
+ char ca = charAt(a, ia);
+ char cb = charAt(b, ib);
+
+ if (!Character.isDigit(ca) && !Character.isDigit(cb))
+ {
+ return bias;
+ }
+ else if (!Character.isDigit(ca))
+ {
+ return -1;
+ }
+ else if (!Character.isDigit(cb))
+ {
+ return +1;
+ }
+ else if (ca < cb)
+ {
+ if (bias == 0)
+ {
+ bias = -1;
+ }
+ }
+ else if (ca > cb)
+ {
+ if (bias == 0)
+ bias = +1;
+ }
+ else if (ca == 0 && cb == 0)
+ {
+ return bias;
+ }
+ }
+ }
+
+ public int compare(Object o1, Object o2)
+ {
+ String a = o1.toString();
+ String b = o2.toString();
+
+ int ia = 0, ib = 0;
+ int nza = 0, nzb = 0;
+ char ca, cb;
+ int result;
+
+ while (true)
+ {
+ // only count the number of zeroes leading the last number compared
+ nza = nzb = 0;
+
+ ca = charAt(a, ia);
+ cb = charAt(b, ib);
+
+ // skip over leading spaces or zeros
+ while (Character.isSpaceChar(ca) || ca == '0')
+ {
+ if (ca == '0')
+ {
+ nza++;
+ }
+ else
+ {
+ // only count consecutive zeroes
+ nza = 0;
+ }
+
+ ca = charAt(a, ++ia);
+ }
+
+ while (Character.isSpaceChar(cb) || cb == '0')
+ {
+ if (cb == '0')
+ {
+ nzb++;
+ }
+ else
+ {
+ // only count consecutive zeroes
+ nzb = 0;
+ }
+
+ cb = charAt(b, ++ib);
+ }
+
+ // process run of digits
+ if (Character.isDigit(ca) && Character.isDigit(cb))
+ {
+ if ((result = compareRight(a.substring(ia), b.substring(ib))) != 0)
+ {
+ return result;
+ }
+ }
+
+ if (ca == 0 && cb == 0)
+ {
+ // The strings compare the same. Perhaps the caller
+ // will want to call strcmp to break the tie.
+ return nza - nzb;
+ }
+
+ if (ca < cb)
+ {
+ return -1;
+ }
+ else if (ca > cb)
+ {
+ return +1;
+ }
+
+ ++ia;
+ ++ib;
+ }
+ }
+
+ static char charAt(String s, int i)
+ {
+ if (i >= s.length())
+ {
+ return 0;
+ }
+ else
+ {
+ return s.charAt(i);
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ String[] strings = new String[] { "1-2", "1-02", "1-20", "10-20", "fred", "jane", "pic01",
+ "pic2", "pic02", "pic02a", "pic3", "pic4", "pic 4 else", "pic 5", "pic05", "pic 5",
+ "pic 5 something", "pic 6", "pic 7", "pic100", "pic100a", "pic120", "pic121",
+ "pic02000", "tom", "x2-g8", "x2-y7", "x2-y08", "x8-y8" };
+
+ List orig = Arrays.asList(strings);
+
+ System.out.println("Original: " + orig);
+
+ List scrambled = Arrays.asList(strings);
+ Collections.shuffle(scrambled);
+
+ System.out.println("Scrambled: " + scrambled);
+
+ Collections.sort(scrambled, new NaturalOrderComparator());
+
+ System.out.println("Sorted: " + scrambled);
+ }
+} \ No newline at end of file
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/PreferencesActivity.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/PreferencesActivity.java
new file mode 100644
index 0000000..634b787
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/PreferencesActivity.java
@@ -0,0 +1,156 @@
+package org.fox.ttcomics2;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceManager;
+import android.widget.Toast;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+public class PreferencesActivity extends PreferenceActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final SharedPreferences prefs = PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext());
+
+ addPreferencesFromResource(R.xml.preferences);
+
+ Preference dirPref = findPreference("comics_directory");
+ dirPref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ Intent intent = new Intent(PreferencesActivity.this, DirectoryPicker.class);
+
+ intent.putExtra(DirectoryPicker.START_DIR, prefs.getString("comics_directory",
+ Environment.getExternalStorageDirectory().getAbsolutePath()));
+
+ startActivityForResult(intent, DirectoryPicker.PICK_DIRECTORY);
+ return true;
+ }
+ });
+
+ Preference clearPref = findPreference("clear_sync_data");
+ clearPref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(PreferencesActivity.this);
+ builder.setMessage(R.string.dialog_clear_data_title)
+ .setCancelable(false)
+ .setPositiveButton(R.string.dialog_clear_data, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+
+ String googleAccount = getGoogleAccount();
+ SyncClient m_syncClient = new SyncClient();
+
+ if (googleAccount != null) {
+ m_syncClient.setOwner(googleAccount);
+ } else {
+ if (Build.HARDWARE.equals("goldfish")) {
+ m_syncClient.setOwner("TEST-ACCOUNT");
+ } else {
+ m_syncClient.setOwner(null);
+
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putBoolean("use_position_sync", false);
+ editor.commit();
+
+ Toast toast = Toast.makeText(PreferencesActivity.this, R.string.error_sync_no_account, Toast.LENGTH_SHORT);
+ toast.show();
+ }
+ }
+
+ if (m_syncClient.hasOwner()) {
+ m_syncClient.clearData();
+ }
+ }
+ })
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.cancel();
+ }
+ });
+ AlertDialog alert = builder.create();
+ alert.show();
+
+ return false;
+ }
+ });
+
+ String version = "?";
+ int versionCode = -1;
+ String buildTimestamp = "N/A";
+
+ try {
+ PackageInfo packageInfo = getPackageManager().
+ getPackageInfo(getPackageName(), 0);
+
+ version = packageInfo.versionName;
+ versionCode = packageInfo.versionCode;
+
+ ApplicationInfo appInfo = getPackageManager().
+ getApplicationInfo(getPackageName(), 0);
+
+ ZipFile zf = new ZipFile(appInfo.sourceDir);
+ ZipEntry ze = zf.getEntry("classes.dex");
+ long time = ze.getTime();
+
+ buildTimestamp = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss",
+ Locale.getDefault()).format(time);
+
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ findPreference("version").setTitle(getString(R.string.prefs_version, version, versionCode));
+ findPreference("build_timestamp").setTitle(getString(R.string.prefs_build_timestamp, buildTimestamp));
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if(requestCode == DirectoryPicker.PICK_DIRECTORY && resultCode == RESULT_OK) {
+ Bundle extras = data.getExtras();
+ String path = (String) extras.get(DirectoryPicker.CHOSEN_DIRECTORY);
+
+ SharedPreferences prefs = PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext());
+
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putString("comics_directory", path);
+ editor.commit();
+
+ }
+ }
+
+ public String getGoogleAccount() {
+ AccountManager manager = (AccountManager) getSystemService(ACCOUNT_SERVICE);
+ Account[] list = manager.getAccounts();
+
+ for (Account account: list) {
+ if (account.type.equalsIgnoreCase("com.google")) {
+ return account.name;
+ }
+ }
+ return null;
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/SyncClient.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/SyncClient.java
new file mode 100644
index 0000000..7d4c64a
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/SyncClient.java
@@ -0,0 +1,161 @@
+package org.fox.ttcomics2;
+
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import android.os.AsyncTask;
+import android.util.Log;
+
+public class SyncClient {
+ public interface PositionReceivedListener {
+ void onPositionReceived(int position);
+ }
+
+ private class HttpTask extends AsyncTask<String, Integer, Boolean> {
+ protected String m_response = null;
+ protected int m_responseCode = -1;
+
+ @Override
+ protected Boolean doInBackground(String... params) {
+
+ String requestStr = null;
+ String op = params[0];
+
+ if (op.equals("set")) {
+ requestStr = String.format("op=set&owner=%1$s&hash=%2$s&position=%3$s", m_owner, params[1], params[2]);
+ } else if (op.equals("get")) {
+ requestStr = String.format("op=get&owner=%1$s&hash=%2$s", m_owner, params[1]);
+ } else if (op.equals("clear")) {
+ if (params.length > 1) {
+ requestStr = String.format("op=clear&owner=%1$s&hash=%2$s", m_owner, params[1]);
+ } else {
+ requestStr = String.format("op=clear&owner=%1$s", m_owner);
+ }
+ }
+
+ requestStr += "&version=2";
+
+ Log.d(TAG, requestStr);
+
+ if (requestStr == null) return false;
+
+ try {
+ byte[] postData = requestStr.getBytes("UTF-8");
+
+ URL url = new URL(SYNC_ENDPOINT);
+
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setDoInput(true);
+ conn.setDoOutput(true);
+ conn.setUseCaches(false);
+ conn.setRequestMethod("POST");
+
+ OutputStream out = conn.getOutputStream();
+ out.write(postData);
+ out.close();
+
+ m_responseCode = conn.getResponseCode();
+
+ if (m_responseCode == HttpURLConnection.HTTP_OK) {
+ StringBuffer response = new StringBuffer();
+ InputStreamReader in = new InputStreamReader(conn.getInputStream(), "UTF-8");
+
+ char[] buf = new char[1024];
+ int read = 0;
+
+ while ((read = in.read(buf)) >= 0) {
+ response.append(buf, 0, read);
+ }
+
+ //Log.d(TAG, "<<< " + response);
+
+ m_response = response.toString();
+
+ if (response.indexOf("ERROR") == -1) {
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ Log.d(TAG, "HTTP error, code: " + m_responseCode);
+ }
+
+ conn.disconnect();
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return false;
+ }
+
+ }
+
+ private final String TAG = this.getClass().getSimpleName();
+ private static final String SYNC_ENDPOINT = "http://tt-rss.org/tcrsync/";
+ private String m_owner = null;
+
+ public void setOwner(String owner) {
+ m_owner = CommonActivity.sha1(owner);
+ }
+
+ public int getPosition(String hash, final PositionReceivedListener listener) {
+ if (m_owner != null) {
+ Log.d(TAG, "Requesting sync data...");
+
+ HttpTask task = new HttpTask() {
+ @Override
+ protected void onPostExecute(Boolean result) {
+ if (result) {
+ try {
+ listener.onPositionReceived(Integer.valueOf(m_response));
+ } catch (NumberFormatException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ };
+
+ task.execute("get", hash);
+
+ }
+ return -1;
+ }
+
+ public void setPosition(String hash, int position) {
+ if (m_owner != null) {
+ Log.d(TAG, "Uploading sync data...");
+
+ HttpTask task = new HttpTask();
+
+ task.execute("set", hash, String.valueOf(position));
+ }
+ }
+
+ public void clearData() {
+ if (m_owner != null) {
+ Log.d(TAG, "Clearing sync data...");
+
+ HttpTask task = new HttpTask();
+
+ task.execute("clear");
+ }
+ }
+
+ public void clearData(String hash) {
+ if (m_owner != null) {
+ Log.d(TAG, "Clearing sync data: " + hash);
+
+ HttpTask task = new HttpTask();
+
+ task.execute("clear", hash);
+ }
+ }
+
+ public boolean hasOwner() {
+ return m_owner != null;
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ViewComicActivity.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ViewComicActivity.java
new file mode 100644
index 0000000..abbf835
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ViewComicActivity.java
@@ -0,0 +1,321 @@
+package org.fox.ttcomics2;
+
+
+import android.annotation.SuppressLint;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.net.Uri;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.support.v4.app.FragmentTransaction;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.NumberPicker;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ViewComicActivity extends CommonActivity {
+ private final String TAG = this.getClass().getSimpleName();
+
+ private String m_fileName;
+ private String m_tmpFileName;
+
+ @SuppressLint("NewApi")
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ m_prefs = PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext());
+
+ setTheme(m_prefs.getBoolean("use_dark_theme", false) ? R.style.ViewDarkTheme : R.style.ViewLightTheme);
+
+ super.onCreate(savedInstanceState);
+
+ if (m_prefs.getBoolean("prevent_screen_sleep", false)) {
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
+
+ if (m_prefs.getBoolean("use_full_screen", false)) {
+ hideSystemUI(true);
+ }
+
+ setContentView(R.layout.activity_view_comic);
+
+ if (savedInstanceState == null) {
+ m_fileName = getIntent().getStringExtra("fileName");
+
+ ComicPager cp = new ComicPager();
+ cp.setFileName(m_fileName);
+
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+ ft.replace(R.id.comics_pager_container, cp, FRAG_COMICS_PAGER);
+ ft.commit();
+ } else {
+ m_fileName = savedInstanceState.getString("fileName");
+ m_tmpFileName = savedInstanceState.getString("tmpFileName");
+ }
+
+ setOrientationLock(isOrientationLocked(), true);
+
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ setTitle(new File(m_fileName).getName());
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.activity_view_comic, menu);
+
+ menu.findItem(R.id.menu_sync_location).setVisible(m_prefs.getBoolean("use_position_sync", false) && m_syncClient.hasOwner());
+
+ return true;
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle out) {
+ super.onSaveInstanceState(out);
+
+ out.putString("fileName", m_fileName);
+ out.putString("tmpFileName", m_tmpFileName);
+ }
+
+ @Override
+ public void onComicSelected(String fileName, int position) {
+ super.onComicSelected(fileName, position);
+ }
+
+ public void onPause() {
+ super.onPause();
+
+ // upload progress
+ if (m_prefs.getBoolean("use_position_sync", false) && m_syncClient.hasOwner()) {
+ //toast(R.string.sync_uploading);
+ m_syncClient.setPosition(sha1(new File(m_fileName).getName()), getLastPosition(m_fileName));
+ }
+ }
+
+ private void shareComic() {
+
+ ComicPager pager = (ComicPager) getSupportFragmentManager().findFragmentByTag(FRAG_COMICS_PAGER);
+
+ if (pager != null) {
+
+ try {
+ File tmpFile = File.createTempFile("trcshare" + sha1(m_fileName + " " + pager.getPosition()), ".jpg", getExternalCacheDir());
+
+ Log.d(TAG, "FILE=" + tmpFile);
+
+ InputStream is = pager.getArchive().getItem(pager.getPosition());
+
+ FileOutputStream fos = new FileOutputStream(tmpFile);
+
+ byte[] buffer = new byte[1024];
+ int len = 0;
+ while ((len = is.read(buffer)) != -1) {
+ fos.write(buffer, 0, len);
+ }
+
+ fos.close();
+ is.close();
+
+ Intent shareIntent = new Intent(Intent.ACTION_SEND);
+
+ shareIntent.setType("image/jpeg");
+ shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(tmpFile));
+
+ m_tmpFileName = tmpFile.getAbsolutePath();
+
+ startActivityForResult(Intent.createChooser(shareIntent, getString(R.string.share_comic)), REQUEST_SHARE);
+
+ } catch (IOException e) {
+ toast(getString(R.string.error_could_not_prepare_file_for_sharing));
+ e.printStackTrace();
+ }
+
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
+ if (requestCode == REQUEST_SHARE) {
+ File tmpFile = new File(m_tmpFileName);
+
+ if (tmpFile.exists()) {
+ tmpFile.delete();
+ }
+
+ }
+ super.onActivityResult(requestCode, resultCode, intent);
+ }
+
+ protected boolean isOrientationLocked() {
+ return m_prefs.getBoolean("prefs_lock_orientation", false);
+ }
+
+ private void setOrientationLock(boolean locked, boolean restoreLast) {
+ if (locked) {
+
+ int currentOrientation = restoreLast ? m_prefs.getInt("last_orientation", getResources().getConfiguration().orientation) :
+ getResources().getConfiguration().orientation;
+
+ if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
+ } else {
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
+ }
+ } else {
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
+ }
+
+ if (locked != isOrientationLocked()) {
+ SharedPreferences.Editor editor = m_prefs.edit();
+ editor.putBoolean("prefs_lock_orientation", locked);
+ editor.putInt("last_orientation", getResources().getConfiguration().orientation);
+ editor.commit();
+ }
+ }
+
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_share:
+ shareComic();
+ return true;
+ case R.id.menu_toggle_orientation_lock:
+ setOrientationLock(!isOrientationLocked(), false);
+ return true;
+ case R.id.menu_sync_location:
+ m_syncClient.getPosition(sha1(new File(m_fileName).getName()), new SyncClient.PositionReceivedListener() {
+ @Override
+ public void onPositionReceived(final int position) {
+ final ComicPager pager = (ComicPager) getSupportFragmentManager().findFragmentByTag(FRAG_COMICS_PAGER);
+
+ if (pager != null && pager.isAdded()) {
+ int localPosition = pager.getPosition();
+
+ if (position > localPosition) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(ViewComicActivity.this);
+ builder.setMessage(getString(R.string.sync_server_has_further_page, localPosition+1, position+1))
+ .setCancelable(true)
+ .setPositiveButton(R.string.dialog_open_page, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ pager.setCurrentItem(position);
+ }
+ })
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.cancel();
+ }
+ });
+ AlertDialog alert = builder.create();
+ alert.show();
+
+ } else {
+ toast(R.string.error_sync_no_data);
+ }
+
+ }
+ }
+ });
+ return true;
+ case R.id.menu_go_location:
+ Dialog dialog = new Dialog(ViewComicActivity.this);
+ AlertDialog.Builder builder = new AlertDialog.Builder(ViewComicActivity.this)
+ .setTitle("Go to...")
+ .setItems(
+ new String[] {
+ getString(R.string.dialog_location_beginning),
+ getString(R.string.dialog_location_furthest),
+ getString(R.string.dialog_location_location),
+ getString(R.string.dialog_location_end)
+ },
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog,
+ int which) {
+
+ final ComicPager cp = (ComicPager) getSupportFragmentManager().findFragmentByTag(CommonActivity.FRAG_COMICS_PAGER);
+
+ switch (which) {
+ case 0:
+ cp.setCurrentItem(0);
+ break;
+ case 1:
+ cp.setCurrentItem(getMaxPosition(m_fileName));
+ break;
+ case 2:
+ if (true) {
+ LayoutInflater inflater = getLayoutInflater();
+ View contentView = inflater.inflate(R.layout.dialog_location, null);
+
+ final NumberPicker picker = (NumberPicker) contentView.findViewById(R.id.number_picker);
+
+ picker.setMinValue(1);
+ picker.setMaxValue(getSize(m_fileName));
+ picker.setValue(cp.getPosition() + 1);
+
+ Dialog seekDialog = new Dialog(ViewComicActivity.this);
+ AlertDialog.Builder seekBuilder = new AlertDialog.Builder(ViewComicActivity.this)
+ .setTitle(R.string.dialog_open_location)
+ .setView(contentView)
+ .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.cancel();
+ }
+ }).setPositiveButton(R.string.dialog_open_location, new DialogInterface.OnClickListener() {
+
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.cancel();
+
+ cp.setCurrentItem(picker.getValue()-1);
+
+ }
+ });
+
+ seekDialog = seekBuilder.create();
+ seekDialog.show();
+ }
+
+ break;
+ case 3:
+ cp.setCurrentItem(cp.getCount() - 1);
+ break;
+ }
+
+ dialog.cancel();
+ }
+ });
+
+ dialog = builder.create();
+ dialog.show();
+
+ return true;
+ case android.R.id.home:
+ finish();
+ return true;
+ default:
+ Log.d(TAG,
+ "onOptionsItemSelected, unhandled id=" + item.getItemId());
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ public void hideSeekBar(boolean hide) {
+ ComicPager pager = (ComicPager) getSupportFragmentManager().findFragmentByTag(FRAG_COMICS_PAGER);
+
+ if (pager != null) {
+ pager.hideSeekBar(hide);
+ }
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ViewPager.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ViewPager.java
new file mode 100644
index 0000000..91feea1
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics2/ViewPager.java
@@ -0,0 +1,38 @@
+package org.fox.ttcomics2;
+
+import it.sephiroth.android.library.imagezoom.ImageViewTouch;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+public class ViewPager extends android.support.v4.view.ViewPager {
+ public ViewPager(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
+ if (v instanceof ImageViewTouch) {
+ ImageViewTouch ivt = (ImageViewTouch) v;
+ try {
+ return ivt.canScroll(dx);
+ } catch (NullPointerException e) {
+ // bad image, etc
+ return false;
+ }
+ } else {
+ return super.canScroll(v, checkV, dx, x, y);
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return super.onTouchEvent(event);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ return super.onInterceptTouchEvent(event);
+ }
+} \ No newline at end of file