Java tutorial
/* * Copyright (C) 2016 Jecelyin Peng <jecelyin@gmail.com> * * This file is part of 920 Text Editor. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.jecelyin.editor.v2.ui; import android.Manifest; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.support.annotation.IdRes; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v4.view.GravityCompat; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.text.TextUtils; import android.view.Gravity; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.TextView; import com.afollestad.materialdialogs.folderselector.FolderChooserDialog; import com.azeesoft.lib.colorpicker.ColorPickerDialog; import com.jecelyin.android.file_explorer.FileExplorerActivity; import com.jecelyin.common.utils.CrashDbHelper; import com.jecelyin.common.utils.IOUtils; import com.jecelyin.common.utils.L; import com.jecelyin.common.utils.SysUtils; import com.jecelyin.common.utils.UIUtils; import com.jecelyin.editor.v2.BaseActivity; import com.jecelyin.editor.v2.Pref; import com.jecelyin.editor.v2.R; import com.jecelyin.editor.v2.common.Command; import com.jecelyin.editor.v2.common.SaveListener; import com.jecelyin.editor.v2.highlight.jedit.modes.Catalog; import com.jecelyin.editor.v2.task.CheckUpgradeTask; import com.jecelyin.editor.v2.task.ClusterCommand; import com.jecelyin.editor.v2.ui.dialog.ChangeThemeDialog; import com.jecelyin.editor.v2.ui.dialog.CharsetsDialog; import com.jecelyin.editor.v2.ui.dialog.GotoLineDialog; import com.jecelyin.editor.v2.ui.dialog.InsertDateTimeDialog; import com.jecelyin.editor.v2.ui.dialog.LangListDialog; import com.jecelyin.editor.v2.ui.dialog.RunDialog; import com.jecelyin.editor.v2.ui.dialog.WrapCharDialog; import com.jecelyin.editor.v2.ui.settings.SettingsActivity; import com.jecelyin.editor.v2.utils.AppUtils; import com.jecelyin.editor.v2.utils.DBHelper; import com.jecelyin.editor.v2.view.TabViewPager; import com.jecelyin.editor.v2.view.menu.MenuDef; import com.jecelyin.editor.v2.view.menu.MenuFactory; import com.jecelyin.editor.v2.view.menu.MenuItemInfo; import com.jecelyin.editor.v2.widget.AnyDrawerLayout; import com.jecelyin.editor.v2.widget.SymbolBarLayout; import java.io.File; import java.io.InputStream; import java.util.List; /** * @author Jecelyin Peng <jecelyin@gmail.com> */ public class MainActivity extends BaseActivity implements MenuItem.OnMenuItemClickListener, FolderChooserDialog.FolderCallback, SharedPreferences.OnSharedPreferenceChangeListener { private static final String TAG = MainActivity.class.getName(); private static final int RC_OPEN_FILE = 1; private final static int RC_SAVE = 3; private static final int RC_PERMISSION_STORAGE = 2; Toolbar mToolbar; LinearLayout mLoadingLayout; TabViewPager mTabPager; RecyclerView mMenuRecyclerView; AnyDrawerLayout mDrawerLayout; RecyclerView mTabRecyclerView; TextView mVersionTextView; private TabManager tabManager; private Pref pref; private ClusterCommand clusterCommand; // TabDrawable tabDrawable; private MenuManager menuManager; private FolderChooserDialog.FolderCallback findFolderCallback; private long mExitTime; @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { try { super.onRestoreInstanceState(savedInstanceState); } catch (Exception e) { L.d(e); //ignore exception: Unmarshalling unknown type code 7602281 at offset 58340 } } private void requestWriteExternalStoragePermission() { final String[] permissions = new String[] { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }; if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { UIUtils.showConfirmDialog(this, null, getString(R.string.need_to_enable_read_storage_permissions), new UIUtils.OnClickCallback() { @Override public void onOkClick() { ActivityCompat.requestPermissions(MainActivity.this, permissions, RC_PERMISSION_STORAGE); } @Override public void onCancelClick() { finish(); } }); } else { ActivityCompat.requestPermissions(MainActivity.this, permissions, RC_PERMISSION_STORAGE); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); pref = Pref.getInstance(this); MenuManager.init(this); setContentView(R.layout.main_activity); L.d(TAG, "onCreate"); CrashDbHelper.getInstance(this).close(); //? mToolbar = (Toolbar) findViewById(R.id.toolbar); mLoadingLayout = (LinearLayout) findViewById(R.id.loading_layout); mTabPager = (TabViewPager) findViewById(R.id.tab_pager); mMenuRecyclerView = (RecyclerView) findViewById(R.id.menuRecyclerView); mDrawerLayout = (AnyDrawerLayout) findViewById(R.id.drawer_layout); mTabRecyclerView = (RecyclerView) findViewById(R.id.tabRecyclerView); mVersionTextView = (TextView) findViewById(R.id.versionTextView); findViewById(R.id.changeThemeBtn).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new ChangeThemeDialog(getContext()).show(); } }); SymbolBarLayout symbolBarLayout = (SymbolBarLayout) findViewById(R.id.symbolBarLayout); symbolBarLayout.setOnSymbolCharClickListener(new SymbolBarLayout.OnSymbolCharClickListener() { @Override public void onClick(View v, String text) { insertText(text); } }); if (!AppUtils.verifySign(getContext())) { UIUtils.showConfirmDialog(getContext(), getString(R.string.verify_sign_failure), new UIUtils.OnClickCallback() { @Override public void onOkClick() { SysUtils.startWebView(getContext(), "https://github.com/jecelyin/920-text-editor-v2/releases"); } }); } setStatusBarColor(mDrawerLayout); bindPreferences(); setScreenOrientation(); mDrawerLayout.setEnabled(false); mDrawerLayout.setScrimColor(Color.TRANSPARENT); String version = SysUtils.getVersionName(this); mVersionTextView.setText(version); if (savedInstanceState == null && pref.isAutoCheckUpdates()) { new CheckUpgradeTask(this).checkVersion(version); } if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED || ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) { requestWriteExternalStoragePermission(); } else { start(); } } private void bindPreferences() { mDrawerLayout.setKeepScreenOn(pref.isKeepScreenOn()); //bind other preference // pref.getSharedPreferences().registerOnSharedPreferenceChangeListener(this); //?? // PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this); pref.registerOnSharedPreferenceChangeListener(this); } /** * ?registerOnSharedPreferenceChangeListenerlistenersWeakHashMap * ?registerOnSharedPreferenceChangeListener(new ...) ???? * @param sharedPreferences * @param key */ @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { if (mToolbar == null) return; switch (key) { case Pref.KEY_KEEP_SCREEN_ON: mToolbar.setKeepScreenOn(sharedPreferences.getBoolean(key, false)); break; case Pref.KEY_ENABLE_HIGHLIGHT: Command command = new Command(Command.CommandEnum.HIGHLIGHT); command.object = pref.isHighlight() ? null : Catalog.DEFAULT_MODE_NAME; doClusterCommand(command); break; case Pref.KEY_SCREEN_ORIENTATION: setScreenOrientation(); break; } } private void setScreenOrientation() { int orgi = pref.getScreenOrientation(); if (Pref.SCREEN_ORIENTATION_AUTO == orgi) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); } else if (Pref.SCREEN_ORIENTATION_LANDSCAPE == orgi) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } else if (Pref.SCREEN_ORIENTATION_PORTRAIT == orgi) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { // Write external store permission requires a restart for (int i = 0; i < permissions.length; i++) { //Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permissions[i]) if (grantResults[i] == PackageManager.PERMISSION_DENIED) { requestWriteExternalStoragePermission(); return; } } start(); } private void start() { ((ViewGroup) mLoadingLayout.getParent()).removeView(mLoadingLayout); // inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); mTabPager.setVisibility(View.VISIBLE); initUI(); } private void initUI() { mMenuRecyclerView.setLayoutManager(new LinearLayoutManager(this)); mTabRecyclerView.setLayoutManager(new LinearLayoutManager(this)); mDrawerLayout.setEnabled(true); initToolbar(); if (menuManager == null) menuManager = new MenuManager(this); //??????Activity?onCreate?onNewIntent processIntent(); } private void initToolbar() { Resources res = getResources(); mToolbar.setNavigationIcon(R.drawable.ic_drawer_raw); mToolbar.setNavigationContentDescription(R.string.tab); Menu menu = mToolbar.getMenu(); List<MenuItemInfo> menuItemInfos = MenuFactory.getInstance(this).getToolbarIcon(); for (MenuItemInfo item : menuItemInfos) { MenuItem menuItem = menu.add(MenuDef.GROUP_TOOLBAR, item.getItemId(), Menu.NONE, item.getTitleResId()); menuItem.setIcon(MenuManager.makeToolbarNormalIcon(res, item.getIconResId())); //menuItem.setShortcut() menuItem.setOnMenuItemClickListener(this); menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); } MenuItem menuItem = menu.add(MenuDef.GROUP_TOOLBAR, R.id.m_menu, Menu.NONE, getString(R.string.more_menu)); menuItem.setIcon(R.drawable.ic_right_menu); menuItem.setOnMenuItemClickListener(this); menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); tabManager = new TabManager(this); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); setIntent(intent); processIntent(); } private void processIntent() { try { if (!processIntentImpl()) { UIUtils.alert(getContext(), getString(R.string.cannt_handle_intent_x, getIntent().toString())); } } catch (Throwable e) { L.e(e); UIUtils.alert(getContext(), getString(R.string.handle_intent_x_error, getIntent().toString() + "\n" + e.getMessage())); } } private boolean processIntentImpl() throws Throwable { Intent intent = getIntent(); L.d("intent=" + intent); if (intent == null) return true; //pass hint String action = intent.getAction(); // action == null if change theme if (action == null || Intent.ACTION_MAIN.equals(action)) { return true; } if (Intent.ACTION_VIEW.equals(action) || Intent.ACTION_EDIT.equals(action)) { if (intent.getScheme().equals("content")) { InputStream attachment = getContentResolver().openInputStream(intent.getData()); String text = IOUtils.toString(attachment); openText(text); return true; } else if (intent.getScheme().equals("file")) { Uri mUri = intent.getData(); String file = mUri != null ? mUri.getPath() : null; if (!TextUtils.isEmpty(file)) { openFile(file); return true; } } } else if (Intent.ACTION_SEND.equals(action) && intent.getExtras() != null) { Bundle extras = intent.getExtras(); CharSequence text = extras.getCharSequence(Intent.EXTRA_TEXT); if (text != null) { openText(text); return true; } else { Object stream = extras.get(Intent.EXTRA_STREAM); if (stream != null && stream instanceof Uri) { openFile(((Uri) stream).getPath()); return true; } } } return false; } /** * * @param menuResId * @param status {@link com.jecelyin.editor.v2.view.menu.MenuDef#STATUS_NORMAL}, {@link com.jecelyin.editor.v2.view.menu.MenuDef#STATUS_DISABLED} */ public void setMenuStatus(@IdRes int menuResId, int status) { MenuItem menuItem = mToolbar.getMenu().findItem(menuResId); if (menuItem == null) { throw new RuntimeException("Can't find a menu item"); } Drawable icon = menuItem.getIcon(); if (status == MenuDef.STATUS_DISABLED) { menuItem.setEnabled(false); menuItem.setIcon(MenuManager.makeToolbarDisabledIcon(icon)); } else { menuItem.setEnabled(true); if (menuItem.getGroupId() == MenuDef.GROUP_TOOLBAR) { menuItem.setIcon(MenuManager.makeToolbarNormalIcon(icon)); } else { menuItem.setIcon(MenuManager.makeMenuNormalIcon(icon)); } } } @Override public boolean onMenuItemClick(MenuItem item) { onMenuClick(item.getItemId()); return true; } private void onMenuClick(int id) { Command.CommandEnum commandEnum; closeMenu(); switch (id) { case R.id.m_new: tabManager.newTab(); break; case R.id.m_open: FileExplorerActivity.startPickFileActivity(this, null, RC_OPEN_FILE); break; case R.id.m_goto_line: new GotoLineDialog(this).show(); break; case R.id.m_history: RecentFilesManager rfm = new RecentFilesManager(this); rfm.setOnFileItemClickListener(new RecentFilesManager.OnFileItemClickListener() { @Override public void onClick(String file, String encoding) { openFile(file, encoding, 0); } }); rfm.show(getContext()); break; case R.id.m_wrap: new WrapCharDialog(this).show(); break; case R.id.m_highlight: new LangListDialog(this).show(); break; case R.id.m_menu: hideSoftInput(); mDrawerLayout.openDrawer(GravityCompat.END); break; case R.id.m_save_all: commandEnum = Command.CommandEnum.SAVE; Command command = new Command(commandEnum); command.args.putBoolean(EditorDelegate.KEY_CLUSTER, true); command.object = new SaveListener() { @Override public void onSaved() { doNextCommand(); } }; doClusterCommand(command); break; case R.id.m_fullscreen: boolean fullscreenMode = pref.isFullScreenMode(); pref.setFullScreenMode(!fullscreenMode); UIUtils.toast(this, fullscreenMode ? R.string.disabled_fullscreen_mode_message : R.string.enable_fullscreen_mode_message); break; case R.id.m_readonly: boolean readOnly = !pref.isReadOnly(); pref.setReadOnly(readOnly); mDrawerLayout.setHideBottomDrawer(readOnly); doClusterCommand(new Command(Command.CommandEnum.READONLY_MODE)); break; case R.id.m_encoding: new CharsetsDialog(this).show(); break; case R.id.m_color: if (ensureNotReadOnly()) { ColorPickerDialog colorPickerDialog = ColorPickerDialog.createColorPickerDialog(this); colorPickerDialog.setOnColorPickedListener(new ColorPickerDialog.OnColorPickedListener() { @Override public void onColorPicked(int color, String hexVal) { insertText(hexVal); } }); colorPickerDialog.show(); } break; case R.id.m_datetime: if (ensureNotReadOnly()) { new InsertDateTimeDialog(this).show(); } break; case R.id.m_run: new RunDialog(this).show(); break; case R.id.m_settings: SettingsActivity.startActivity(this); break; case R.id.m_exit: if (tabManager != null) tabManager.closeAllTabAndExitApp(); break; default: commandEnum = MenuFactory.getInstance(this).idToCommandEnum(id); if (commandEnum != Command.CommandEnum.NONE) doCommand(new Command(commandEnum)); } } private boolean ensureNotReadOnly() { boolean readOnly = pref.isReadOnly(); if (readOnly) { UIUtils.toast(this, R.string.readonly_mode_not_support_this_action); return false; } return true; } @Override public boolean onPrepareOptionsMenu(Menu menu) { onMenuClick(R.id.m_menu); return false; } public void closeMenu() { if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) { mDrawerLayout.closeDrawer(GravityCompat.START); } if (mDrawerLayout.isDrawerOpen(GravityCompat.END)) { mDrawerLayout.closeDrawer(GravityCompat.END); } } @Override public void onFolderSelection(@NonNull FolderChooserDialog dialog, @NonNull File file) { if (findFolderCallback != null) { findFolderCallback.onFolderSelection(dialog, file); } } public void setFindFolderCallback(FolderChooserDialog.FolderCallback findFolderCallback) { this.findFolderCallback = findFolderCallback; } private void hideSoftInput() { doCommand(new Command(Command.CommandEnum.HIDE_SOFT_INPUT)); } private void showSoftInput() { doCommand(new Command(Command.CommandEnum.SHOW_SOFT_INPUT)); } /** * ? {@link #doNextCommand} * @param command */ public void doClusterCommand(Command command) { clusterCommand = tabManager.getEditorAdapter().makeClusterCommand(); clusterCommand.setCommand(command); clusterCommand.doNextCommand(); } public void doNextCommand() { if (clusterCommand == null) return; clusterCommand.doNextCommand(); } public void doCommand(Command command) { clusterCommand = null; EditorDelegate editorDelegate = tabManager.getEditorAdapter().getCurrentEditorDelegate(); if (editorDelegate != null) editorDelegate.doCommand(command); } public void startOpenFileSelectorActivity(Intent it) { startActivityForResult(it, RC_OPEN_FILE); } public void startPickPathActivity(String path, String encoding) { FileExplorerActivity.startPickPathActivity(this, path, encoding, RC_SAVE); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode != RESULT_OK) return; switch (requestCode) { case RC_OPEN_FILE: if (data == null) break; openFile(FileExplorerActivity.getFile(data), FileExplorerActivity.getFileEncoding(data), data.getIntExtra("offset", 0)); break; case RC_SAVE: String file = FileExplorerActivity.getFile(data); String encoding = FileExplorerActivity.getFileEncoding(data); tabManager.getEditorAdapter().getCurrentEditorDelegate().saveTo(new File(file), encoding); break; } } public static Intent getOpenFileIntent(File file, int offset) { Intent intent = new Intent(); intent.putExtra("file", file.getPath()); intent.putExtra("offset", offset); return intent; } private void openText(CharSequence content) { if (TextUtils.isEmpty(content)) return; tabManager.newTab(content); } private void openFile(String file) { openFile(file, null, 0); } public void openFile(String file, String encoding, int offset) { if (TextUtils.isEmpty(file)) return; File f = new File(file); if (!f.isFile()) { UIUtils.toast(this, R.string.file_not_exists); return; } if (!tabManager.newTab(f, offset, encoding)) return; DBHelper.getInstance(this).addRecentFile(file, encoding); } public void insertText(CharSequence text) { if (text == null) return; Command c = new Command(Command.CommandEnum.INSERT_TEXT); c.object = text; doCommand(c); } public TabManager getTabManager() { return tabManager; } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) { if (mDrawerLayout != null) { if (mDrawerLayout.isDrawerOpen(Gravity.LEFT)) { mDrawerLayout.closeDrawer(Gravity.LEFT); return true; } if (mDrawerLayout.isDrawerOpen(Gravity.RIGHT)) { mDrawerLayout.closeDrawer(Gravity.RIGHT); return true; } } if ((System.currentTimeMillis() - mExitTime) > 2000) { UIUtils.toast(getContext(), R.string.press_again_will_exit); mExitTime = System.currentTimeMillis(); return true; } else { return tabManager == null || tabManager.closeAllTabAndExitApp(); } } return super.onKeyDown(keyCode, event); } }