diff --git a/.classpath b/.classpath index 609aa00..7bc01d9 100644 --- a/.classpath +++ b/.classpath @@ -3,5 +3,7 @@ - + + + diff --git a/AndroidManifest.xml b/AndroidManifest.xml index ca5f575..4ac8205 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -6,35 +6,30 @@ + android:targetSdkVersion="18" /> - - - + diff --git a/README.txt b/README.txt index 5a91d73..465fa9b 100644 --- a/README.txt +++ b/README.txt @@ -1,7 +1,7 @@ README ======= -Copyright (C) 2011-2012 Takahiro Yoshimura +Copyright (C) 2011-2013 Takahiro Yoshimura This is a simple text-like file viewer application for the Android platform. It is also my learning project. @@ -34,10 +34,11 @@ It is available at: https://gist.github.com/1223663 . Thanks to the jchardet project (http://jchardet.sourceforge.net/). - * 2 color themes + * 4 color themes White-on-black (energy saving on OLED display, and eye saver under dark conditions) and black-on-white are supported. + Update: Solarized Color theme (dark & light) * Configurable font size @@ -46,6 +47,10 @@ It is available at: https://gist.github.com/1223663 . * Words-searching/finding * Selectable/copyable text (only in Honeycomb or above) + + * Now, it can choose text file from installed file manager + + * Recent files 2. BUGS @@ -64,3 +69,8 @@ Character set detection is done with the jchardet project Word searching, text selection/copy feature is kindly contributed by Renjaya Raga Zenta . + +Solarized Color: Precision colors for machines and people +(http://ethanschoonover.com/solarized) + +Saving recent files use ObjectSerializer (https://github.com/apache/pig) diff --git a/project.properties b/project.properties index 5b44c8c..f5544e2 100644 --- a/project.properties +++ b/project.properties @@ -11,4 +11,5 @@ proguard.config=etc/proguard/android.txt:etc/proguard/project.txt # Project target. -target=android-17 +target=android-18 +android.library.reference.1=../../android-sdk-linux/extras/android/support/v7/appcompat diff --git a/res/drawable-hdpi-v11/cancel.png b/res/drawable-hdpi-v11/cancel.png deleted file mode 100644 index 094eea5..0000000 Binary files a/res/drawable-hdpi-v11/cancel.png and /dev/null differ diff --git a/res/drawable-hdpi-v11/next.png b/res/drawable-hdpi-v11/next.png deleted file mode 100644 index 50b1587..0000000 Binary files a/res/drawable-hdpi-v11/next.png and /dev/null differ diff --git a/res/drawable-hdpi-v11/previous.png b/res/drawable-hdpi-v11/previous.png deleted file mode 100644 index 64538ce..0000000 Binary files a/res/drawable-hdpi-v11/previous.png and /dev/null differ diff --git a/res/drawable-hdpi-v11/action_search.png b/res/drawable-hdpi/action_search.png similarity index 100% rename from res/drawable-hdpi-v11/action_search.png rename to res/drawable-hdpi/action_search.png diff --git a/res/drawable-hdpi/cancel.png b/res/drawable-hdpi/cancel.png index cde36e1..094eea5 100644 Binary files a/res/drawable-hdpi/cancel.png and b/res/drawable-hdpi/cancel.png differ diff --git a/res/drawable-hdpi/drawer_shadow.9.png b/res/drawable-hdpi/drawer_shadow.9.png new file mode 100644 index 0000000..224cc4f Binary files /dev/null and b/res/drawable-hdpi/drawer_shadow.9.png differ diff --git a/res/drawable-hdpi/ic_drawer.png b/res/drawable-hdpi/ic_drawer.png new file mode 100644 index 0000000..ff7b1de Binary files /dev/null and b/res/drawable-hdpi/ic_drawer.png differ diff --git a/res/drawable-hdpi/next.png b/res/drawable-hdpi/next.png index e6495b2..50b1587 100644 Binary files a/res/drawable-hdpi/next.png and b/res/drawable-hdpi/next.png differ diff --git a/res/drawable-hdpi/previous.png b/res/drawable-hdpi/previous.png index 23778ae..64538ce 100644 Binary files a/res/drawable-hdpi/previous.png and b/res/drawable-hdpi/previous.png differ diff --git a/res/drawable-mdpi-v11/cancel.png b/res/drawable-mdpi-v11/cancel.png deleted file mode 100644 index 3336760..0000000 Binary files a/res/drawable-mdpi-v11/cancel.png and /dev/null differ diff --git a/res/drawable-mdpi-v11/next.png b/res/drawable-mdpi-v11/next.png deleted file mode 100644 index 3fa4887..0000000 Binary files a/res/drawable-mdpi-v11/next.png and /dev/null differ diff --git a/res/drawable-mdpi-v11/previous.png b/res/drawable-mdpi-v11/previous.png deleted file mode 100644 index b8b3e1a..0000000 Binary files a/res/drawable-mdpi-v11/previous.png and /dev/null differ diff --git a/res/drawable-mdpi-v11/action_search.png b/res/drawable-mdpi/action_search.png similarity index 100% rename from res/drawable-mdpi-v11/action_search.png rename to res/drawable-mdpi/action_search.png diff --git a/res/drawable-mdpi/cancel.png b/res/drawable-mdpi/cancel.png index 9f4c3d6..3336760 100644 Binary files a/res/drawable-mdpi/cancel.png and b/res/drawable-mdpi/cancel.png differ diff --git a/res/drawable-mdpi/drawer_shadow.9.png b/res/drawable-mdpi/drawer_shadow.9.png new file mode 100644 index 0000000..3797f99 Binary files /dev/null and b/res/drawable-mdpi/drawer_shadow.9.png differ diff --git a/res/drawable-mdpi/ic_drawer.png b/res/drawable-mdpi/ic_drawer.png new file mode 100644 index 0000000..fb681ba Binary files /dev/null and b/res/drawable-mdpi/ic_drawer.png differ diff --git a/res/drawable-mdpi/next.png b/res/drawable-mdpi/next.png index 88029a8..3fa4887 100644 Binary files a/res/drawable-mdpi/next.png and b/res/drawable-mdpi/next.png differ diff --git a/res/drawable-mdpi/previous.png b/res/drawable-mdpi/previous.png index 8d19e39..b8b3e1a 100644 Binary files a/res/drawable-mdpi/previous.png and b/res/drawable-mdpi/previous.png differ diff --git a/res/layout-v11/main.xml b/res/layout-v11/viewer.xml similarity index 74% rename from res/layout-v11/main.xml rename to res/layout-v11/viewer.xml index c435ea7..e359b67 100644 --- a/res/layout-v11/main.xml +++ b/res/layout-v11/viewer.xml @@ -1,20 +1,39 @@ + android:layout_width="match_parent" + android:layout_height="match_parent" > + + + + + + android:background="?attr/dividerHorizontal" /> + android:background="?attr/dividerHorizontal" /> - - - - - \ No newline at end of file diff --git a/res/layout/drawer_list_item.xml b/res/layout/drawer_list_item.xml new file mode 100644 index 0000000..86f6ca7 --- /dev/null +++ b/res/layout/drawer_list_item.xml @@ -0,0 +1,14 @@ + + + + \ No newline at end of file diff --git a/res/layout/main.xml b/res/layout/main.xml index f4714aa..d4911d4 100644 --- a/res/layout/main.xml +++ b/res/layout/main.xml @@ -1,66 +1,30 @@ - - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/res/layout/viewer.xml b/res/layout/viewer.xml new file mode 100644 index 0000000..3554bb2 --- /dev/null +++ b/res/layout/viewer.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/menu-v11/main.xml b/res/menu-v11/main.xml deleted file mode 100644 index f85b485..0000000 --- a/res/menu-v11/main.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/res/menu/main.xml b/res/menu/main.xml index 7881488..67956a1 100644 --- a/res/menu/main.xml +++ b/res/menu/main.xml @@ -2,12 +2,13 @@ + android:id="@+id/menu_main_open" + android:title="@string/menu_title_main_open"/> + \ No newline at end of file diff --git a/res/menu/viewer.xml b/res/menu/viewer.xml new file mode 100644 index 0000000..4468da5 --- /dev/null +++ b/res/menu/viewer.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/res/values-ja/arrays.xml b/res/values-ja/arrays.xml index 1aef267..dca4e91 100644 --- a/res/values-ja/arrays.xml +++ b/res/values-ja/arrays.xml @@ -3,6 +3,8 @@ 黒地に白 白地に黒 + Solarizedさ暗い + Solarizedさ光 1画面 diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index 189fbe4..14d1a73 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -1,12 +1,15 @@ テキスト見本です。\nファイルマネージャなどのアプリを使用して、表示したいファイルを読み込んで下さい。 + テキストファイルをブラウズ + 最近使ったファイル 設定 - 探す + オープンファイル + 明確な最近のファイル + 探す 文字の配色 文字のサイズ 等幅フォントを使用する - タイトルバーを表示する 音量キーでスクロール スクロール幅 主な言語 diff --git a/res/values-v11/styles.xml b/res/values-v11/styles.xml deleted file mode 100644 index 7234bfc..0000000 --- a/res/values-v11/styles.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/res/values-v14/styles.xml b/res/values-v14/styles.xml deleted file mode 100644 index 7234bfc..0000000 --- a/res/values-v14/styles.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/res/values/arrays.xml b/res/values/arrays.xml index 466c05c..d821dba 100644 --- a/res/values/arrays.xml +++ b/res/values/arrays.xml @@ -3,10 +3,14 @@ White on black Black on white + Solarized Dark + Solarized Light black white + solarized_dark + solarized_light 8 diff --git a/res/values/strings.xml b/res/values/strings.xml index 8209322..3898d5f 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1,14 +1,19 @@ Text Viewer - 0.1.8 + 0.2.0 This is an example text.\nPlease use something like file-manager to open a text-like file. - Preferences - Search + Browse text files + Recent files + Settings + Open File + Clear Recent Files + Search + Open navigation drawer + Close navigation drawer Color theme Font size Use monospace fonts - Show title bar Scroll by volume keys Scroll height Preferred Lang. diff --git a/res/values/styles.xml b/res/values/styles.xml index 66e522e..ed1a705 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -1,6 +1,32 @@ - + - + + + + + + + + + #333333 + #99000000 \ No newline at end of file diff --git a/res/xml/config.xml b/res/xml/config.xml index 3820874..5d9d891 100644 --- a/res/xml/config.xml +++ b/res/xml/config.xml @@ -11,16 +11,12 @@ - = Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + getActionBar().setHomeButtonEnabled(true); + } this.colortheme = (ListPreference) getPreferenceScreen() .findPreference(ConfigKey.COLORTHEME); @@ -44,6 +54,7 @@ protected void onCreate(Bundle savedInstanceState) { .findPreference(ConfigKey.CHARSET_PREFERENCE); } + @SuppressWarnings("deprecation") @Override protected void onResume() { super.onResume(); @@ -58,6 +69,7 @@ protected void onResume() { sharedPreferences.registerOnSharedPreferenceChangeListener(this); } + @SuppressWarnings("deprecation") @Override protected void onPause() { super.onPause(); @@ -85,4 +97,14 @@ private void updateSummary(SharedPreferences sharedPreferences, String key) { this.charsetpreference .setSummary(this.charsetpreference.getEntry()); } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home) { + finish(); + return true; + } + return super.onOptionsItemSelected(item); + } + } diff --git a/src/com/gmail/altakey/dawne/NewMainActivity.java b/src/com/gmail/altakey/dawne/NewMainActivity.java new file mode 100644 index 0000000..ad421e0 --- /dev/null +++ b/src/com/gmail/altakey/dawne/NewMainActivity.java @@ -0,0 +1,340 @@ +/** + * Copyright (C) 2011-2013 Takahiro Yoshimura, Renjaya Raga Zenta + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.gmail.altakey.dawne; + +import android.app.Activity; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.content.res.Configuration; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.preference.PreferenceManager; +import android.support.v4.app.ActionBarDrawerToggle; +import android.support.v4.app.FragmentManager; +import android.support.v4.view.GravityCompat; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.app.ActionBar; +import android.support.v7.app.ActionBarActivity; +import android.util.Log; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.webkit.MimeTypeMap; +import android.widget.AdapterView; +import android.widget.Button; +import android.widget.ListView; +import android.widget.Toast; + +import com.gmail.altakey.dawne.ViewerFragment.OnContentTapListener; +import com.gmail.altakey.dawne.util.ConfigKey; +import com.gmail.altakey.dawne.util.ObjectSerializer; +import com.gmail.altakey.dawne.util.RecentFile; +import com.gmail.altakey.dawne.util.StackRecentFiles; + +import java.io.IOException; + +public class NewMainActivity extends ActionBarActivity implements OnContentTapListener { + + private static final int BROWSE_FILE_REQUEST_CODE = 4869; + private static final String TAG = "dawne"; + + private ActionBar mActionBar; + private DrawerLayout mDrawerLayout; + private ListView mDrawerList; + private ActionBarDrawerToggle mDrawerToggle; + private StackRecentFiles mStackRecentFiles; + private RecentFilesAdapter mDrawerAdapter; + + private Handler mHandler; + private final Runnable mHideActionBarDelayed = new Runnable() { + @Override + public void run() { + mActionBar.hide(); + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + + mHandler = new Handler(); + + SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this); + String recentFiles = pref.getString(ConfigKey.RECENT_FILES, null); + if (recentFiles == null || recentFiles.equals("")) { + mStackRecentFiles = new StackRecentFiles(); + } else { + try { + mStackRecentFiles = (StackRecentFiles) ObjectSerializer.deserialize(recentFiles); + } catch (IOException e) { + e.printStackTrace(); + } + } + + mActionBar = getSupportActionBar(); + mActionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE + | ActionBar.DISPLAY_HOME_AS_UP); + + mDrawerAdapter = new RecentFilesAdapter(this, mStackRecentFiles); + mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); + mDrawerList = (ListView) findViewById(R.id.left_drawer); + mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); + mDrawerList.setAdapter(mDrawerAdapter); + mDrawerList.setOnItemClickListener(new DrawerItemClickListener()); + mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.drawable.ic_drawer, + R.string.drawer_open, R.string.drawer_close) { + + @Override + public void onDrawerClosed(View drawerView) { + final ViewerFragment f = (ViewerFragment) getSupportFragmentManager() + .findFragmentById(R.id.content_frame); + if (f != null) { + if (f.isSearching()) { + mActionBar.hide(); + } else { + hideActionBarDelayed(); + } + mActionBar.setTitle(""); + } else { + mActionBar.setTitle(getTitle()); + } + supportInvalidateOptionsMenu(); + + } + + @Override + public void onDrawerOpened(View drawerView) { + stopHideActionBarDelayed(); + if (!mActionBar.isShowing()) { + mActionBar.show(); + } + mActionBar.setTitle(R.string.recent_file); + supportInvalidateOptionsMenu(); + } + + }; + mDrawerLayout.setDrawerListener(mDrawerToggle); + + final Intent intent = getIntent(); + final String action = intent.getAction(); + final String type = intent.getType(); + if (Intent.ACTION_VIEW.equals(action) && type != null) { + if (type.startsWith("text/")) { + handleTextFile(intent.getData()); + } + } + + final Button buttonBrowse = (Button) findViewById(R.id.buttonBrowse); + buttonBrowse.setOnClickListener(new OnClickListener() { + + @Override + public void onClick(View v) { + browseFile(); + } + }); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.main, menu); + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + menu.findItem(R.id.menu_main_clear_recent).setEnabled(!mDrawerAdapter.isEmpty()); + return super.onPrepareOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (mDrawerToggle.onOptionsItemSelected(item)) { + return true; + } + switch (item.getItemId()) { + case R.id.menu_main_open: + browseFile(); + return true; + case R.id.menu_main_clear_recent: + clearRecentFiles(); + return true; + case R.id.menu_main_config: + startActivity(new Intent(this, ConfigActivity.class)); + return true; + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + mDrawerToggle.onConfigurationChanged(newConfig); + } + + @Override + protected void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + mDrawerToggle.syncState(); + } + + @Override + protected void onResume() { + super.onResume(); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == BROWSE_FILE_REQUEST_CODE) { + if (resultCode == Activity.RESULT_OK) { + final Uri uri = data.getData(); + final String intentType = data.getType(); + final String mimeType = getContentResolver().getType(uri); + final String extension = MimeTypeMap.getFileExtensionFromUrl(uri.toString()); + Log.d(TAG, "File type: " + intentType + "::" + extension + "::" + mimeType); + if ((intentType != null && intentType.startsWith("text/")) || + (mimeType != null && mimeType.startsWith("text/")) || + (extension != null && extension.equals("txt"))) { + final ViewerFragment f = ViewerFragment.newInstance(uri.toString()); + getSupportFragmentManager().beginTransaction().replace(R.id.content_frame, f) + .commit(); + // Save recent file + saveRecentFiles(uri); + hideActionBarDelayed(); + Log.d(TAG, "Opening file: " + uri.toString()); + } else { + Toast.makeText(this, "Unsupported file type", Toast.LENGTH_LONG).show(); + } + + } + } + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this); + boolean scrollerEnabled = pref.getBoolean(ConfigKey.SCROLL_BY_VOLUME_KEYS, false); + + if (scrollerEnabled) { + int divisor = Integer.parseInt(pref.getString(ConfigKey.SCROLL_LINES, "2")); + + int action = event.getAction(); + int keyCode = event.getKeyCode(); + final FragmentManager fm = getSupportFragmentManager(); + final ViewerFragment f = (ViewerFragment) fm.findFragmentById(R.id.content_frame); + if (f != null) { + switch (keyCode) { + case KeyEvent.KEYCODE_VOLUME_UP: + if (action == KeyEvent.ACTION_DOWN) { + f.onVolumeKeyUp(divisor); + } + return true; + case KeyEvent.KEYCODE_VOLUME_DOWN: + if (action == KeyEvent.ACTION_DOWN) { + f.onVolumeKeyDown(divisor); + } + return true; + } + } + } + return super.dispatchKeyEvent(event); + } + + void browseFile() { + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("text/*"); + if (intent.resolveActivity(getPackageManager()) != null) { + startActivityForResult(intent, BROWSE_FILE_REQUEST_CODE); + } else { + Toast.makeText(this, "Sorry, there's no file browser available", Toast.LENGTH_LONG) + .show(); + } + } + + void handleTextFile(Uri uri) { + final ViewerFragment f = ViewerFragment.newInstance(uri.toString()); + getSupportFragmentManager().beginTransaction().replace(R.id.content_frame, f).commit(); + // Save recent file + saveRecentFiles(uri); + Log.d(TAG, "Viewing file: " + uri.getLastPathSegment()); + hideActionBarDelayed(); + } + + void saveRecentFiles(Uri uri) { + mStackRecentFiles.pushRecentFile(new RecentFile(uri.getLastPathSegment(), uri.toString())); + mDrawerAdapter.notifyDataSetChanged(); + SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this); + Editor editor = pref.edit(); + try { + editor.putString(ConfigKey.RECENT_FILES, ObjectSerializer.serialize(mStackRecentFiles)); + } catch (IOException e) { + e.printStackTrace(); + } + editor.commit(); + } + + void clearRecentFiles() { + mStackRecentFiles.clear(); + mDrawerAdapter.notifyDataSetChanged(); + SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this); + pref.edit().putString(ConfigKey.RECENT_FILES, "").commit(); + supportInvalidateOptionsMenu(); + } + + void hideActionBarDelayed() { + mHandler.postDelayed(mHideActionBarDelayed, 5000); + } + + void stopHideActionBarDelayed() { + mHandler.removeCallbacks(mHideActionBarDelayed); + } + + boolean isDrawerOpen() { + return mDrawerLayout.isDrawerOpen(mDrawerList); + } + + private class DrawerItemClickListener implements ListView.OnItemClickListener { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + final String uriString = ((RecentFile) mDrawerAdapter.getItem(position)).getUri(); + Log.d(TAG, "Re-opening file: " + uriString); + final ViewerFragment f = ViewerFragment.newInstance(uriString); + getSupportFragmentManager().beginTransaction().replace(R.id.content_frame, f) + .commit(); + mDrawerLayout.closeDrawer(mDrawerList); + // Save recent file + saveRecentFiles(Uri.parse(uriString)); + } + } + + @Override + public void onContentTap() { + if (!mActionBar.isShowing()) { + mActionBar.show(); + hideActionBarDelayed(); + } + } +} diff --git a/src/com/gmail/altakey/dawne/RecentFilesAdapter.java b/src/com/gmail/altakey/dawne/RecentFilesAdapter.java new file mode 100644 index 0000000..bacb56b --- /dev/null +++ b/src/com/gmail/altakey/dawne/RecentFilesAdapter.java @@ -0,0 +1,74 @@ +/** + * Copyright (C) 2013 Takahiro Yoshimura, Renjaya Raga Zenta + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.gmail.altakey.dawne; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.TextView; + +import com.gmail.altakey.dawne.util.StackRecentFiles; + +public class RecentFilesAdapter extends BaseAdapter { + + private StackRecentFiles mStack; + private LayoutInflater mInflater; + + public RecentFilesAdapter(Context context, StackRecentFiles stack) { + mStack = stack; + mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + } + + @Override + public int getCount() { + return mStack.size(); + } + + @Override + public Object getItem(int position) { + return mStack.getRecentFileAt(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View view = convertView; + ViewHolder holder; + if (view == null) { + view = mInflater.inflate(R.layout.drawer_list_item, parent, false); + holder = new ViewHolder(); + holder.text = (TextView) view.findViewById(android.R.id.text1); + view.setTag(holder); + } else { + holder = (ViewHolder) view.getTag(); + } + holder.text.setText(mStack.getRecentFileAt(position).getFileName()); + return view; + } + + static class ViewHolder { + TextView text; + } + +} diff --git a/src/com/gmail/altakey/dawne/ViewActivity.java b/src/com/gmail/altakey/dawne/ViewActivity.java deleted file mode 100644 index a609ec0..0000000 --- a/src/com/gmail/altakey/dawne/ViewActivity.java +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Copyright (C) 2011-2012 Takahiro Yoshimura - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.gmail.altakey.dawne; - -import android.app.ProgressDialog; -import android.content.Context; -import android.content.SharedPreferences; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.text.Selection; -import android.text.Spannable; -import android.view.KeyEvent; -import android.widget.EditText; -import android.widget.ScrollView; - -public class ViewActivity extends MainActivity { - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - SharedPreferences pref = PreferenceManager - .getDefaultSharedPreferences(this); - - final TextLoaderParam param = new TextLoaderParam(); - param.context = getApplicationContext(); - param.uri = getIntent().getData(); - param.charset = pref - .getString(ConfigKey.CHARSET_PREFERENCE, "japanese"); - new LoadTextTask().execute(param); - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) { - SharedPreferences pref = PreferenceManager - .getDefaultSharedPreferences(this); - boolean scrollerEnabled = pref.getBoolean( - ConfigKey.SCROLL_BY_VOLUME_KEYS, false); - - if (scrollerEnabled) { - int divisor = Integer.parseInt(pref.getString( - ConfigKey.SCROLL_LINES, "2")); - - int action = event.getAction(); - int keyCode = event.getKeyCode(); - - final TextPager textPager = TextPager.create(this.textView, - (ScrollView) this.rootView, divisor); - switch (keyCode) { - case KeyEvent.KEYCODE_VOLUME_UP: - if (action == KeyEvent.ACTION_DOWN) - textPager.up(); - return true; - case KeyEvent.KEYCODE_VOLUME_DOWN: - if (action == KeyEvent.ACTION_DOWN) - textPager.down(); - return true; - } - } - - return super.dispatchKeyEvent(event); - } - - private class LoadTextTask extends AsyncTask { - - ProgressDialog progressDialog; - - @Override - protected void onPreExecute() { - ViewActivity.this.textView.setText(""); - progressDialog = ProgressDialog.show(ViewActivity.this, "", - "Loading ...", true); - } - - @Override - protected String doInBackground(TextLoaderParam... params) { - final TextLoaderParam param = params[0]; - return TextLoader.create(param.context, param.uri, param.charset) - .read(); - } - - @Override - protected void onPostExecute(String result) { - ViewActivity.this.textView.setText(result); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - Selection.setSelection( - (Spannable) ViewActivity.this.textView.getText(), - selectionStart, selectionEnd); - } else { - ((EditText) ViewActivity.this.textView).setSelection( - ViewActivity.this.selectionStart, - ViewActivity.this.selectionEnd); - } - progressDialog.dismiss(); - } - - } - - static class TextLoaderParam { - Context context; - Uri uri; - String charset; - } -} diff --git a/src/com/gmail/altakey/dawne/MainActivity.java b/src/com/gmail/altakey/dawne/ViewerFragment.java similarity index 58% rename from src/com/gmail/altakey/dawne/MainActivity.java rename to src/com/gmail/altakey/dawne/ViewerFragment.java index d2d8949..55ac6e9 100644 --- a/src/com/gmail/altakey/dawne/MainActivity.java +++ b/src/com/gmail/altakey/dawne/ViewerFragment.java @@ -1,5 +1,5 @@ /** - * Copyright (C) 2011-2012 Takahiro Yoshimura + * Copyright (C) 2011-2013 Takahiro Yoshimura, Renjaya Raga Zenta * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,38 +18,51 @@ package com.gmail.altakey.dawne; import android.annotation.SuppressLint; -import android.app.ActionBar; import android.app.Activity; +import android.app.ProgressDialog; import android.content.Context; -import android.content.Intent; import android.content.SharedPreferences; +import android.net.Uri; +import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; +import android.support.v4.app.Fragment; +import android.support.v7.app.ActionBarActivity; import android.text.Selection; import android.text.Spannable; +import android.util.Log; import android.view.KeyEvent; +import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; -import android.view.Window; +import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import android.widget.ImageButton; +import android.widget.ScrollView; import android.widget.TextView; -public class MainActivity extends Activity { - protected View rootView; - protected TextView textView; - protected View searchBar; - protected EditText searchField; - protected int selectionStart; - protected int selectionEnd; - private boolean titleHidden; +import com.gmail.altakey.dawne.util.ConfigKey; +import com.gmail.altakey.dawne.util.TextLoader; +import com.gmail.altakey.dawne.util.TextPager; +import com.gmail.altakey.dawne.util.TextStyler; - private String currentCharsetpreference; +public class ViewerFragment extends Fragment { + + private View scrollView; + private TextView textView; + private View searchBar; + private EditText searchField; + private int selectionStart; + private int selectionEnd; + private Uri mUri; + private OnContentTapListener mCallback; + private boolean isSearching = false; + private String currentCharsetPreference; private final View.OnClickListener cancelButtonListener = new View.OnClickListener() { @@ -72,11 +85,25 @@ public void onClick(View v) { searchNext(); } }; + private final View.OnTouchListener contentTouchListener = new View.OnTouchListener() { + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (!isSearching) { + mCallback.onContentTap(); + } + // call super listener (for selecting text) + return false; + } + }; private final View.OnTouchListener dummyTouchListener = new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { - // do nothing + if (!isSearching) { + mCallback.onContentTap(); + } + // ignore super listener return true; } }; @@ -113,166 +140,179 @@ public boolean onKey(View v, int keyCode, KeyEvent event) { } }; - /** Called when the activity is first created. */ - @SuppressLint("NewApi") + public ViewerFragment() { + } + + public static ViewerFragment newInstance(String uriString) { + final ViewerFragment f = new ViewerFragment(); + final Bundle args = new Bundle(1); + args.putString("uri", uriString); + f.setArguments(args); + return f; + } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - this.setupWindow(); + mUri = getArguments() != null ? Uri.parse(getArguments().getString("uri")) : null; + setHasOptionsMenu(true); + } - setContentView(R.layout.main); + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + try { + mCallback = (OnContentTapListener) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + + " must implement OnContentClickListener"); + } + } - this.rootView = findViewById(R.id.view); + @SuppressLint("NewApi") + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final View rootView = inflater.inflate(R.layout.viewer, container, false); + scrollView = rootView.findViewById(R.id.view); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // In Honeycomb or above, we can set TextView to be selectable - this.textView = (TextView) findViewById(R.id.textview); - this.textView.setTextIsSelectable(true); + textView = (TextView) rootView.findViewById(R.id.textview); + textView.setTextIsSelectable(true); + textView.setOnTouchListener(contentTouchListener); } else { // Below Honeycomb we need EditText class - this.textView = (EditText) findViewById(R.id.textview); + textView = (EditText) rootView.findViewById(R.id.textview); // this is needed to make EditText behaves like simple TextView // so the soft keyboard won't appear if user touches the text - this.textView.setOnTouchListener(dummyTouchListener); + textView.setOnTouchListener(dummyTouchListener); // this is needed to make EditText won't be changed if user tries // to edit the text with physical keyboard - this.textView.setOnKeyListener(dummyKeyListener); + textView.setOnKeyListener(dummyKeyListener); } - this.textView.setSaveEnabled(false); + textView.setSaveEnabled(false); + searchBar = rootView.findViewById(R.id.search); + searchField = (EditText) rootView.findViewById(R.id.edittext); + searchField.setOnKeyListener(searchKeyListener); - this.searchBar = findViewById(R.id.search); - this.searchField = (EditText) findViewById(R.id.edittext); - this.searchField.setOnKeyListener(searchKeyListener); - - ImageButton cancelButton = (ImageButton) findViewById(R.id.cancel); + ImageButton cancelButton = (ImageButton) rootView.findViewById(R.id.cancel); cancelButton.setOnClickListener(cancelButtonListener); - ImageButton prevButton = (ImageButton) findViewById(R.id.previous); + ImageButton prevButton = (ImageButton) rootView.findViewById(R.id.previous); prevButton.setOnClickListener(prevButtonListener); - ImageButton nextButton = (ImageButton) findViewById(R.id.next); + ImageButton nextButton = (ImageButton) rootView.findViewById(R.id.next); nextButton.setOnClickListener(nextButtonListener); - this.restyle(); + return rootView; } - @SuppressLint("InlinedApi") - protected void setupWindow() { - SharedPreferences pref = PreferenceManager - .getDefaultSharedPreferences(this); - boolean hideTitle = !pref.getBoolean(ConfigKey.SHOW_TITLE, true); - String charsetpreference = pref.getString(ConfigKey.CHARSET_PREFERENCE, - "all"); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - this.requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY); - } - if (hideTitle) { - this.requestWindowFeature(Window.FEATURE_NO_TITLE); - } - - this.titleHidden = hideTitle; - this.currentCharsetpreference = charsetpreference; - } - - private void restyle() { - SharedPreferences pref = PreferenceManager - .getDefaultSharedPreferences(this); - - String colortheme = pref.getString(ConfigKey.COLORTHEME, "black"); - int foreground = 0xffffffff; - int background = 0xff000000; - - float fontsize = Float.parseFloat(pref.getString(ConfigKey.FONTSIZE, - "14")); - boolean useMonospaceFonts = pref.getBoolean( - ConfigKey.USE_MONOSPACE_FONTS, false); - - if (colortheme.equals("white")) { - foreground = 0xff000000; - background = 0xffffffff; - } - - TextStyler.create(this.rootView, this.textView, background, foreground, - fontsize, useMonospaceFonts ? "monospace" : "default").style(); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - if (titleHidden) { - rootView.setPadding(0, 0, 0, 0); - } + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getActivity()); + String charsetPreference = pref.getString(ConfigKey.CHARSET_PREFERENCE, "all"); + currentCharsetPreference = charsetPreference; + if (savedInstanceState != null) { + selectionStart = savedInstanceState.getInt("selectionStart", 0); + selectionEnd = savedInstanceState.getInt("selectionEnd", 0); } + ((ActionBarActivity) getActivity()).getSupportActionBar().setTitle(""); + loadText(charsetPreference); } @Override public void onResume() { super.onResume(); - - SharedPreferences pref = PreferenceManager - .getDefaultSharedPreferences(this); - boolean hideTitle = !pref.getBoolean(ConfigKey.SHOW_TITLE, true); - String charsetpreference = pref.getString(ConfigKey.CHARSET_PREFERENCE, - "all"); - - if (this.titleHidden != hideTitle - || this.currentCharsetpreference != charsetpreference) { - this.restart(); - return; + SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getActivity()); + String charsetPreference = pref.getString(ConfigKey.CHARSET_PREFERENCE, "all"); + if (!currentCharsetPreference.equals(charsetPreference)) { + loadText(charsetPreference); + currentCharsetPreference = charsetPreference; } - - this.restyle(); + ((NewMainActivity) getActivity()).hideActionBarDelayed(); + restyle(); } @Override - protected void onSaveInstanceState(Bundle outState) { + public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("selectionStart", selectionStart); outState.putInt("selectionEnd", selectionEnd); } @Override - protected void onRestoreInstanceState(Bundle savedInstanceState) { - super.onRestoreInstanceState(savedInstanceState); - if (savedInstanceState != null) { - selectionStart = savedInstanceState.getInt("selectionStart", 0); - selectionEnd = savedInstanceState.getInt("selectionEnd", 0); - } + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.viewer, menu); + super.onCreateOptionsMenu(menu, inflater); } - private void restart() { - Intent intent = getIntent(); - finish(); - startActivity(intent); + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_viewer_search: + showSearchBar(true); + return true; + } + return super.onOptionsItemSelected(item); } @Override - public boolean onCreateOptionsMenu(Menu menu) { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.main, menu); - return true; + public void onPrepareOptionsMenu(Menu menu) { + menu.findItem(R.id.menu_viewer_search).setVisible( + !((NewMainActivity) getActivity()).isDrawerOpen()); + super.onPrepareOptionsMenu(menu); } - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.menu_main_search: - showSearchBar(true); - break; - case R.id.menu_main_config: - startActivity(new Intent(this, ConfigActivity.class)); - break; + private void loadText(String charsetPreference) { + final TextLoaderParam param = new TextLoaderParam(); + param.context = getActivity().getApplicationContext(); + param.uri = mUri; + param.charset = charsetPreference; + new LoadTextTask().execute(param); + } + + private void restyle() { + if (getActivity() == null) { + Log.d("dawne.Viewer", "Activity null :("); + return; } - return true; + SharedPreferences pref = PreferenceManager + .getDefaultSharedPreferences(getActivity()); + + String colortheme = pref.getString(ConfigKey.COLORTHEME, "black"); + int foreground = 0xffffffff; + int background = 0xff000000; + + float fontsize = Float.parseFloat(pref.getString(ConfigKey.FONTSIZE, + "14")); + boolean useMonospaceFonts = pref.getBoolean( + ConfigKey.USE_MONOSPACE_FONTS, false); + + if (colortheme.equals("white")) { + foreground = 0xff000000; + background = 0xffffffff; + } else if (colortheme.equals("solarized_dark")) { + foreground = 0xff839496; + background = 0xff002b36; + } else if (colortheme.equals("solarized_light")) { + foreground = 0xff657b83; + background = 0xfffdf6e3; + } + + TextStyler.create(scrollView, textView, background, foreground, + fontsize, useMonospaceFonts ? "monospace" : "default").style(); } void hideSoftKeyboard() { - ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)) + ((InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE)) .hideSoftInputFromWindow(searchField.getWindowToken(), 0); } void showSoftKeyBoard() { - ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)) + ((InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE)) .toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); } - @SuppressLint("NewApi") void showSearchBar(boolean shown) { + isSearching = shown; if (searchBar == null) { throw new IllegalStateException(); } @@ -280,26 +320,20 @@ void showSearchBar(boolean shown) { searchBar.setVisibility(View.VISIBLE); searchBar.requestFocus(); showSoftKeyBoard(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - if (!titleHidden) { - final ActionBar actionBar = getActionBar(); - rootView.setPadding(0, 0, 0, 0); - actionBar.hide(); - } - } + ((ActionBarActivity) getActivity()).getSupportActionBar().hide(); } else { hideSoftKeyboard(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - if (!titleHidden) { - final ActionBar actionBar = getActionBar(); - rootView.setPadding(0, actionBar.getHeight(), 0, 0); - actionBar.show(); - } - } + ((ActionBarActivity) getActivity()).getSupportActionBar().show(); + ((NewMainActivity) getActivity()).hideActionBarDelayed(); searchBar.setVisibility(View.GONE); } } + boolean isSearching() { + return isSearching; + } + + @SuppressLint("DefaultLocale") void searchNext() { final String text = textView.getText().toString().toLowerCase(); final String search = searchField.getText().toString().toLowerCase(); @@ -353,6 +387,7 @@ void searchNext() { } + @SuppressLint("DefaultLocale") void searchPrevious() { final String text = textView.getText().toString().toLowerCase(); final String search = searchField.getText().toString().toLowerCase(); @@ -402,4 +437,55 @@ void searchPrevious() { hideSoftKeyboard(); } } + + private class LoadTextTask extends AsyncTask { + + ProgressDialog progressDialog; + + @Override + protected void onPreExecute() { + textView.setText(""); + progressDialog = ProgressDialog.show(getActivity(), "", + "Loading ...", true); + } + + @Override + protected String doInBackground(TextLoaderParam... params) { + final TextLoaderParam param = params[0]; + return TextLoader.create(param.context, param.uri, param.charset) + .read(); + } + + @Override + protected void onPostExecute(String result) { + restyle(); + textView.setText(result); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { + Selection.setSelection( + (Spannable) textView.getText(), selectionStart, selectionEnd); + } else { + ((EditText) textView).setSelection(selectionStart, selectionEnd); + } + progressDialog.dismiss(); + } + + } + + static class TextLoaderParam { + Context context; + Uri uri; + String charset; + } + + public void onVolumeKeyUp(int divisor) { + (TextPager.create(textView, (ScrollView) scrollView, divisor)).up(); + } + + public void onVolumeKeyDown(int divisor) { + (TextPager.create(textView, (ScrollView) scrollView, divisor)).down(); + } + + public interface OnContentTapListener { + public void onContentTap(); + } } diff --git a/src/com/gmail/altakey/dawne/CharsetDetector.java b/src/com/gmail/altakey/dawne/util/CharsetDetector.java similarity index 98% rename from src/com/gmail/altakey/dawne/CharsetDetector.java rename to src/com/gmail/altakey/dawne/util/CharsetDetector.java index f072c44..15ca58b 100644 --- a/src/com/gmail/altakey/dawne/CharsetDetector.java +++ b/src/com/gmail/altakey/dawne/util/CharsetDetector.java @@ -1,5 +1,5 @@ /** - * Copyright (C) 2011-2012 Takahiro Yoshimura + * Copyright (C) 2011-2013 Takahiro Yoshimura * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.gmail.altakey.dawne; +package com.gmail.altakey.dawne.util; import java.util.Arrays; import java.util.List; diff --git a/src/com/gmail/altakey/dawne/ConfigKey.java b/src/com/gmail/altakey/dawne/util/ConfigKey.java similarity index 88% rename from src/com/gmail/altakey/dawne/ConfigKey.java rename to src/com/gmail/altakey/dawne/util/ConfigKey.java index fdcc2e3..33a9f27 100644 --- a/src/com/gmail/altakey/dawne/ConfigKey.java +++ b/src/com/gmail/altakey/dawne/util/ConfigKey.java @@ -1,5 +1,5 @@ /** - * Copyright (C) 2011-2012 Takahiro Yoshimura + * Copyright (C) 2011-2013 Takahiro Yoshimura * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.gmail.altakey.dawne; +package com.gmail.altakey.dawne.util; public class ConfigKey { public static final String COLORTHEME = "colortheme"; @@ -23,6 +23,6 @@ public class ConfigKey { public static final String USE_MONOSPACE_FONTS = "usemonospacefonts"; public static final String SCROLL_BY_VOLUME_KEYS = "scrollbyvolumekeys"; public static final String SCROLL_LINES = "scrolllines"; - public static final String SHOW_TITLE = "showtitle"; public static final String CHARSET_PREFERENCE = "charsetpreference"; + public static final String RECENT_FILES = "recentfiles"; } diff --git a/src/com/gmail/altakey/dawne/util/ObjectSerializer.java b/src/com/gmail/altakey/dawne/util/ObjectSerializer.java new file mode 100644 index 0000000..e19ab81 --- /dev/null +++ b/src/com/gmail/altakey/dawne/util/ObjectSerializer.java @@ -0,0 +1,83 @@ +/** + * Copyright (C) 2013 Takahiro Yoshimura, Renjaya Raga Zenta + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.gmail.altakey.dawne.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +public class ObjectSerializer { + public static String serialize(Serializable obj) throws IOException { + if (obj == null) { + return ""; + } + try { + ByteArrayOutputStream serialObj = new ByteArrayOutputStream(); + ObjectOutputStream objStream = new ObjectOutputStream(serialObj); + objStream.writeObject(obj); + objStream.close(); + return encodeBytes(serialObj.toByteArray()); + } catch (Exception e) { + throw new IOException("Serialization error: " + e.getMessage()); + } + + } + + public static Object deserialize(String str) throws IOException { + if (str == null || str.length() == 0) { + return null; + } + try { + ByteArrayInputStream serialObj = new ByteArrayInputStream(decodeBytes(str)); + ObjectInputStream objStream = new ObjectInputStream(serialObj); + return objStream.readObject(); + } catch (Exception e) { + throw new IOException("Deserialization error: " + e.getMessage()); + } + + } + + public static String encodeBytes(byte[] bytes) { + final StringBuffer strBuf = new StringBuffer(); + + final int length = bytes.length; + for (int i = 0; i < length; i++) { + strBuf.append((char) (((bytes[i] >> 4) & 0xF) + ((int) 'a'))); + strBuf.append((char) (((bytes[i]) & 0xF) + ((int) 'a'))); + } + + return strBuf.toString(); + } + + public static byte[] decodeBytes(String str) { + final byte[] bytes = new byte[str.length() / 2]; + final int length = str.length(); + for (int i = 0; i < length; i += 2) { + char c = str.charAt(i); + bytes[i / 2] = (byte) ((c - 'a') << 4); + c = str.charAt(i + 1); + bytes[i / 2] += (c - 'a'); + } + return bytes; + + } + +} diff --git a/src/com/gmail/altakey/dawne/util/RecentFile.java b/src/com/gmail/altakey/dawne/util/RecentFile.java new file mode 100644 index 0000000..74f25ea --- /dev/null +++ b/src/com/gmail/altakey/dawne/util/RecentFile.java @@ -0,0 +1,48 @@ +/** + * Copyright (C) 2013 Takahiro Yoshimura, Renjaya Raga Zenta + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.gmail.altakey.dawne.util; + +import java.io.Serializable; + +public class RecentFile implements Serializable { + + private String fileName; + private String uri; + private static final long serialVersionUID = 7359036288102114071L; + + public RecentFile(String fileName, String uri) { + this.setFileName(fileName); + this.setUri(uri); + } + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } +} diff --git a/src/com/gmail/altakey/dawne/util/StackRecentFiles.java b/src/com/gmail/altakey/dawne/util/StackRecentFiles.java new file mode 100644 index 0000000..df3c150 --- /dev/null +++ b/src/com/gmail/altakey/dawne/util/StackRecentFiles.java @@ -0,0 +1,99 @@ +/** + * Copyright (C) 2013 Takahiro Yoshimura, Renjaya Raga Zenta + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.gmail.altakey.dawne.util; + +import java.io.Serializable; +import java.util.ArrayList; + +public class StackRecentFiles implements Serializable { + + public static final int MAX_CAPACITY = 10; + private ArrayList stack; + private int top; + private static final long serialVersionUID = 7977738208421002767L; + + public StackRecentFiles() { + this.stack = new ArrayList(MAX_CAPACITY); + this.top = -1; + } + + public void pushRecentFile(RecentFile recentFile) { + final int index = indexOf(recentFile); + if (index == -1) { + if (top >= MAX_CAPACITY - 1) { + shiftElementToBottom(); + stack.set(top, recentFile); + } else { + stack.add(++top, recentFile); + } + } else { + shiftElementToBottom(index); + } + } + + public RecentFile getRecentFile() { + if (top < 0) { + throw new IndexOutOfBoundsException(); + } + return stack.get(top); + } + + public RecentFile getRecentFileAt(int position) { + final int size = stack.size(); + if (position < 0 || position >= size) { + throw new IndexOutOfBoundsException(); + } + return stack.get(size - position - 1); + } + + public int size() { + return stack.size(); + } + + public void clear() { + stack.clear(); + top = -1; + } + + private int indexOf(RecentFile recentFile) { + final int size = stack.size(); + for (int i = 0; i < size; i++) { + final RecentFile rf = stack.get(i); + if (recentFile.getFileName().equals(rf.getFileName())) { + return i; + } + } + return -1; + } + + // Only called if stack is full! + private void shiftElementToBottom() { + for (int i = 0; i < top; i++) { + stack.set(i, stack.get(i + 1)); + } + stack.set(top, null); + } + + private void shiftElementToBottom(int index) { + final RecentFile rotated = stack.get(index); + for (int i = index; i < top; i++) { + stack.set(i, stack.get(i + 1)); + } + stack.set(top, rotated); + } +} diff --git a/src/com/gmail/altakey/dawne/TextLoader.java b/src/com/gmail/altakey/dawne/util/TextLoader.java similarity index 95% rename from src/com/gmail/altakey/dawne/TextLoader.java rename to src/com/gmail/altakey/dawne/util/TextLoader.java index 9727fa4..f04455b 100644 --- a/src/com/gmail/altakey/dawne/TextLoader.java +++ b/src/com/gmail/altakey/dawne/util/TextLoader.java @@ -1,5 +1,5 @@ /** - * Copyright (C) 2011-2012 Takahiro Yoshimura + * Copyright (C) 2011-2013 Takahiro Yoshimura * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.gmail.altakey.dawne; +package com.gmail.altakey.dawne.util; import java.io.ByteArrayOutputStream; import java.io.InputStream; diff --git a/src/com/gmail/altakey/dawne/TextPager.java b/src/com/gmail/altakey/dawne/util/TextPager.java similarity index 94% rename from src/com/gmail/altakey/dawne/TextPager.java rename to src/com/gmail/altakey/dawne/util/TextPager.java index 1fdaee4..9faacdd 100644 --- a/src/com/gmail/altakey/dawne/TextPager.java +++ b/src/com/gmail/altakey/dawne/util/TextPager.java @@ -1,5 +1,5 @@ /** - * Copyright (C) 2011-2012 Takahiro Yoshimura + * Copyright (C) 2011-2013 Takahiro Yoshimura * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.gmail.altakey.dawne; +package com.gmail.altakey.dawne.util; import android.widget.ScrollView; import android.widget.TextView; diff --git a/src/com/gmail/altakey/dawne/TextStyler.java b/src/com/gmail/altakey/dawne/util/TextStyler.java similarity index 96% rename from src/com/gmail/altakey/dawne/TextStyler.java rename to src/com/gmail/altakey/dawne/util/TextStyler.java index be0691f..dc07b62 100644 --- a/src/com/gmail/altakey/dawne/TextStyler.java +++ b/src/com/gmail/altakey/dawne/util/TextStyler.java @@ -1,5 +1,5 @@ /** - * Copyright (C) 2011-2012 Takahiro Yoshimura + * Copyright (C) 2011-2013 Takahiro Yoshimura * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package com.gmail.altakey.dawne; +package com.gmail.altakey.dawne.util; import android.graphics.Typeface; import android.util.TypedValue;