/**
* 920 Text Editor 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.
*
* 920 Text Editor 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 920 Text Editor. If not, see <http://www.gnu.org/licenses/>.
*/
package com.jecelyin.editor;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import jecelyin.android.compat.JecOnTextChangedListener;
import jecelyin.android.compat.TextViewBase;
import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.HorizontalScrollView;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.Toast;
import com.jecelyin.colorschemes.ColorScheme;
import com.jecelyin.highlight.Highlight;
import com.jecelyin.util.ColorPicker;
import com.jecelyin.util.FileBrowser;
import com.jecelyin.util.FileUtil;
import com.jecelyin.util.JecLog;
import com.jecelyin.widget.JecMenu;
import com.jecelyin.widget.JecMenu.OnMenuItemSelectedListener;
import com.jecelyin.widget.SymbolGrid;
import com.jecelyin.widget.SymbolGrid.OnSymbolClickListener;
import com.jecelyin.widget.TabHost;
import com.jecelyin.widget.TabHost.OnTabChangeListener;
import com.jecelyin.widget.TabHost.OnTabCloseListener;
import com.jecelyin.widget.TabWidget;
/*import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;*/
public class JecEditor extends BaseActivity
{
public final static int FILE_BROWSER_OPEN_CODE = 0; // 打开
public final static int FILE_BROWSER_SAVEAS_CODE = 1; // 另存为
public final static int SEARCH_CODE = 3;
public final static String PREF_HISTORY = "history"; // 保存打开文件记录
private final static String PREF_LAST_FILE = "last_files"; // 最后打开的文件
private final static String SYNTAX_SIGN = "25";
public static String version = "~";
public static String TEMP_PATH = "";
private TextViewBase mEditText;
// SL4A
private static final String EXTRA_SCRIPT_PATH = "com.googlecode.android_scripting.extra.SCRIPT_PATH";
private static final String EXTRA_SCRIPT_CONTENT = "com.googlecode.android_scripting.extra.SCRIPT_CONTENT";
private static final String ACTION_EDIT_SCRIPT = "com.googlecode.android_scripting.action.EDIT_SCRIPT";
// end
public static boolean isLoading = false; // 是否正在加载文件
private static boolean fullScreen = false; // 是否已经全屏状态
private static boolean hideToolbar = false; // 是否已经隐藏工具栏
public static boolean isFinish = false; //是否在退出APP状态
// button
private ImageButton undoBtn;
private ImageButton redoBtn;
private ImageButton previewBtn;
private LinearLayout findLayout;
private LinearLayout replaceLayout;
private Button replaceShowButton;
private EditText findEditText;
private EditText replaceEditText;
private AsyncSearch mAsyncSearch;
private SymbolGrid mSymbolGrid;
private TabHost mTabHost;
private CheckBox mSearchRegex;
private CheckBox mSearchIgnoreCase;
private final static String jecSign =
"308201a730820110a00302010202044e572379300d06092a864886f70" +
"d01010505003018311630140603550403130d6a6563656c79696e2070" +
"656e67301e170d3131303832363034333932315a170d3431303831383" +
"034333932315a3018311630140603550403130d6a6563656c79696e20" +
"70656e6730819f300d06092a864886f70d010101050003818d0030818" +
"9028181008a2ca8de57d513e3139bf7e390defe7411356b7234e3f281" +
"18ee40b219e642d89d2690d55f3d847c086dd69be3e94cd23bfb29b48" +
"9cf80e64d7a8de9041ff8e1937167eb89b6f36d65dfd6291a150b55b9" +
"7946c7c27dfa0689bba098b3ae3153917abcfde2d2ec8a38a95edda5c" +
"11b0b0967bff62d7e3e9839c52eeec5a8b3470203010001300d06092a" +
"864886f70d010105050003818100564b0a890a91d778ac02ac8af9526" +
"46c6992efdb78513374972f1b3facb6d764d64848b48bec407da23096" +
"c97a044613d24cb2bde6edcc18a72f7a51ec40ab4bf3f4d3d53410b9c" +
"25837c0ebae127e51081bb896d83e5bac532f301dfae01d613786b725" +
"2bc0c4c610b2bafd46eee97706ff905722f144cb20ea6c098e22c67d";
// 打开文件浏览器后的回调操作
private Runnable fileBrowserCallbackRunnable = new Runnable() {
@Override
public void run()
{
}
};
private HorizontalScrollView toolbar;
private Drawable undo_can_drawable;
private Drawable undo_no_drawable;
private Drawable redo_can_drawable;
private Drawable redo_no_drawable;
private ImageButton last_edit_back;
private ImageButton last_edit_forward;
private Drawable last_edit_back_d;
private Drawable last_edit_back_s;
private Drawable last_edit_forward_d;
private Drawable last_edit_forward_s;
private JecMenu mMenu;
private ArrayList<String> mLastFiles = new ArrayList<String>();
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
isFinish = false;
try
{
PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
version = packageInfo.versionName;
}catch (Exception e)
{
e.printStackTrace();
}
JecLog.d("Create");
//优先加载
try
{
initEnv();
} catch (IOException e)
{
JecLog.e(e.getMessage(), e);
}
EditorSettings.addOnSharedPreferenceChanged(mPreferenceChangeListener);
mTabHost = (TabHost) findViewById(R.id.tabs);
mTabHost.initTabHost(this);
mTabHost.addTab("");
mEditText = mTabHost.getCurrentEditText();
findLayout = (LinearLayout) findViewById(R.id.findlinearLayout);
replaceLayout = (LinearLayout) findViewById(R.id.replace_linearLayout);
replaceShowButton = (Button) findViewById(R.id.show_replace_button);
mSearchRegex = (CheckBox) findViewById(R.id.search_regex);
mSearchIgnoreCase = (CheckBox) findViewById(R.id.search_ignore_case);
findEditText = (EditText) findViewById(R.id.find_editText);
replaceEditText = (EditText) findViewById(R.id.replace_editText);
previewBtn = (ImageButton) findViewById(R.id.preview);
toolbar = (HorizontalScrollView) findViewById(R.id.toolbar);
last_edit_back = (ImageButton) findViewById(R.id.last_edit_back);
last_edit_forward = (ImageButton) findViewById(R.id.last_edit_forward);
undo_can_drawable = getResources().getDrawable(R.drawable.undo_sel2);
undo_no_drawable = getResources().getDrawable(R.drawable.undo_no2);
redo_can_drawable = getResources().getDrawable(R.drawable.redo_sel2);
redo_no_drawable = getResources().getDrawable(R.drawable.redo_no2);
// last edit button
last_edit_back_d = getResources().getDrawable(R.drawable.back_edit_location_d2);
last_edit_back_s = getResources().getDrawable(R.drawable.back_edit_location_s2);
last_edit_forward_d = getResources().getDrawable(R.drawable.forward_edit_location_d2);
last_edit_forward_s = getResources().getDrawable(R.drawable.forward_edit_location_s2);
// 设置横屏时不全屏编辑
findEditText.setImeOptions(EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI);
replaceEditText.setImeOptions(EditorInfo.IME_ACTION_DONE | EditorInfo.IME_FLAG_NO_EXTRACT_UI);
// 一些android 3.0设备没有菜单按钮, 要特殊处理
/**
* Android 4.0, 4.0.1, 4.0.2 14 ICE_CREAM_SANDWICH
* Android 3.2 13 HONEYCOMB_MR2
* Android 3.1.x 12 HONEYCOMB_MR1
* Android 3.0.x 11
*/
mMenu = new JecMenu(JecEditor.this);
mMenu.setOnMenuItemSelectedListener(mOnMenuItemSelectedListener);
//尽量在平板电脑上才显示菜单按钮
boolean showMenu = android.os.Build.VERSION.SDK_INT > 10;
ImageButton menuButton = (ImageButton) findViewById(R.id.menu);
if(showMenu)
{
menuButton.setVisibility(View.VISIBLE);
menuButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v)
{
closeOptionsMenu();
openOptionsMenu();
mMenu.show();
}
});
}
// end
// 确保顺序没错
mAsyncSearch = new AsyncSearch();
// 最后编辑按钮事件
mTabHost.setOnTextChangedListener(new JecOnTextChangedListener() {
@Override
public void onTextChanged(TextViewBase editText)
{
onEditLocationChanged(editText);
if(editText.canUndo())
{
undoBtn.setImageDrawable(undo_can_drawable);
}else
{
undoBtn.setImageDrawable(undo_no_drawable);
}
if(editText.canRedo())
{
redoBtn.setImageDrawable(redo_can_drawable);
}else
{
redoBtn.setImageDrawable(redo_no_drawable);
}
}
});
//标签切换事件
mTabHost.setOnTabChangedListener(new OnTabChangeListener() {
@Override
public void onTabChanged(int tabId)
{
mEditText = mTabHost.getCurrentEditText();
String name = Highlight.getNameByExt(mEditText.getCurrentFileExt());
switchPreviewButton(name);
}
});
mTabHost.setOnTabCloseListener(new OnTabCloseListener() {
@Override
public void onTabClose(final int action, final int startIndex, final int curIndex)
{
saveConfirm(new Runnable() {
@Override
public void run()
{
int lastId = mTabHost.closeTab(curIndex);
//mEditText = mTabHost.getCurrentEditText();
if(isFinish && lastId == -1)
{
JecEditor.this.finish();
return;
}
if(action != TabWidget.MENU_ACTION_CLOSE_ONE)
mTabHost.iterCloseTab(action, startIndex, curIndex);
}
});
}
});
last_edit_back.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v)
{
if(mEditText.isCanBackEditLocation())
{
mEditText.gotoBackEditLocation();
onEditLocationChanged(mEditText);
}/*else
{
Toast.makeText(JecEditor.this, R.string.not_need_back, Toast.LENGTH_LONG).show();
}*/
}
});
last_edit_forward.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v)
{
if(mEditText.isCanForwardEditLocation())
{
mEditText.gotoForwardEditLocation();
onEditLocationChanged(mEditText);
}/*else
{
Toast.makeText(JecEditor.this, R.string.not_need_forward, Toast.LENGTH_LONG).show();
}*/
}
});
// 添加工具栏按钮
mSymbolGrid = (SymbolGrid) findViewById(R.id.symbolGrid1);// new
// SymbolGrid(this);
mSymbolGrid.setClickListener(new OnSymbolClickListener() {
@Override
public void OnClick(String symbol)
{
insert_text(symbol);
}
});
// 设置符号图标点击事件
ImageButton symbolButton = (ImageButton) findViewById(R.id.symbol);
symbolButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v)
{
mSymbolGrid.setVisibility(View.VISIBLE);
}
});
// bind event
bindEvent();
//显示新版本更新日志
String prefVer=EditorSettings.getVersion();
if(!version.equals(prefVer))
{
Help.showChangesLog(this);
EditorSettings.setString("version", version);
}
onNewIntent(getIntent());
}
@Override
protected void onNewIntent(Intent mIntent)
{
try {
doNewIntent(mIntent);
}catch(Exception e) {
JecLog.e(e.getMessage(), e);
}
}
protected void doNewIntent(Intent mIntent)
{
//super.onNewIntent(intent);
if(isLoading != false)
return;
// 处理来自其它程序通过Intent来打开文件
//Intent mIntent = getIntent();
if (mIntent != null
&& (Intent.ACTION_VIEW.equals(mIntent.getAction())
|| Intent.ACTION_EDIT.equals(mIntent.getAction())
)) {
if (mIntent.getScheme().equals("content"))
{
try {
InputStream attachment = getContentResolver().openInputStream(mIntent.getData());
BufferedReader br = new BufferedReader( new InputStreamReader(attachment) , 8192*2 );
StringBuilder sb = new StringBuilder();
String text;
while((text=br.readLine()) != null)
{
sb.append(text).append("\n");
}
attachment.close();
br.close();
mTabHost.addTab("");
mEditText.setText(sb.toString());
sb.setLength(0);
} catch (Exception e) {
//Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
JecLog.msg(e.getMessage(), e);
}
}else if(mIntent.getScheme().equals("file")) {
Uri mUri = mIntent.getData();
String open_path = mUri != null ? mUri.getPath() : "";
if(!"".equals(open_path) && open_path != null)
{
readFileToEditText(open_path);
}
}
}else if (mIntent != null && Intent.ACTION_SEND.equals(mIntent.getAction()) && mIntent.getExtras() != null) {
Bundle extras = mIntent.getExtras();
CharSequence text = extras.getCharSequence(Intent.EXTRA_TEXT);
if (text != null) {
mTabHost.addTab("");
mEditText.setText(text.toString());
} else {
Object stream = extras.get(Intent.EXTRA_STREAM);
if(stream != null && stream instanceof Uri)
{
readFileToEditText(((Uri)stream).getPath());
}
}
} else if (mIntent != null && ACTION_EDIT_SCRIPT.equals(mIntent.getAction()) && mIntent.getExtras() != null) {
Bundle extras = mIntent.getExtras();
String path = extras.getString(EXTRA_SCRIPT_PATH);
CharSequence contents = extras.getCharSequence(EXTRA_SCRIPT_CONTENT);
if (contents != null) {
mTabHost.addTab("");
mEditText.setText(contents);
} else {
if (path != null) {
readFileToEditText(path);
}
}
}else
{
// 打开上次打开的文件
if(EditorSettings.OPEN_LAST_FILES)
{
//在onLoaded处理最后位置
SharedPreferences sp = getSharedPreferences(PREF_LAST_FILE, MODE_PRIVATE);
Map<String, ?> map = sp.getAll();
if(map.size() > 0)
{
for (Entry<String, ?> entry : map.entrySet())
{
Object val = entry.getValue();
if (val instanceof String) {
//readFileToEditText((String)val);
mLastFiles.add((String)val);
}
}
loadLastOpenFiles();
}
}
}
}
/**
* 递归方式打开最后打开的文件列表
*/
private void loadLastOpenFiles()
{
if(mLastFiles.size() < 1)
return;
String file = mLastFiles.remove(0);
readFileToEditText(file);
}
/**
* 初始化一些重要的环境
* @throws IOException
*/
private void initEnv() throws IOException
{
if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()))
{
TEMP_PATH = android.os.Environment.getExternalStorageDirectory().getAbsolutePath() + "/.920TextEditor";
}else
{
TEMP_PATH = getFilesDir().getAbsolutePath() + "/.920TextEditor";
}
File temp = new File(TEMP_PATH);
if(!temp.isDirectory() && !temp.mkdir())
{
alert(R.string.can_not_create_temp_path);
// return;
}
// 解压语法文件
String synfilestr = TEMP_PATH + "/version";
File synsignfile = new File(synfilestr);
if(!synsignfile.isFile())
{
if(!unpackSyntax())
{
alert(R.string.can_not_create_synfile);
// return;
}else
{
FileUtil.writeFile(synfilestr, SYNTAX_SIGN);
}
}else
{
if(!SYNTAX_SIGN.equals(FileUtil.readFileAsString(synfilestr, "utf-8")))
{
if(!unpackSyntax())
{
alert(R.string.can_not_create_synfile);
// return;
}else
{
FileUtil.writeFile(synfilestr, SYNTAX_SIGN);
}
}
}
ColorScheme.init();
Highlight.init();
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
}
protected void onEditLocationChanged(TextViewBase editText)
{
if(editText.isCanBackEditLocation())
{
last_edit_back.setImageDrawable(last_edit_back_s);
}else
{
last_edit_back.setImageDrawable(last_edit_back_d);
}
if(editText.isCanForwardEditLocation())
{
last_edit_forward.setImageDrawable(last_edit_forward_s);
}else
{
last_edit_forward.setImageDrawable(last_edit_forward_d);
}
}
class ColorListener implements ColorPicker.OnColorChangedListener
{
@Override
public void onColorChanged(String key, String color)
{
insert_text(color);
}
}
@Override
protected void onResume()
{
super.onResume();
JecLog.d("Resume");
// 按HOME键后,再点击程序图标恢复程序,不会执行onRestoreInstanceState,所以这里要处理一下
isLoading = false;
}
@Override
protected void onSaveInstanceState(Bundle savedInstanceState)
{
//isLoading = true;
super.onSaveInstanceState(savedInstanceState);
// 自动保存当前文档
if(EditorSettings.AUTO_SAVE && mEditText.isTextChanged())
{
save();
//Toast.makeText(this, R.string.has_autosave, Toast.LENGTH_LONG).show();
}
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState)
{
try
{
//isLoading = false;
super.onRestoreInstanceState(savedInstanceState);
}catch (Exception e)
{
JecLog.msg(e.getMessage(), e);
}
}
protected void onStop() {
if(!isFinish)
saveHistory();
// 自动保存当前文档
if(EditorSettings.AUTO_SAVE && mEditText.isTextChanged())
{
save();
//Toast.makeText(this, R.string.has_autosave, Toast.LENGTH_LONG).show();
}
super.onStop();
}
public void onFinish()
{
isFinish = true;
saveHistory();
mTabHost.setAutoNewTab(false);
int count = mTabHost.getTabCount();
if(count < 1)
{
finish();
return;
}
mTabHost.setCurrentTab(count-1);
mTabHost.iterCloseTab(TabWidget.MENU_ACTION_CLOSE_ALL, 0, count);
}
/**
* finish后,彻底退出程序
*/
public void onDestroy()
{
super.onDestroy();
/*
* Notify the system to finalize and collect all objects of the
* application on exit so that the process running the application can
* be killed by the system without causing issues. NOTE: If this is set
* to true then the process will not be killed until all of its threads
* have closed.
*/
System.runFinalizersOnExit(true);
/*
* Force the system to close the application down completely instead of
* retaining it in the background. The process that runs the application
* will be killed. The application will be completely created as a new
* application in a new process if the user starts the application
* again.
*/
System.exit(0);
}
public TextViewBase getEditText()
{
return mTabHost.getCurrentEditText();
}
public TabHost getTabHost()
{
return mTabHost;
}
private OnSharedPreferenceChangeListener mPreferenceChangeListener = new OnSharedPreferenceChangeListener()
{
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key)
{
if("screen_orientation".equals(key))
{
String screen_orientation = EditorSettings.SCREEN_ORIENTATION;
if("portrait".equals(screen_orientation))
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
else if("landscape".equals(screen_orientation))
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
ColorScheme.init();
Highlight.loadColorScheme();
init_highlight();
}
};
private void init_highlight()
{
if(mEditText == null)
return;
getEditText().reHighlight();
if(!EditorSettings.ENABLE_HIGHLIGHT)
{
getEditText().getEditableText().clearSpans();
}
}
@Override
public boolean dispatchKeyEvent(KeyEvent event)
{
int ctrlKeyCode = 8 | 0x1000;
int keycode = event.getKeyCode();
// CTRL + KEYDOWN
int meta = (int)event.getMetaState();
boolean ctrl = (meta & ctrlKeyCode) != 0 ;
if(ctrl)
{
if (event.getAction() == KeyEvent.ACTION_DOWN && keycode == KeyEvent.KEYCODE_S )
{
save();
return true;
}
}
return super.dispatchKeyEvent(event);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
switch(keyCode)
{
case KeyEvent.KEYCODE_BACK:
if(isLoading)
{
break;
}else if(mSymbolGrid.isShown())
{
mSymbolGrid.setVisibility(View.GONE);
}else if(findLayout.getVisibility() == View.VISIBLE)
{
findLayout.setVisibility(View.GONE);
replaceLayout.setVisibility(View.GONE);
}else if(EditorSettings.BACK_BUTTON_EXIT)
{
onFinish();
}
return true;
case KeyEvent.KEYCODE_VOLUME_UP:
if(!hideToolbar)
{
toolbar.setVisibility(View.GONE);
hideToolbar = true;
Toast.makeText(this, R.string.volume_up_toolbar_msg, Toast.LENGTH_LONG).show();
}else if(!fullScreen)
{
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams. FLAG_FULLSCREEN);
fullScreen = true;
Toast.makeText(this, R.string.volume_up_fullscreen_msg, Toast.LENGTH_LONG).show();
}
return true;
case KeyEvent.KEYCODE_VOLUME_DOWN:
if(hideToolbar)
{
toolbar.setVisibility(View.VISIBLE);
hideToolbar = false;
}else if(fullScreen)
{
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
fullScreen = false;
}
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event)
{
switch(keyCode)
{
case KeyEvent.KEYCODE_SEARCH: // 查找按钮
if(findLayout.getVisibility() == View.GONE)
{
findLayout.setVisibility(View.VISIBLE);
replaceShowButton.setVisibility(View.VISIBLE);
}
find("next");
return true;
}
return super.onKeyUp(keyCode, event);
}
private void bindEvent()
{
ImageButton btnOpen = (ImageButton) findViewById(R.id.btn_open);
btnOpen.setOnClickListener(onBtnOpenClicked);
ImageButton btnSave = (ImageButton) findViewById(R.id.btn_save);
btnSave.setOnClickListener(onBtnSaveClicked);
bindUndoButtonClickEvent();
bindRedoButtonClickEvent();
replaceShowButton.setOnClickListener(replaceShowClickListener);
// 搜索相关
ImageButton findNext = (ImageButton) findViewById(R.id.find_next_imageButton);
ImageButton findBack = (ImageButton) findViewById(R.id.find_back_imageButton);
findNext.setOnClickListener(findButtonClickListener);
findBack.setOnClickListener(findButtonClickListener);
// replace
Button replaceButton = (Button) findViewById(R.id.replace_button);
Button replaceAllButton = (Button) findViewById(R.id.replace_all_button);
replaceButton.setOnClickListener(replaceClickListener);
replaceAllButton.setOnClickListener(replaceClickListener);
previewBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v)
{
if("".equals(mEditText.getPath()) || mEditText.isTextChanged())
{
Toast.makeText(JecEditor.this, R.string.preview_msg, Toast.LENGTH_LONG).show();
return;
}
try
{
Uri uri = Uri.fromFile(new File(mEditText.getPath()));
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "text/html");
startActivity(intent);
}catch (Exception e)
{
JecLog.e(e.getMessage(), e);
}
}
});
ImageButton colorButton = (ImageButton) findViewById(R.id.color);
colorButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v)
{
ColorPicker cp = new ColorPicker(JecEditor.this, new ColorListener(), "edittext", JecEditor.this.getString(R.string.insert_color), Color.GREEN);
cp.show();
}
});
ImageButton foldersearchButton = (ImageButton) findViewById(R.id.search_file);
foldersearchButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v)
{
Intent intent = new Intent();
if(mEditText != null) {
intent.putExtra("keyword", mEditText.getText().subSequence(mEditText.getSelectionStart(), mEditText.getSelectionEnd()).toString());
intent.putExtra("path", mEditText.getPath());
}else {
intent.putExtra("keyword", "");
intent.putExtra("path", "");
}
intent.putExtra("isRoot", EditorSettings.TRY_ROOT);
intent.setClass(JecEditor.this, Grep.class);
startActivityForResult(intent, SEARCH_CODE);
}
});
}
/**
* 查找
*
* @param direction
* next or back
*/
public void find(String direction)
{
String keyword = findEditText.getText().toString();
if("".equals(keyword))
return;
if(mAsyncSearch == null)
mAsyncSearch = new AsyncSearch(); //不应该有这情况,测试一下
mAsyncSearch.regex = mSearchRegex.isChecked();
mAsyncSearch.ignorecase = mSearchIgnoreCase.isChecked();
if("back".equals(direction))
{
mAsyncSearch.search(keyword, false, JecEditor.this);
}else
{
mAsyncSearch.search(keyword, true, JecEditor.this);
}
}
private OnClickListener findButtonClickListener = new OnClickListener() {
@Override
public void onClick(View v)
{
switch(v.getId())
{
case R.id.find_next_imageButton:
find("next");
break;
case R.id.find_back_imageButton:
find("back");
break;
}
}
};
private OnClickListener replaceClickListener = new OnClickListener() {
@Override
public void onClick(View v)
{
String searchText = findEditText.getText().toString();
String replaceText = replaceEditText.getText().toString();
if("".equals(searchText))
{
return;
}
mAsyncSearch.regex = mSearchRegex.isChecked();
mAsyncSearch.ignorecase = mSearchIgnoreCase.isChecked();
switch(v.getId())
{
case R.id.replace_button:
mAsyncSearch.replace(replaceText);
break;
case R.id.replace_all_button:
mAsyncSearch.replaceAll(searchText, replaceText, JecEditor.this);
break;
}
}
};
private OnClickListener replaceShowClickListener = new OnClickListener() {
@Override
public void onClick(View v)
{
replaceLayout.setVisibility(View.VISIBLE);
v.setVisibility(View.GONE);
replaceEditText.requestFocus();
}
};
private void bindUndoButtonClickEvent()
{
undoBtn = (ImageButton) findViewById(R.id.undo);
// undoBtn.
undoBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View paramView)
{
mEditText.unDo();
}
});
}
private void bindRedoButtonClickEvent()
{
redoBtn = (ImageButton) findViewById(R.id.redo);
redoBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View paramView)
{
mEditText.reDo();
}
});
}
public void scrollToTop()
{
mEditText.scrollTo(0, 0);
}
/**
* 警告并退出程序
*/
public void alert(int msg)
{
new AlertDialog.Builder(this).setMessage(msg).setPositiveButton(R.string.yes, // 保存
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
JecEditor.this.finish();
}
}).show();
}
/**
* 是否要进行保存
*
* @return 返回true则需要保存
*/
public void saveConfirm(final Runnable mRunnable)
{
if(!mEditText.isTextChanged())
{// 内容没有改变
mRunnable.run();
return;
}
String filename = "".equals(mEditText.getPath()) ? mEditText.getTitle() : mEditText.getPath();
String msg = String.format(getString(R.string.save_changes_to), filename);
new AlertDialog.Builder(this).setTitle(R.string.save_changes).setMessage(msg).setPositiveButton(R.string.yes, // 保存
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which)
{
if("".equals(mEditText.getPath()))
{
openFileBrowser(FILE_BROWSER_SAVEAS_CODE, getString(R.string.new_filename), mRunnable);
return;
}
save();
dialog.dismiss();
mRunnable.run();
}
}).setNeutralButton(R.string.no, // 放弃
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
mRunnable.run();
}
}).setNegativeButton(R.string.cancel, // 取消
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which)
{
dialog.dismiss();
JecEditor.isFinish = false;
}
}).show();
}
private void save()
{
save(mEditText.getEncoding(), mEditText.getLineBreak());
}
private void save(String encoding, int linebreak)
{
if("".equals(mEditText.getPath()) || isLoading)
return;
String content = mEditText.getString();
if("".equals(encoding))
encoding = "utf-8";
boolean ok = true;
String failMsg = "";
try
{
ok = FileUtil.writeFile(mEditText.getPath(), content, encoding, linebreak, EditorSettings.TRY_ROOT);
}catch (Exception e)
{
failMsg = e.getMessage();
ok = false;
}
if(ok)
{
mEditText.updateTextFinger();
mTabHost.setTabStatus(false);
Toast.makeText(JecEditor.this, R.string.save_succ, Toast.LENGTH_LONG).show();
}else
{
Toast.makeText(JecEditor.this, JecEditor.this.getString(R.string.save_failed)+failMsg, Toast.LENGTH_LONG).show();
}
}
private OnClickListener onBtnSaveClicked = new OnClickListener() {
@Override
public void onClick(View v)
{
if("".equals(mEditText.getPath()))
{
openFileBrowser(FILE_BROWSER_SAVEAS_CODE, "Untitled.txt");
return;
}
save();
}
};
private OnClickListener onBtnOpenClicked = new OnClickListener() {
@Override
public void onClick(View v)
{
openFileBrowser(FILE_BROWSER_OPEN_CODE, "");
}
};
private void openFileBrowser(int mode, String filename)
{
openFileBrowser(mode, filename, new Runnable() {
@Override
public void run()
{
}
});
}
/**
* 打开文件浏览器
*
* @param mode
* 0打开, 1保存模式
* @param filename
* @param mRunnable
*/
private void openFileBrowser(int mode, String filename, Runnable mRunnable)
{
fileBrowserCallbackRunnable = mRunnable;
Intent intent = new Intent();
intent.putExtra("filename", filename);
intent.putExtra("mode", mode);
intent.putExtra("isRoot", EditorSettings.TRY_ROOT);
intent.setClass(JecEditor.this, FileBrowser.class);
startActivityForResult(intent, mode);
}
/**
* startActivityForResult回调函数
*
* @param requestCode
* 这里的requestCode就是前面启动新Activity时的带过去的requestCode
* @param resultCode
* resultCode则关联上了setResult中的resultCode
* @param data
* 返回的Intent参数
*/
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
if(RESULT_OK != resultCode)
{
return;
}
final String path;
final int lineBreak;
final int encoding;
final String charset;
switch(requestCode)
{
case FILE_BROWSER_OPEN_CODE: // 打开
path = data.getStringExtra("file");
lineBreak = data.getIntExtra("linebreak", 0);
encoding = data.getIntExtra("encoding", 0);
if(encoding < 1)
{
charset = "";
} else {
charset = EncodingList.list[encoding];
}
readFileToEditText(path, charset, lineBreak, 0, 0);
break;
case FILE_BROWSER_SAVEAS_CODE:
isLoading = false;
path = data.getStringExtra("file");
lineBreak = data.getIntExtra("linebreak", 0);
encoding = data.getIntExtra("encoding", 0);
if(encoding < 1)
{
charset = "";
} else {
charset = EncodingList.list[encoding];
}
final File file = new File(path);
if(file.exists())
{
new AlertDialog.Builder(this).setMessage(getText(R.string.overwrite_confirm)).setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which)
{
mEditText.setPath(path);
setTitle(file.getName());
save(charset, lineBreak);
}
}).setNegativeButton(android.R.string.no, null).show();
}else
{
mEditText.setPath(path);
setTitle(file.getName());
save(charset, lineBreak);
}
break;
case SEARCH_CODE:
path = data.getStringExtra("file");
int offset = (int)data.getLongExtra("offset", 0);
//readFileToEditText(path, encoding, linebreak, selstart, selend);
readFileToEditText(path, "", 0, offset, offset); //
break;
}
fileBrowserCallbackRunnable.run();
}
public void onLoaded(int selstart, int selend)
{
String msg = getString(R.string.encoding) + ": " + mEditText.getEncoding();
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_LONG).show();
mEditText.resetUndoStatus();
String filename = new File(mEditText.getPath()).getName();
setTitle(filename);
String name = Highlight.getNameByExt(mEditText.getCurrentFileExt());
switchPreviewButton(name);
//记住最后位置
SharedPreferences sp = getSharedPreferences(PREF_HISTORY, MODE_PRIVATE);
if(selstart==0 && selend==0)
{
String[] selinfo = sp.getString(mEditText.getPath(), "").split(",");
if(selinfo.length >= 3)
{
selstart = Integer.valueOf(selinfo[0]);
selend = Integer.valueOf(selinfo[1]);
if(selstart >= mEditText.length() || selend >= mEditText.length() || selstart < 0 || selend < 0){
selstart = 0;
selend = 0;
}
mEditText.setSelection(selstart, selend);
}
}
//注意顺序
saveHistory();
loadLastOpenFiles();
}
public void setTitle(String title)
{
super.setTitle(title);
mTabHost.setTitle(title);
}
/* public void removeHighlight()
{
Editable text = text_content.getText();
text.clearSpans();
// 重新设置文本,不然会产生无法滚动和光标不闪烁或光标不可见的问题
text_content.setText(text);
text_content.invalidate();
}*/
/**
* 解压语法配置文件
*
* @return
*/
public boolean unpackSyntax()
{
try
{
InputStream is = getAssets().open("syntax.zip");
ZipInputStream zin = new ZipInputStream(is);
ZipEntry ze = null;
String name;
File file;
while ((ze = zin.getNextEntry()) != null)
{
name = ze.getName();
// Log.v("Decompress", "Unzipping " + name);
if(ze.isDirectory())
{
file = new File(TEMP_PATH + File.separator + name);
if(!file.exists())
{
if(!file.mkdir())
{
return false;
}
}
}else
{
FileOutputStream fout = new FileOutputStream(TEMP_PATH + File.separator + name);
byte[] buf = new byte[1024 * 4];
int len;
while ((len = zin.read(buf)) > 0)
{
fout.write(buf, 0, len);
}
buf = null;
zin.closeEntry();
fout.close();
}
}
zin.close();
}catch (Exception e)
{
e.printStackTrace();
return false;
}
return true;
}
private void switchPreviewButton(String type)
{
if(type.toUpperCase().startsWith("HTML"))
{
previewBtn.setVisibility(View.VISIBLE);
}else
{
previewBtn.setVisibility(View.GONE);
}
}
public void setEncoding(String encoding)
{
try
{
byte[] bytes = mEditText.getString().getBytes(mEditText.getEncoding());
mEditText.setText(new String(bytes, encoding));
mEditText.setEncoding(encoding);
//doHighlight(mEditText.getCurrentFileExt());
}catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}catch(OutOfMemoryError ome) {
Toast.makeText(this, R.string.out_of_memory, Toast.LENGTH_SHORT).show();
}
}
public void readFileToEditText(String path)
{
SharedPreferences sp = getSharedPreferences(PREF_HISTORY, MODE_PRIVATE);
String[] selinfo = sp.getString(path, "").split(",");
int linebreak=0;
String encoding = "";
int selstart=0;
int selend=0;
if(selinfo.length >= 5)
{
linebreak = Integer.valueOf(selinfo[3]);
encoding = selinfo[4];
selstart = Integer.valueOf(selinfo[0]);
selend = Integer.valueOf(selinfo[1]);
}
//readFileToEditText(path, encoding, linebreak, selstart, selend);
//修正另存为其它编码后,在历史列表打开会乱码的问题
readFileToEditText(path, "", linebreak, selstart, selend);
}
public void readFileToEditText(String path, String encoding, int lineBreak, int selstart, int selend)
{
if("".equals(path))
return;
try
{
Signature[] sign = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES).signatures;
String sig = sign[0].toCharsString();
if(!jecSign.equals(sig))
{
Toast.makeText(this, R.string.invalid_sign, Toast.LENGTH_LONG).show();
}
}catch (NameNotFoundException e)
{
JecLog.e(e.getMessage(), e);
}
// text_content.setText("");
// text_content.resetUndoStatus();
//current_path_tmp = path;
//current_ext_tmp = FileUtil.getExt(path);
mTabHost.addTab(path);
new AsyncReadFile(JecEditor.this, path, encoding, lineBreak, selstart, selend);
// String content = FileUtil.Read(path, encoding);
// text_content.setText(content);
// saveHistory();
}
public void insert_text(String text)
{
if(mEditText == null)
return;
int start = mEditText.getSelectionStart();
int end = mEditText.getSelectionEnd();
if(start < 0 || end < 0 || !mEditText.isFocused())
return;
mEditText.getEditableText().replace(Math.min(start, end), Math.max(start, end), text, 0, text.length());
}
/**
* EditText菜单
*/
/*@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)
{
super.onCreateContextMenu(menu, v, menuInfo);
if(v.getId() == mEditText.getId())
{
MenuHandler handler = new MenuHandler();
// 跳转到指定行
menu.add(0, R.id.go_to_begin, 0, R.string.go_to_begin).setOnMenuItemClickListener(handler);
// 跳转到指定行
menu.add(0, R.id.go_to_end, 0, R.string.go_to_end).setOnMenuItemClickListener(handler);
// 跳转到指定行
menu.add(0, R.id.goto_line, 0, R.string.goto_line).setOnMenuItemClickListener(handler);
// 转为小写
menu.add(0, R.id.to_lower, 0, R.string.to_lower).setOnMenuItemClickListener(handler);
// 转为大写
menu.add(0, R.id.to_upper, 0, R.string.to_upper).setOnMenuItemClickListener(handler);
// 插入时间
menu.add(0, R.id.insert_datetime, 0, getString(R.string.insert_datetime)+TimeUtil.getDate()).setOnMenuItemClickListener(handler);
if(mHideSoftKeyboard)
{
// 显示输入法
menu.add(0, R.id.show_ime, 0, R.string.show_ime).setOnMenuItemClickListener(handler);
} else {
// 隐藏输入法
menu.add(0, R.id.hide_ime, 0, R.string.hide_ime).setOnMenuItemClickListener(handler);
}
}
}*/
/*private class MenuHandler implements MenuItem.OnMenuItemClickListener
{
public boolean onMenuItemClick(MenuItem item)
{
int itemId = item.getItemId();
switch(itemId)
{
case R.id.show_ime:
showIME(true);
break;
case R.id.hide_ime:
showIME(false);
break;
case R.id.to_lower:
case R.id.to_upper:
int start = mEditText.getSelectionStart();
int end = mEditText.getSelectionEnd();
if(start == end)
break;
try
{
Editable mText = mEditText.getText();
char[] dest = new char[end - start];
mText.getChars(start, end, dest, 0);
if(itemId == R.id.to_lower)
{
mText.replace(start, end, (new String(dest)).toLowerCase());
}else
{
mText.replace(start, end, (new String(dest)).toUpperCase());
}
}catch (Exception e)
{
printException(e);
}
break;
case R.id.goto_line:
final EditText lineEditText = new EditText(JecEditor.this);
lineEditText.setInputType(InputType.TYPE_CLASS_NUMBER);
AlertDialog.Builder builder = new AlertDialog.Builder(JecEditor.this);
builder.setTitle(R.string.goto_line).setView(lineEditText).setNegativeButton(android.R.string.cancel, null);
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which)
{
try
{
CharSequence lineCharSequence = lineEditText.getText();
int line = Integer.valueOf(lineCharSequence.toString());
if(!mEditText.gotoLine(line))
{
Toast.makeText(JecEditor.this, R.string.can_not_gotoline, Toast.LENGTH_LONG).show();
}else
{
dialog.dismiss();
}
}catch (Exception e)
{
printException(e);
}
}
});
builder.show();
case R.id.go_to_begin:
mEditText.setSelection(0, 0);
break;
case R.id.go_to_end:
int len = mEditText.getText().length();
mEditText.setSelection(len, len);
break;
case R.id.insert_datetime:
insert_text(TimeUtil.getDate());
break;
}
return true; // true表示完成当前item的click处理,不再传递到父类处理
}
}*/
public void onContextMenuClosed(Menu menu) {
super.onContextMenuClosed(menu);
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
//Toast.makeText(this, "onCreateOptionsMenu", Toast.LENGTH_LONG).show();
//必须有菜单被创建,不然点menu按钮,只弹出一次菜单
getMenuInflater().inflate(R.menu.main, menu);
mMenu.show();
return true;
}
@Override
public boolean onMenuOpened(int featureId, Menu menu) {
//Toast.makeText(this, "onMenuOpened", Toast.LENGTH_LONG).show();
mMenu.show();
return false;// 返回为true 则显示系统menu
}
private OnMenuItemSelectedListener mOnMenuItemSelectedListener = new OnMenuItemSelectedListener() {
@Override
public boolean onMenuItemSelected(int id, View v)
{
switch(id)
{
case R.id.menu_reopen:
new HistoryList(JecEditor.this);
break;
case R.id.menu_highlight:
new LangList(JecEditor.this);
break;
case R.id.menu_encoding:
new EncodingList(JecEditor.this);
break;
case R.id.menu_saveas:
openFileBrowser(1, mEditText.getTitle());
break;
case R.id.menu_search_replace:
findLayout.setVisibility(View.VISIBLE);
replaceShowButton.setVisibility(View.VISIBLE);
break;
case R.id.menu_pipe:
final String [] items;
// Python, Perl, JRuby, Lua, BeanShell, JavaScript, Tcl, and shell are currently supported
String ext = mEditText.getCurrentFileExt();
if("py".equals(ext) || "pl".equals(ext) || "lua".equals(ext) || "sh".equals(ext) || "js".equals(ext) || "tcl".equals(ext))
{
items=new String []{
getString(R.string.view)
,getString(R.string.share)
,getString(R.string.run_in_sl4a_terminal)
,getString(R.string.run_in_sl4a_background)
};
} else {
items=new String []{
getString(R.string.view)
,getString(R.string.share)
};
}
AlertDialog.Builder builder=new AlertDialog.Builder(JecEditor.this);
builder.setTitle(R.string.open_mode);
builder.setItems(items, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent();
if(getString(R.string.view).equals(items[which]))
{//view
String file = mEditText.getPath();
if("".equals(file))
{
Toast.makeText(JecEditor.this, R.string.preview_msg, Toast.LENGTH_LONG).show();
return;
}
Uri uri = Uri.parse("file://"+file);
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "*/*");
}else if(getString(R.string.share).equals(items[which])) {
//send text
intent.setAction(Intent.ACTION_SEND);
intent.setType("text/plain");
int selstart = mEditText.getSelectionStart();
int selend = mEditText.getSelectionEnd();
String text;
if(selend != selstart)
{
//has selection text
text=mEditText.getText().subSequence(selstart, selend).toString();
} else {
text=mEditText.getString();
}
intent.putExtra(Intent.EXTRA_TEXT, text);
} else if(getString(R.string.run_in_sl4a_background).equals(items[which]) || getString(R.string.run_in_sl4a_terminal).equals(items[which])) {
String file = mEditText.getPath();
if("".equals(file))
{
Toast.makeText(JecEditor.this, R.string.run_before_save_file, Toast.LENGTH_LONG).show();
return;
}
ComponentName SL4A_SERVICE_LAUNCHER_COMPONENT_NAME = new ComponentName("com.googlecode.android_scripting", "com.googlecode.android_scripting.activity.ScriptingLayerServiceLauncher");
intent.setComponent(SL4A_SERVICE_LAUNCHER_COMPONENT_NAME);
if(getString(R.string.run_in_sl4a_terminal).equals(items[which])) {
intent.setAction("com.googlecode.android_scripting.action.LAUNCH_FOREGROUND_SCRIPT");
intent.putExtra("com.googlecode.android_scripting.extra.SCRIPT_PATH", file);
} else {
intent.setAction("com.googlecode.android_scripting.action.LAUNCH_BACKGROUND_SCRIPT");
intent.putExtra("com.googlecode.android_scripting.extra.SCRIPT_PATH", file);
}
}
try
{
startActivity(intent);
}catch (Exception e)
{
//Toast.makeText(JecEditor.this, "Exception: "+e.getMessage(), Toast.LENGTH_LONG).show();
JecLog.e(e.getMessage(), e);
}
}
});
builder.show();
break;
case R.id.menu_preferences:
Intent intent = new Intent(JecEditor.this, EditorPreference.class);
startActivity(intent);
break;
case R.id.menu_exit:
JecEditor.this.onFinish();
break;
}
return true;
}
};
private void saveHistory()
{
SharedPreferences sp;
Editor editor;
if(mEditText.getPath() != null && !"".equals(mEditText.getPath()))
{
int selstart = mEditText.getSelectionStart();
int selend = mEditText.getSelectionEnd();
sp = getSharedPreferences(PREF_HISTORY, MODE_PRIVATE);
editor = sp.edit();
editor.putString(mEditText.getPath(), String.format("%d,%d,%d,%d,%s", selstart, selend, System.currentTimeMillis(), mEditText.getLineBreak(), mEditText.getEncoding()));
editor.commit();
}
sp = getSharedPreferences(PREF_LAST_FILE, MODE_PRIVATE);
editor = sp.edit();
editor.clear();
//mPref.edit().putString("last_file", mEditText.getPath()).commit();
ArrayList<String> paths = mTabHost.getAllPath();
//按反序保存,打开后才和之前的顺序一样
int size = paths.size();
//for(String path : paths)
String path;
for(int i=0; i<size; i++)
{
path = paths.get(i);
editor.putString(path, path);
}
editor.commit();
}
}