summaryrefslogtreecommitdiff
path: root/org.fox.ttcomics/src/main/java/org
diff options
context:
space:
mode:
Diffstat (limited to 'org.fox.ttcomics/src/main/java/org')
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/CbrComicArchive.java69
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/CbzComicArchive.java50
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicArchive.java12
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicFragment.java329
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicListFragment.java529
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicPager.java166
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/CommonActivity.java653
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/DatabaseHelper.java68
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/DirectoryPicker.java169
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/MainActivity.java253
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/PreferencesActivity.java124
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/SyncClient.java159
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/ViewComicActivity.java391
-rw-r--r--org.fox.ttcomics/src/main/java/org/fox/ttcomics/ViewPager.java38
14 files changed, 3010 insertions, 0 deletions
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics/CbrComicArchive.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/CbrComicArchive.java
new file mode 100644
index 0000000..b1fb65d
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/CbrComicArchive.java
@@ -0,0 +1,69 @@
+package org.fox.ttcomics;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.zip.ZipFile;
+
+import com.github.junrar.Archive;
+import com.github.junrar.exception.RarException;
+import com.github.junrar.rarfile.FileHeader;
+
+import android.util.Log;
+
+public class CbrComicArchive extends ComicArchive {
+ private final String TAG = this.getClass().getSimpleName();
+
+ private Archive m_archive;
+ private ArrayList<FileHeader> m_entries = new ArrayList<FileHeader>();
+
+ @Override
+ public int getCount() {
+ return m_entries.size();
+ }
+
+ @Override
+ public InputStream getItem(int index) throws IOException {
+ try {
+ return m_archive.getInputStream(m_entries.get(index));
+ } catch (RarException e) {
+ e.printStackTrace();
+ return null;
+ } catch (OutOfMemoryError e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public CbrComicArchive(String fileName) throws IOException, RarException {
+ m_archive = new Archive(new File(fileName));
+
+ FileHeader header = m_archive.nextFileHeader();
+
+ while (header != null) {
+ if (!header.isDirectory()) {
+ String name = header.isUnicode() ? header.getFileNameW() : header.getFileNameString();
+
+ if (isValidComic(name)) {
+ m_entries.add(header);
+ }
+ }
+
+ header = m_archive.nextFileHeader();
+ }
+
+ Collections.sort(m_entries, new Comparator<FileHeader>() {
+ public int compare(FileHeader a, FileHeader b) {
+ String nameA = a.isUnicode() ? a.getFileNameW() : a.getFileNameString();
+ String nameB = b.isUnicode() ? b.getFileNameW() : b.getFileNameString();
+
+ return nameA.compareTo(nameB);
+ }
+ });
+
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics/CbzComicArchive.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/CbzComicArchive.java
new file mode 100644
index 0000000..5e5b54f
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/CbzComicArchive.java
@@ -0,0 +1,50 @@
+package org.fox.ttcomics;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+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 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));
+ }
+
+ public CbzComicArchive(String fileName) throws IOException {
+ m_zipFile = new ZipFile(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 Comparator<ZipEntry>() {
+ public int compare(ZipEntry a, ZipEntry b) {
+ return a.getName().compareTo(b.getName());
+ }
+ });
+
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicArchive.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicArchive.java
new file mode 100644
index 0000000..3070323
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicArchive.java
@@ -0,0 +1,12 @@
+package org.fox.ttcomics;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public abstract class ComicArchive {
+ public abstract int getCount();
+ public abstract InputStream getItem(int index) throws IOException;
+ public boolean isValidComic(String fileName) {
+ return fileName.toLowerCase().matches(".*\\.(jpg|bmp|gif|png)$");
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicFragment.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicFragment.java
new file mode 100644
index 0000000..75d750f
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicFragment.java
@@ -0,0 +1,329 @@
+package org.fox.ttcomics;
+
+
+import it.sephiroth.android.library.imagezoom.ImageViewTouch;
+
+import java.io.IOException;
+
+import android.app.Activity;
+import android.content.SharedPreferences;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.support.v4.app.Fragment;
+import android.support.v7.app.ActionBar;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+public class ComicFragment extends Fragment implements GestureDetector.OnDoubleTapListener {
+ private final String TAG = this.getClass().getSimpleName();
+
+ private SharedPreferences m_prefs;
+ private int m_page;
+ private CommonActivity m_activity;
+ private GestureDetector m_detector;
+ private boolean m_thumbnail = false;
+
+ public ComicFragment() {
+ super();
+ }
+
+ private void setThumbnail(boolean thumbnail) {
+ if (m_thumbnail != thumbnail) {
+ m_thumbnail = thumbnail;
+
+ if (isAdded()) {
+ AsyncTask<ComicArchive, Void, Bitmap> loadTask = new AsyncTask<ComicArchive, Void, Bitmap>() {
+ @Override
+ protected Bitmap doInBackground(ComicArchive... params) {
+ return loadImage(params[0], m_page);
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap result) {
+ CommonActivity activity = (CommonActivity) getActivity();
+
+ ImageViewTouch image = (ImageViewTouch) getView().findViewById(R.id.comic_image);
+
+ if (activity != null && isAdded() && image != null) {
+ if (result != null) {
+ image.setImageBitmap(result);
+ } else {
+ activity.toast(R.string.error_loading_image);
+ image.setImageResource(R.drawable.badimage);
+ }
+ }
+ }
+ };
+
+ ComicPager pager = (ComicPager) getActivity().getSupportFragmentManager().findFragmentByTag(CommonActivity.FRAG_COMICS_PAGER);
+ loadTask.execute(pager.getArchive());
+ }
+ }
+ }
+
+ public void setPage(int page) {
+ m_page = page;
+ }
+
+ public Bitmap loadImage(ComicArchive archive, int page) {
+ CommonActivity activity = (CommonActivity) getActivity();
+
+ try {
+ final BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeStream(archive.getItem(page), null, options);
+ options.inJustDecodeBounds = false;
+
+ Bitmap bitmap = null;
+
+ int sampleSizes[];
+
+ if (m_thumbnail) {
+ sampleSizes = new int[] { 512, 256 };
+ } else {
+ sampleSizes = new int[] { 1024, 768, 512, 256 };
+ }
+
+ for (int sampleSize : sampleSizes) {
+ try {
+ options.inSampleSize = CommonActivity.calculateInSampleSize(options, sampleSize, sampleSize);
+ bitmap = BitmapFactory.decodeStream(archive.getItem(page), null, options);
+
+ return bitmap;
+ } catch (OutOfMemoryError e) {
+ if (sampleSize == sampleSizes[sampleSizes.length-1]) {
+ e.printStackTrace();
+
+ if (activity != null && isAdded()) {
+ activity.toast(R.string.error_out_of_memory);
+ }
+ }
+ }
+ }
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ @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");
+ }
+
+ ComicPager pager = (ComicPager) getActivity().getSupportFragmentManager().findFragmentByTag(CommonActivity.FRAG_COMICS_PAGER);
+
+ if (pager != null) {
+ if (CommonActivity.isCompatMode() && m_prefs.getBoolean("use_dark_theme", false)) {
+ image.setBackgroundColor(0xff000000);
+ }
+
+ image.setFitToScreen(true);
+
+ if (m_prefs.getBoolean("fit_to_width", false)) {
+ image.setFitToWidth(true);
+ }
+
+ AsyncTask<ComicArchive, Void, Bitmap> loadTask = new AsyncTask<ComicArchive, Void, Bitmap>() {
+ @Override
+ protected Bitmap doInBackground(ComicArchive... params) {
+ return loadImage(params[0], m_page);
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap result) {
+ CommonActivity activity = (CommonActivity) getActivity();
+
+ if (activity != null && isAdded()) {
+ if (result != null) {
+ image.setImageBitmap(result);
+ } else {
+ activity.toast(R.string.error_loading_image);
+ image.setImageResource(R.drawable.badimage);
+ }
+ }
+ }
+ };
+
+ loadTask.execute(pager.getArchive());
+
+ 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 = (CommonActivity) 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);
+ }
+
+ @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.hideSystemUiIfNecessary();
+ } else {
+ m_activity.showSystemUiIfNecessary();
+ bar.show();
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicListFragment.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicListFragment.java
new file mode 100644
index 0000000..26166fc
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicListFragment.java
@@ -0,0 +1,529 @@
+package org.fox.ttcomics;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+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.github.junrar.exception.RarException;
+
+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);
+
+ boolean isList = ComicListFragment.this.getView().findViewById(R.id.comics_list) != null;
+
+ if (v == null) {
+ LayoutInflater vi = (LayoutInflater)getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+
+ v = vi.inflate(isList ? R.layout.comics_list_row : 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 {
+ info.setVisibility(View.GONE);
+ }
+ }
+
+ 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.setVisibility(View.VISIBLE);
+ } else {
+ progressBar.setVisibility(View.GONE);
+ }
+ }
+
+ 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) {
+ View imageholder = v.findViewById(R.id.imageholder);
+
+ int padding = dpToPx(2);
+
+ if (imageholder != null) {
+ if (size == SIZE_DIR) {
+ imageholder.setBackgroundResource(R.drawable.comic_tile_folder);
+ imageholder.setPadding(padding, padding, padding, padding);
+ } else {
+ imageholder.setBackgroundResource(R.drawable.comic_tile);
+ imageholder.setPadding(padding, padding, padding, padding);
+ }
+ }
+
+ thumbnail.setTag("");
+ thumbnail.setImageResource(R.drawable.ic_launcher);
+
+ if (m_activity.isStorageAvailable() && thumbnailFile.exists()) {
+ thumbnail.setTag(thumbnailFile.getAbsolutePath());
+
+ CoverImageLoader imageLoader = new CoverImageLoader();
+ imageLoader.execute(thumbnail);
+ }
+ }
+
+ return v;
+ }
+ }
+
+ public int dpToPx(int dp) {
+ DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
+ int px = Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
+ return px;
+ }
+
+ class CoverImageLoader extends AsyncTask<ImageView, Void, Bitmap> {
+ private ImageView m_thumbnail;
+ private String m_tag;
+
+ @Override
+ protected Bitmap doInBackground(ImageView... params) {
+ m_thumbnail = params[0];
+
+ if (m_thumbnail != null) {
+ m_tag = m_thumbnail.getTag().toString();
+
+ File thumbnailFile = new File(m_tag);
+
+ if (thumbnailFile.exists() && thumbnailFile.canRead()) {
+
+ final BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(thumbnailFile.getAbsolutePath(), options);
+
+ options.inSampleSize = CommonActivity.calculateInSampleSize(options, 256, 256);
+ options.inJustDecodeBounds = false;
+
+ Bitmap bmp = BitmapFactory.decodeFile(thumbnailFile.getAbsolutePath(), options);
+
+ return bmp;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap bmp) {
+ if (isAdded() && bmp != null && m_tag != null && m_tag.equals(m_thumbnail.getTag().toString())) {
+ m_thumbnail.setImageBitmap(bmp);
+ }
+ }
+
+ };
+
+ @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_files = savedInstanceState.getStringArrayList("files");
+ }
+
+ m_swipeLayout = (SwipeRefreshLayout) view.findViewById(R.id.comics_swipe_container);
+
+ m_swipeLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
+ @Override
+ public void onRefresh() {
+ rescan(true);
+ }
+ });
+
+ if (!CommonActivity.isCompatMode()) {
+ 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_list_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) {
+ m_swipeLayout.setRefreshing(true);
+
+ 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);
+ } else {
+ try {
+ cba = new CbrComicArchive(filePath);
+ } catch (RarException e) {
+ e.printStackTrace();
+ }
+ }
+
+ 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 = 0;
+ 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);
+
+ m_swipeLayout.setRefreshing(false);
+ }
+
+ return fileIndex; //m_files.size();
+ }
+
+ @Override
+ protected void onPostExecute(Integer result) {
+ if (isAdded() && m_adapter != null) {
+ m_adapter.changeCursor(createCursor());
+ m_adapter.notifyDataSetChanged();
+ }
+ }
+ };
+
+ String comicsDir = m_prefs.getString("comics_directory", null);
+
+ if (comicsDir != null && m_activity.isStorageAvailable()) {
+ 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);
+ //out.putStringArrayList("files", m_files);
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicPager.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicPager.java
new file mode 100644
index 0000000..4371df4
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ComicPager.java
@@ -0,0 +1,166 @@
+package org.fox.ttcomics;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+import com.github.junrar.exception.RarException;
+
+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.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+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 class PagerAdapter extends FragmentStatePagerAdapter {
+ public PagerAdapter(FragmentManager fm) {
+ super(fm);
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ ComicFragment cf = new ComicFragment();
+ cf.setPage(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);
+ } 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);
+ } else {
+ m_archive = new CbrComicArchive(m_fileName);
+ }
+
+ int position = m_activity.getLastPosition(m_fileName);
+
+ pager.setAdapter(m_adapter);
+ pager.setCurrentItem(position);
+
+ m_activity.onComicSelected(m_fileName, position);
+ m_activity.setProgress(Math.round(((float)position / (float)(m_archive.getCount()-1)) * 10000));
+ m_activity.hideSystemUiIfNecessary();
+
+ } catch (IOException e) {
+ m_activity.toast(R.string.error_could_not_open_comic_archive);
+ e.printStackTrace();
+ } catch (RarException 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_activity.setProgress(Math.round(((float)position / (float)(m_archive.getCount()-1)) * 10000));
+
+ m_activity.hideSystemUiIfNecessary();
+ }
+
+ 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/ttcomics/CommonActivity.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/CommonActivity.java
new file mode 100644
index 0000000..6d392d2
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/CommonActivity.java
@@ -0,0 +1,653 @@
+package org.fox.ttcomics;
+
+
+import java.io.File;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Date;
+
+import com.readystatesoftware.systembartint.SystemBarTintManager;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteStatement;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+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.util.TypedValue;
+import android.view.Display;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Toast;
+
+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);
+
+ 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);
+ }
+ }
+
+ public static boolean isCompatMode() {
+ return android.os.Build.VERSION.SDK_INT <= 10;
+ }
+
+ 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_donate:
+ if (true) {
+ try {
+ Intent intent = new Intent(Intent.ACTION_VIEW,
+ Uri.parse("market://details?id=org.fox.ttcomics.donation"));
+ startActivity(intent);
+ } catch (ActivityNotFoundException ae) {
+ try {
+ Intent intent = new Intent(Intent.ACTION_VIEW,
+ Uri.parse("https://play.google.com/store/apps/details?id=org.fox.ttcomics.donation"));
+ startActivity(intent);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+
+ return true;
+ 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 showSystemUiIfNecessary() {
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
+ if (m_prefs.getBoolean("use_full_screen", false)) {
+ getWindow().getDecorView().setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
+ } else {
+ getWindow().getDecorView().setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_VISIBLE);
+ }
+ }
+ }
+
+ public void setStatusBarTint() {
+ if (android.os.Build.VERSION.SDK_INT == android.os.Build.VERSION_CODES.KITKAT) {
+ SystemBarTintManager tintManager = new SystemBarTintManager(this);
+ // enable status bar tint
+ tintManager.setStatusBarTintEnabled(true);
+ // enable navigation bar tint
+ tintManager.setNavigationBarTintEnabled(true);
+
+ TypedValue tv = new TypedValue();
+ getTheme().resolveAttribute(R.attr.statusBarHintColor, tv, true);
+
+ tintManager.setStatusBarTintColor(tv.data);
+ }
+ }
+
+ public void hideSystemUiIfNecessary() {
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
+ if (m_prefs.getBoolean("use_full_screen", false)) {
+
+ getWindow().getDecorView().setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | 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 if (m_prefs.getBoolean("dim_status_bar", false)) {
+ getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
+ }
+
+ } else {
+
+ if (!isCompatMode()) {
+ if (m_prefs.getBoolean("dim_status_bar", false)) {
+ getWindow().getDecorView().setSystemUiVisibility(View.STATUS_BAR_HIDDEN);
+ }
+ }
+
+ if (m_prefs.getBoolean("use_full_screen", false)) {
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+ WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ getSupportActionBar().hide();
+ }
+
+ }
+
+
+ }
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics/DatabaseHelper.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/DatabaseHelper.java
new file mode 100644
index 0000000..15b7a25
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/DatabaseHelper.java
@@ -0,0 +1,68 @@
+package org.fox.ttcomics;
+
+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/ttcomics/DirectoryPicker.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/DirectoryPicker.java
new file mode 100644
index 0000000..d607b48
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/DirectoryPicker.java
@@ -0,0 +1,169 @@
+package org.fox.ttcomics;
+
+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/ttcomics/MainActivity.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/MainActivity.java
new file mode 100644
index 0000000..0e589fa
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/MainActivity.java
@@ -0,0 +1,253 @@
+package org.fox.ttcomics;
+
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import android.animation.LayoutTransition;
+import android.annotation.SuppressLint;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v4.widget.SwipeRefreshLayout;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.ActionBar.Tab;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.ShareActionProvider;
+
+public class MainActivity extends CommonActivity {
+ private final String TAG = this.getClass().getSimpleName();
+
+ private TabListener m_tabListener;
+ private int m_selectedTab;
+ private String m_baseDirectory = "";
+ private String m_fileName = "";
+
+ @SuppressLint("NewApi")
+ private class TabListener implements ActionBar.TabListener {
+
+ @Override
+ public void onTabReselected(Tab tab, FragmentTransaction ft) {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
+ public void onTabSelected(Tab tab, FragmentTransaction ft) {
+ 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 onTabUnselected(Tab tab, FragmentTransaction ft) {
+ // TODO Auto-generated method stub
+
+ }
+
+ }
+
+ @SuppressLint("NewApi")
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ requestWindowFeature(Window.FEATURE_PROGRESS);
+
+ setContentView(R.layout.activity_main);
+
+ setProgressBarVisibility(false);
+
+ setStatusBarTint();
+ setSmallScreen(findViewById(R.id.tablet_layout_hack) == null);
+
+ 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");
+ }
+
+ m_tabListener = new TabListener();
+
+ ActionBar actionBar = getSupportActionBar();
+
+ actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+
+ actionBar.addTab(getSupportActionBar().newTab()
+ .setText(R.string.tab_all_comics)
+ .setTabListener(m_tabListener));
+
+ actionBar.addTab(getSupportActionBar().newTab()
+ .setText(R.string.tab_unread)
+ .setTabListener(m_tabListener));
+
+ actionBar.addTab(getSupportActionBar().newTab()
+ .setText(R.string.tab_unfinished)
+ .setTabListener(m_tabListener));
+
+ actionBar.addTab(getSupportActionBar().newTab()
+ .setText(R.string.tab_read)
+ .setTabListener(m_tabListener));
+
+ if (savedInstanceState != null) {
+ m_selectedTab = savedInstanceState.getInt("selectedTab");
+ } else {
+ m_selectedTab = getIntent().getIntExtra("selectedTab", 0);
+ }
+
+ actionBar.selectTab(actionBar.getTabAt(m_selectedTab));
+
+ 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();
+ }
+
+ if (!isCompatMode()) {
+ ((ViewGroup)findViewById(R.id.comics_list)).setLayoutTransition(new LayoutTransition());
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.activity_main, menu);
+
+ boolean isDonationFound = getPackageManager().checkSignatures(
+ getPackageName(), "org.fox.ttcomics.donation") == PackageManager.SIGNATURE_MATCH;
+
+ if (isDonationFound)
+ menu.findItem(R.id.menu_donate).setVisible(false);
+
+ 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);
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics/PreferencesActivity.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/PreferencesActivity.java
new file mode 100644
index 0000000..7aa24a5
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/PreferencesActivity.java
@@ -0,0 +1,124 @@
+package org.fox.ttcomics;
+
+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.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceCategory;
+import android.preference.PreferenceManager;
+import android.widget.Toast;
+
+public class PreferencesActivity extends PreferenceActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final SharedPreferences prefs = PreferenceManager
+ .getDefaultSharedPreferences(getApplicationContext());
+
+ addPreferencesFromResource(R.xml.preferences);
+
+ if (CommonActivity.isCompatMode()) {
+ Preference dimPref = findPreference("dim_status_bar");
+ PreferenceCategory readingCat = (PreferenceCategory) findPreference("prefs_reading");
+ readingCat.removePreference(dimPref);
+ }
+
+ 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;
+ }
+ });
+ }
+
+ @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/ttcomics/SyncClient.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/SyncClient.java
new file mode 100644
index 0000000..f67d01b
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/SyncClient.java
@@ -0,0 +1,159 @@
+package org.fox.ttcomics;
+
+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);
+ }
+ }
+
+ 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/ttcomics/ViewComicActivity.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ViewComicActivity.java
new file mode 100644
index 0000000..6ccc0a5
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ViewComicActivity.java
@@ -0,0 +1,391 @@
+package org.fox.ttcomics;
+
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+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.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.net.Uri;
+import android.os.Bundle;
+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.Window;
+import android.view.WindowManager;
+import android.widget.NumberPicker;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+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) {
+ super.onCreate(savedInstanceState);
+
+ if (!isCompatMode())
+ setTheme(m_prefs.getBoolean("use_dark_theme", false) ? R.style.ViewDarkTheme : R.style.ViewLightTheme);
+
+ requestWindowFeature(Window.FEATURE_PROGRESS);
+
+ if (m_prefs.getBoolean("prevent_screen_sleep", false))
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+
+ /* if (android.os.Build.VERSION.SDK_INT == android.os.Build.VERSION_CODES.KITKAT && !m_prefs.getBoolean("use_full_screen", false)) {
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+ setStatusBarTint();
+ } */
+
+ 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());
+
+ /* if (m_prefs.getBoolean("use_full_screen", false)) {
+ //getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+ // WindowManager.LayoutParams.FLAG_FULLSCREEN);
+
+ //getSupportActionBar().hide();
+ } */
+
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT && m_prefs.getBoolean("use_dark_theme", false)) {
+ getSupportActionBar().setBackgroundDrawable(new ColorDrawable(Color.BLACK));
+ }
+ }
+
+ @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", ".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 (!isCompatMode()) {
+ 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();
+ } else {
+ LayoutInflater inflater = getLayoutInflater();
+ final View contentView = inflater.inflate(R.layout.dialog_location_compat, null);
+
+ final SeekBar seeker = (SeekBar) contentView.findViewById(R.id.number_seeker);
+ final TextView pageNum = (TextView) contentView.findViewById(R.id.page_number);
+ final int size = getSize(m_fileName);
+
+ seeker.setMax(size-1);
+ seeker.setProgress(cp.getPosition());
+
+ pageNum.setText(getString(R.string.dialog_location_page_number, cp.getPosition()+1, size));
+
+ seeker.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener(){
+
+ @Override
+ public void onProgressChanged(
+ SeekBar seekBar, int progress,
+ boolean fromUser) {
+
+ pageNum.setText(getString(R.string.dialog_location_page_number, progress+1, size));
+
+ }
+
+ @Override
+ public void onStartTrackingTouch(
+ SeekBar arg0) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void onStopTrackingTouch(
+ SeekBar arg0) {
+ // TODO Auto-generated method stub
+
+ }
+
+
+ });
+
+ 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(seeker.getProgress());
+
+ }
+ });
+
+ 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);
+ }
+ }
+
+}
diff --git a/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ViewPager.java b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ViewPager.java
new file mode 100644
index 0000000..a81261d
--- /dev/null
+++ b/org.fox.ttcomics/src/main/java/org/fox/ttcomics/ViewPager.java
@@ -0,0 +1,38 @@
+package org.fox.ttcomics;
+
+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