Fix: Crash when click context menu too fast.

This commit is contained in:
zt515
2017-07-08 00:23:43 +08:00
parent 1812d348df
commit c9de9541bf
10 changed files with 121 additions and 35 deletions

View File

@ -17,8 +17,8 @@ android {
applicationId "io.neoterm" applicationId "io.neoterm"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 25 targetSdkVersion 25
versionCode 5 versionCode 6
versionName "1.1.3" versionName "1.1.4"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
resConfigs "zh" resConfigs "zh"
externalNativeBuild { externalNativeBuild {

View File

@ -10,14 +10,12 @@
<application <application
android:name=".App" android:name=".App"
android:allowBackup="true" android:allowBackup="true"
android:fullBackupContent="@xml/backup_config"
android:extractNativeLibs="true" android:extractNativeLibs="true"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme"> android:theme="@style/AppTheme">
<activity
android:name=".ui.setup.SetupActivity"
android:theme="@style/AppTheme.NoActionBar" />
<activity <activity
android:name=".ui.NeoTermActivity" android:name=".ui.NeoTermActivity"
android:configChanges="orientation|keyboardHidden" android:configChanges="orientation|keyboardHidden"
@ -31,6 +29,9 @@
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name=".ui.setup.SetupActivity"
android:theme="@style/AppTheme.NoActionBar" />
<activity <activity
android:name=".ui.pm.PackageManagerActivity" android:name=".ui.pm.PackageManagerActivity"
android:label="@string/package_settings" android:label="@string/package_settings"
@ -50,9 +51,21 @@
android:name=".ui.settings.UISettingsActivity" android:name=".ui.settings.UISettingsActivity"
android:theme="@style/Theme.AppCompat" /> android:theme="@style/Theme.AppCompat" />
<activity-alias
android:name=".NeoLotMainActivity"
android:targetActivity="io.neoterm.ui.NeoTermActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.IOT_LAUNCHER"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity-alias>
<service <service
android:name=".services.NeoTermService" android:name=".services.NeoTermService"
android:enabled="true" /> android:enabled="true" />
<meta-data android:name="com.sec.android.support.multiwindow" android:value="true" />
</application> </application>
</manifest> </manifest>

View File

@ -421,8 +421,14 @@ public final class TerminalEmulator {
if (codePoint >= 0x80 && codePoint <= 0x9F) { if (codePoint >= 0x80 && codePoint <= 0x9F) {
// Sequence decoded to a C1 control character which is the same as escape followed by // Sequence decoded to a C1 control character which is the same as escape followed by
// ((code & 0x7F) + 0x40). // ((code & 0x7F) + 0x40).
processCodePoint(/* escape (hexadecimal=0x1B, octal=033): */27); // processCodePoint(/* escape (hexadecimal=0x1B, octal=033): */27);
processCodePoint((codePoint & 0x7F) + 0x40); // processCodePoint((codePoint & 0x7F) + 0x40);
// Sequence decoded to a C1 control character which we ignore. They are
// not used nowadays and increases the risk of messing up the terminal state
// on binary input. XTerm does not allow them in utf-8:
// "It is not possible to use a C1 control obtained from decoding the
// UTF-8 text" - http://invisible-island.net/xterm/ctlseqs/ctlseqs.htm
} else { } else {
switch (Character.getType(codePoint)) { switch (Character.getType(codePoint)) {
case Character.UNASSIGNED: case Character.UNASSIGNED:

View File

@ -114,7 +114,7 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreference
TabSwitcher.setupWithMenu(tabSwitcher, toolbar.menu, View.OnClickListener { TabSwitcher.setupWithMenu(tabSwitcher, toolbar.menu, View.OnClickListener {
val imm = this@NeoTermActivity.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager val imm = this@NeoTermActivity.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
if (!tabSwitcher.isSwitcherShown) { if (!tabSwitcher.isSwitcherShown) {
if (imm.isActive) { if (imm.isActive && tabSwitcher.selectedTab is TermTab) {
val tab = tabSwitcher.selectedTab as TermTab val tab = tabSwitcher.selectedTab as TermTab
tab.hideIme() tab.hideIme()
} }

View File

@ -68,10 +68,7 @@ class PackageManagerActivity : AppCompatActivity(), SearchView.OnQueryTextListen
.setTitle(model.packageInfo.packageName) .setTitle(model.packageInfo.packageName)
.setMessage(model.getPackageDetails(this@PackageManagerActivity)) .setMessage(model.getPackageDetails(this@PackageManagerActivity))
.setPositiveButton(R.string.install, { _, _ -> .setPositiveButton(R.string.install, { _, _ ->
TerminalDialog(this@PackageManagerActivity) installPackage(model.packageInfo.packageName)
.execute(NeoTermPath.APT_BIN_PATH,
arrayOf("apt", "install", "-y", model.packageInfo.packageName!!))
.show("Installing ${model.packageInfo.packageName}")
}) })
.setNegativeButton(android.R.string.no, null) .setNegativeButton(android.R.string.no, null)
.show() .show()
@ -89,6 +86,20 @@ class PackageManagerActivity : AppCompatActivity(), SearchView.OnQueryTextListen
refreshPackageList() refreshPackageList()
} }
private fun installPackage(packageName: String?) {
if (packageName != null) {
TerminalDialog(this@PackageManagerActivity)
.execute(NeoTermPath.APT_BIN_PATH,
arrayOf("apt", "install", "-y", packageName))
.onFinish(object : TerminalDialog.SessionFinishedCallback {
override fun onSessionFinished(dialog: TerminalDialog, finishedSession: TerminalSession?) {
dialog.setTitle(getString(R.string.done))
}
})
.show("Installing $packageName")
}
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean { override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.menu_pm, menu) menuInflater.inflate(R.menu.menu_pm, menu)
val searchItem = menu!!.findItem(R.id.action_search) val searchItem = menu!!.findItem(R.id.action_search)
@ -170,6 +181,7 @@ class PackageManagerActivity : AppCompatActivity(), SearchView.OnQueryTextListen
TerminalDialog(this@PackageManagerActivity) TerminalDialog(this@PackageManagerActivity)
.onFinish(object : TerminalDialog.SessionFinishedCallback { .onFinish(object : TerminalDialog.SessionFinishedCallback {
override fun onSessionFinished(dialog: TerminalDialog, finishedSession: TerminalSession?) { override fun onSessionFinished(dialog: TerminalDialog, finishedSession: TerminalSession?) {
dialog.dismiss()
refreshPackageList() refreshPackageList()
} }
}) })

View File

@ -4,6 +4,7 @@ import android.app.AlertDialog
import android.os.Bundle import android.os.Bundle
import android.support.v7.app.AppCompatPreferenceActivity import android.support.v7.app.AppCompatPreferenceActivity
import android.view.MenuItem import android.view.MenuItem
import android.widget.Toast
import io.neoterm.R import io.neoterm.R
import io.neoterm.backend.TerminalSession import io.neoterm.backend.TerminalSession
import io.neoterm.preference.NeoPreference import io.neoterm.preference.NeoPreference
@ -39,9 +40,11 @@ class UISettingsActivity : AppCompatPreferenceActivity() {
TerminalDialog(this) TerminalDialog(this)
.onFinish(object : TerminalDialog.SessionFinishedCallback { .onFinish(object : TerminalDialog.SessionFinishedCallback {
override fun onSessionFinished(dialog: TerminalDialog, finishedSession: TerminalSession?) { override fun onSessionFinished(dialog: TerminalDialog, finishedSession: TerminalSession?) {
dialog.dismiss()
if (finishedSession?.exitStatus == 0) { if (finishedSession?.exitStatus == 0) {
dialog.dismiss()
NeoPreference.store(R.string.key_general_shell, "zsh") NeoPreference.store(R.string.key_general_shell, "zsh")
} else {
dialog.setTitle(getString(R.string.error))
} }
} }
}) })

View File

@ -1,7 +1,6 @@
package io.neoterm.ui.setup package io.neoterm.ui.setup
import android.app.Activity import android.app.Activity
import android.content.DialogInterface
import android.os.Bundle import android.os.Bundle
import android.support.v4.content.ContextCompat import android.support.v4.content.ContextCompat
import android.support.v7.app.AlertDialog import android.support.v7.app.AlertDialog
@ -63,11 +62,17 @@ class SetupActivity : AppCompatActivity() {
} }
TerminalDialog(this@SetupActivity) TerminalDialog(this@SetupActivity)
.onDismiss(DialogInterface.OnCancelListener { .onFinish(object : TerminalDialog.SessionFinishedCallback {
if (withShell != null) { override fun onSessionFinished(dialog: TerminalDialog, finishedSession: TerminalSession?) {
NeoPreference.store(R.string.key_general_shell, withShell!!) if (finishedSession?.exitStatus == 0) {
dialog.dismiss()
if (withShell != null) {
NeoPreference.store(R.string.key_general_shell, withShell!!)
}
} else {
dialog.setTitle(getString(R.string.error))
}
} }
finish()
}) })
.execute(NeoTermPath.APT_BIN_PATH, packageList.toTypedArray()) .execute(NeoTermPath.APT_BIN_PATH, packageList.toTypedArray())
.show(getString(R.string.installer_message)) .show(getString(R.string.installer_message))
@ -106,11 +111,13 @@ class SetupActivity : AppCompatActivity() {
TerminalDialog(this) TerminalDialog(this)
.onFinish(object : TerminalDialog.SessionFinishedCallback { .onFinish(object : TerminalDialog.SessionFinishedCallback {
override fun onSessionFinished(dialog: TerminalDialog, finishedSession: TerminalSession?) { override fun onSessionFinished(dialog: TerminalDialog, finishedSession: TerminalSession?) {
dialog.dismiss()
nextButton.visibility = View.VISIBLE nextButton.visibility = View.VISIBLE
val exit = finishedSession?.exitStatus ?: 1 val exit = finishedSession?.exitStatus ?: 1
if (exit == 0) { if (exit == 0) {
dialog.dismiss()
aptUpdated = true aptUpdated = true
} else {
dialog.setTitle(getString(R.string.error))
} }
} }
}) })

View File

@ -66,6 +66,11 @@ class TerminalDialog(val context: Context) {
return this return this
} }
fun setTitle(title: String?) : TerminalDialog {
dialog?.setTitle(title)
return this
}
fun onFinish(finishedCallback: SessionFinishedCallback):TerminalDialog { fun onFinish(finishedCallback: SessionFinishedCallback):TerminalDialog {
this.sessionFinishedCallback = finishedCallback this.sessionFinishedCallback = finishedCallback
return this return this

View File

@ -36,22 +36,32 @@ import io.neoterm.backend.TerminalBuffer;
import io.neoterm.backend.TerminalEmulator; import io.neoterm.backend.TerminalEmulator;
import io.neoterm.backend.TerminalSession; import io.neoterm.backend.TerminalSession;
/** View displaying and interacting with a {@link TerminalSession}. */ /**
* View displaying and interacting with a {@link TerminalSession}.
*/
public final class TerminalView extends View { public final class TerminalView extends View {
/** Log view key and IME events. */ /**
* Log view key and IME events.
*/
private static final boolean LOG_KEY_EVENTS = false; private static final boolean LOG_KEY_EVENTS = false;
/** The currently displayed terminal session, whose emulator is {@link #mEmulator}. */ /**
* The currently displayed terminal session, whose emulator is {@link #mEmulator}.
*/
TerminalSession mTermSession; TerminalSession mTermSession;
/** Our terminal emulator whose session is {@link #mTermSession}. */ /**
* Our terminal emulator whose session is {@link #mTermSession}.
*/
TerminalEmulator mEmulator; TerminalEmulator mEmulator;
TerminalRenderer mRenderer; TerminalRenderer mRenderer;
TerminalViewClient mClient; TerminalViewClient mClient;
/** The top row of text to display. Ranges from -activeTranscriptRows to 0. */ /**
* The top row of text to display. Ranges from -activeTranscriptRows to 0.
*/
int mTopRow; int mTopRow;
boolean mIsSelectingText = false, mIsDraggingLeftSelection, mInitialTextSelection; boolean mIsSelectingText = false, mIsDraggingLeftSelection, mInitialTextSelection;
@ -63,17 +73,25 @@ public final class TerminalView extends View {
float mScaleFactor = 1.f; float mScaleFactor = 1.f;
/* final */ GestureAndScaleRecognizer mGestureRecognizer; /* final */ GestureAndScaleRecognizer mGestureRecognizer;
/** Keep track of where mouse touch event started which we report as mouse scroll. */ /**
* Keep track of where mouse touch event started which we report as mouse scroll.
*/
private int mMouseScrollStartX = -1, mMouseScrollStartY = -1; private int mMouseScrollStartX = -1, mMouseScrollStartY = -1;
/** Keep track of the time when a touch event leading to sending mouse scroll events started. */ /**
* Keep track of the time when a touch event leading to sending mouse scroll events started.
*/
private long mMouseStartDownTime = -1; private long mMouseStartDownTime = -1;
/* final */ Scroller mScroller; /* final */ Scroller mScroller;
/** What was left in from scrolling movement. */ /**
* What was left in from scrolling movement.
*/
float mScrollRemainder; float mScrollRemainder;
/** If non-zero, this is the last unicode code point received if that was a combining character. */ /**
* If non-zero, this is the last unicode code point received if that was a combining character.
*/
int mCombiningAccent; int mCombiningAccent;
int mTextSize; int mTextSize;
@ -429,7 +447,9 @@ public final class TerminalView extends View {
return true; return true;
} }
/** Send a single mouse event code to the terminal. */ /**
* Send a single mouse event code to the terminal.
*/
void sendMouseEventCode(MotionEvent e, int button, boolean pressed) { void sendMouseEventCode(MotionEvent e, int button, boolean pressed) {
int x = (int) (e.getX() / mRenderer.mFontWidth) + 1; int x = (int) (e.getX() / mRenderer.mFontWidth) + 1;
int y = (int) ((e.getY() - mRenderer.mFontLineSpacingAndAscent) / mRenderer.mFontLineSpacing) + 1; int y = (int) ((e.getY() - mRenderer.mFontLineSpacingAndAscent) / mRenderer.mFontLineSpacing) + 1;
@ -446,7 +466,9 @@ public final class TerminalView extends View {
mEmulator.sendMouseEvent(button, x, y, pressed); mEmulator.sendMouseEvent(button, x, y, pressed);
} }
/** Perform a scroll, either from dragging the screen or by scrolling a mouse wheel. */ /**
* Perform a scroll, either from dragging the screen or by scrolling a mouse wheel.
*/
void doScroll(MotionEvent event, int rowsDown) { void doScroll(MotionEvent event, int rowsDown) {
boolean up = rowsDown < 0; boolean up = rowsDown < 0;
int amount = Math.abs(rowsDown); int amount = Math.abs(rowsDown);
@ -464,7 +486,9 @@ public final class TerminalView extends View {
} }
} }
/** Overriding {@link View#onGenericMotionEvent(MotionEvent)}. */ /**
* Overriding {@link View#onGenericMotionEvent(MotionEvent)}.
*/
@Override @Override
public boolean onGenericMotionEvent(MotionEvent event) { public boolean onGenericMotionEvent(MotionEvent event) {
if (mEmulator != null && event.isFromSource(InputDevice.SOURCE_MOUSE) && event.getAction() == MotionEvent.ACTION_SCROLL) { if (mEmulator != null && event.isFromSource(InputDevice.SOURCE_MOUSE) && event.getAction() == MotionEvent.ACTION_SCROLL) {
@ -656,7 +680,7 @@ public final class TerminalView extends View {
void inputCodePoint(int codePoint, boolean controlDownFromEvent, boolean leftAltDownFromEvent) { void inputCodePoint(int codePoint, boolean controlDownFromEvent, boolean leftAltDownFromEvent) {
if (LOG_KEY_EVENTS) { if (LOG_KEY_EVENTS) {
Log.i(EmulatorDebug.LOG_TAG, "inputCodePoint(codePoint=" + codePoint + ", controlDownFromEvent=" + controlDownFromEvent + ", leftAltDownFromEvent=" Log.i(EmulatorDebug.LOG_TAG, "inputCodePoint(codePoint=" + codePoint + ", controlDownFromEvent=" + controlDownFromEvent + ", leftAltDownFromEvent="
+ leftAltDownFromEvent + ")"); + leftAltDownFromEvent + ")");
} }
final boolean controlDown = controlDownFromEvent || mClient.readControlKey(); final boolean controlDown = controlDownFromEvent || mClient.readControlKey();
@ -709,7 +733,9 @@ public final class TerminalView extends View {
} }
} }
/** Input the specified keyCode if applicable and return if the input was consumed. */ /**
* Input the specified keyCode if applicable and return if the input was consumed.
*/
public boolean handleKeyCode(int keyCode, int keyMod) { public boolean handleKeyCode(int keyCode, int keyMod) {
TerminalEmulator term = mTermSession.getEmulator(); TerminalEmulator term = mTermSession.getEmulator();
String code = KeyHandler.getCode(keyCode, keyMod, term.isCursorKeysApplicationMode(), term.isKeypadApplicationMode()); String code = KeyHandler.getCode(keyCode, keyMod, term.isCursorKeysApplicationMode(), term.isKeypadApplicationMode());
@ -751,7 +777,9 @@ public final class TerminalView extends View {
updateSize(); updateSize();
} }
/** Check if the terminal size in rows and columns should be updated. */ /**
* Check if the terminal size in rows and columns should be updated.
*/
public void updateSize() { public void updateSize() {
int viewWidth = getWidth(); int viewWidth = getWidth();
int viewHeight = getHeight(); int viewHeight = getHeight();
@ -795,7 +823,9 @@ public final class TerminalView extends View {
} }
} }
/** Toggle text selection mode in the view. */ /**
* Toggle text selection mode in the view.
*/
@TargetApi(23) @TargetApi(23)
public void toggleSelectingText(MotionEvent ev) { public void toggleSelectingText(MotionEvent ev) {
mIsSelectingText = !mIsSelectingText; mIsSelectingText = !mIsSelectingText;
@ -851,6 +881,11 @@ public final class TerminalView extends View {
@Override @Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) { public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
if (!mIsSelectingText) {
// Fix issue where the dialog is pressed while being dismissed.
return true;
}
switch (item.getItemId()) { switch (item.getItemId()) {
case 1: case 1:
String selectedText = mEmulator.getSelectedText(mSelX1, mSelY1, mSelX2, mSelY2).trim(); String selectedText = mEmulator.getSelectedText(mSelX1, mSelY1, mSelX2, mSelY2).trim();

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<full-backup-content>
<!-- See https://developer.android.com/training/backup/autosyncapi.html -->
<include domain="file" path="home/auto_backup" />
</full-backup-content>