Feature: Full Screen Mode

This commit is contained in:
zt515
2017-06-18 03:12:23 +08:00
parent 576a8cd4a0
commit f045c0687a
11 changed files with 224 additions and 76 deletions

View File

@ -8,7 +8,6 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application <application
android:name=".NeoApp"
android:allowBackup="true" android:allowBackup="true"
android:extractNativeLibs="true" android:extractNativeLibs="true"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"

View File

@ -1,21 +0,0 @@
package io.neoterm;
import android.app.Application;
/**
* @author kiva
*/
public class NeoApp extends Application {
private static NeoApp app;
@Override
public void onCreate() {
super.onCreate();
app = this;
}
public static NeoApp get() {
return app;
}
}

View File

@ -1,7 +1,7 @@
package io.neoterm.customize.font package io.neoterm.customize.font
import android.content.Context
import android.graphics.Typeface import android.graphics.Typeface
import io.neoterm.NeoApp
/** /**
* @author kiva * @author kiva
@ -9,10 +9,11 @@ import io.neoterm.NeoApp
object FontManager { object FontManager {
private var DEFAULT_FONT: Typeface? = null private var DEFAULT_FONT: Typeface? = null
fun getDefaultFont(): Typeface { fun init(context: Context) {
if (DEFAULT_FONT == null) { DEFAULT_FONT = Typeface.createFromAsset(context.assets, "font.ttf")
DEFAULT_FONT = Typeface.createFromAsset(NeoApp.get().assets, "font.ttf")
} }
fun getDefaultFont(): Typeface {
return DEFAULT_FONT!! return DEFAULT_FONT!!
} }
} }

View File

@ -5,6 +5,7 @@ import android.content.*
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Bundle import android.os.Bundle
import android.os.IBinder import android.os.IBinder
import android.preference.PreferenceManager
import android.support.v4.content.ContextCompat import android.support.v4.content.ContextCompat
import android.support.v4.view.OnApplyWindowInsetsListener import android.support.v4.view.OnApplyWindowInsetsListener
import android.support.v4.view.ViewCompat import android.support.v4.view.ViewCompat
@ -15,10 +16,11 @@ import android.view.View
import android.view.WindowManager import android.view.WindowManager
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.ImageButton import android.widget.ImageButton
import android.widget.Toast
import de.mrapp.android.tabswitcher.* import de.mrapp.android.tabswitcher.*
import de.mrapp.android.tabswitcher.view.TabSwitcherButton
import io.neoterm.R import io.neoterm.R
import io.neoterm.backend.TerminalSession import io.neoterm.backend.TerminalSession
import io.neoterm.customize.font.FontManager
import io.neoterm.customize.shortcut.ShortcutConfigLoader import io.neoterm.customize.shortcut.ShortcutConfigLoader
import io.neoterm.customize.shortcut.builtin.BuiltinShortcutKeys import io.neoterm.customize.shortcut.builtin.BuiltinShortcutKeys
import io.neoterm.installer.BaseFileInstaller import io.neoterm.installer.BaseFileInstaller
@ -26,63 +28,24 @@ import io.neoterm.preference.NeoPermission
import io.neoterm.preference.NeoTermPreference import io.neoterm.preference.NeoTermPreference
import io.neoterm.services.NeoTermService import io.neoterm.services.NeoTermService
import io.neoterm.ui.settings.SettingActivity import io.neoterm.ui.settings.SettingActivity
import io.neoterm.utils.NeoTermFullScreen
import io.neoterm.view.tab.* import io.neoterm.view.tab.*
import android.content.Intent
class NeoTermActivity : AppCompatActivity(), ServiceConnection {
class NeoTermActivity : AppCompatActivity(), ServiceConnection, SharedPreferences.OnSharedPreferenceChangeListener {
lateinit var tabSwitcher: TabSwitcher lateinit var tabSwitcher: TabSwitcher
var systemShell = true var systemShell = true
var termService: NeoTermService? = null var termService: NeoTermService? = null
var restartRequired = false
override fun onServiceDisconnected(name: ComponentName?) {
if (termService != null) {
finish()
}
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
termService = (service as NeoTermService.NeoTermBinder).service
if (termService == null) {
finish()
return
}
var resultListener: BaseFileInstaller.ResultListener? = null
resultListener = BaseFileInstaller.ResultListener { error ->
if (error == null) {
initShortcutKeys()
systemShell = false
if (!termService!!.sessions.isEmpty()) {
for (session in termService!!.sessions) {
addNewSession(session)
}
switchToSession(getStoredCurrentSessionOrLast())
} else {
tabSwitcher.showSwitcher()
addNewSession("NeoTerm #0", systemShell, createRevealAnimation())
}
} else {
AlertDialog.Builder(this@NeoTermActivity)
.setTitle("Error")
.setMessage(error.toString())
.setNegativeButton("System Shell", { _, _ ->
tabSwitcher.showSwitcher()
addNewSession("NeoTerm #0", systemShell, createRevealAnimation())
})
.setPositiveButton("Retry", { dialog, _ ->
dialog.dismiss()
BaseFileInstaller.installBaseFiles(this@NeoTermActivity, resultListener)
}).show()
}
}
BaseFileInstaller.installBaseFiles(this, resultListener)
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
NeoPermission.initAppPermission(this, NeoPermission.REQUEST_APP_PERMISSION) NeoPermission.initAppPermission(this, NeoPermission.REQUEST_APP_PERMISSION)
FontManager.init(this)
NeoTermPreference.init(this) NeoTermPreference.init(this)
if (NeoTermPreference.loadBoolean(R.string.key_ui_fullscreen, false)) { if (NeoTermPreference.loadBoolean(R.string.key_ui_fullscreen, false)) {
@ -92,6 +55,17 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection {
setContentView(R.layout.tab_main) setContentView(R.layout.tab_main)
val fullScreenHelper = NeoTermFullScreen.injectActivity(this)
fullScreenHelper.setKeyBoardListener({ isShow, _ ->
if (NeoTermPreference.loadBoolean(R.string.key_ui_fullscreen, false)
|| NeoTermPreference.loadBoolean(R.string.key_ui_hide_toolbar, false)) {
if (tabSwitcher.selectedTab is TermTab) {
val tab = tabSwitcher.selectedTab as TermTab
tab.toolbar?.visibility = if (isShow) View.GONE else View.VISIBLE
}
}
})
tabSwitcher = findViewById(R.id.tab_switcher) as TabSwitcher tabSwitcher = findViewById(R.id.tab_switcher) as TabSwitcher
tabSwitcher.decorator = TermTabDecorator(this) tabSwitcher.decorator = TermTabDecorator(this)
ViewCompat.setOnApplyWindowInsetsListener(tabSwitcher, createWindowInsetsListener()) ViewCompat.setOnApplyWindowInsetsListener(tabSwitcher, createWindowInsetsListener())
@ -114,6 +88,13 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
if (restartRequired) {
restartRequired = false
this.recreate()
}
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener(this)
tabSwitcher.addListener(object : TabSwitcherListener { tabSwitcher.addListener(object : TabSwitcherListener {
private var tabSwitcherButtonInit = false private var tabSwitcherButtonInit = false
@ -162,6 +143,8 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection {
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
PreferenceManager.getDefaultSharedPreferences(this)
.unregisterOnSharedPreferenceChangeListener(this)
if (termService != null) { if (termService != null) {
if (termService!!.sessions.isEmpty()) { if (termService!!.sessions.isEmpty()) {
termService!!.stopSelf() termService!!.stopSelf()
@ -189,7 +172,7 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection {
NeoPermission.REQUEST_APP_PERMISSION -> { NeoPermission.REQUEST_APP_PERMISSION -> {
if (grantResults.isEmpty() if (grantResults.isEmpty()
|| grantResults[0] != PackageManager.PERMISSION_GRANTED) { || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
AlertDialog.Builder(this).setMessage("应用无法取得必须的权限,正在退出") AlertDialog.Builder(this).setMessage(R.string.permission_denied)
.setPositiveButton(android.R.string.ok, { _: DialogInterface, _: Int -> .setPositiveButton(android.R.string.ok, { _: DialogInterface, _: Int ->
finish() finish()
}) })
@ -200,6 +183,58 @@ class NeoTermActivity : AppCompatActivity(), ServiceConnection {
} }
} }
override fun onServiceDisconnected(name: ComponentName?) {
if (termService != null) {
finish()
}
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
termService = (service as NeoTermService.NeoTermBinder).service
if (termService == null) {
finish()
return
}
var resultListener: BaseFileInstaller.ResultListener? = null
resultListener = BaseFileInstaller.ResultListener { error ->
if (error == null) {
initShortcutKeys()
systemShell = false
if (!termService!!.sessions.isEmpty()) {
for (session in termService!!.sessions) {
addNewSession(session)
}
switchToSession(getStoredCurrentSessionOrLast())
} else {
tabSwitcher.showSwitcher()
addNewSession("NeoTerm #0", systemShell, createRevealAnimation())
}
} else {
AlertDialog.Builder(this@NeoTermActivity)
.setTitle(R.string.error)
.setMessage(error.toString())
.setNegativeButton(R.string.use_system_shell, { _, _ ->
tabSwitcher.showSwitcher()
addNewSession("NeoTerm #0", systemShell, createRevealAnimation())
})
.setPositiveButton(R.string.retry, { dialog, _ ->
dialog.dismiss()
BaseFileInstaller.installBaseFiles(this@NeoTermActivity, resultListener)
}).show()
}
}
BaseFileInstaller.installBaseFiles(this, resultListener)
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
if (key == getString(R.string.key_ui_fullscreen)) {
Toast.makeText(this, R.string.fullscreen_mode_changed, Toast.LENGTH_SHORT).show()
restartRequired = true
}
}
private fun addNewSession(session: TerminalSession?) { private fun addNewSession(session: TerminalSession?) {
if (session == null) { if (session == null) {
return return

View File

@ -0,0 +1,112 @@
package io.neoterm.utils;
import android.app.Activity;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
/**
* Helper class to "adjustResize" Activity when we are in full screen mode and check IME status.
* Android Bug 5497: https://code.google.com/p/android/issues/detail?id=5497
* author @kiva
*/
public class NeoTermFullScreen {
public static NeoTermFullScreen injectActivity(Activity activity) {
return new NeoTermFullScreen(activity);
}
public interface KeyBoardListener {
/**
* call back
*
* @param isShow true is show else hidden
* @param keyboardHeight keyboard height
*/
void onKeyboardChange(boolean isShow, int keyboardHeight);
}
private View mChildOfContent;
private int usableHeightPrevious;
private FrameLayout.LayoutParams frameLayoutParams;
private int mOriginHeight;
private int mPreHeight;
private KeyBoardListener mKeyBoardListener;
public void setKeyBoardListener(KeyBoardListener mKeyBoardListener) {
this.mKeyBoardListener = mKeyBoardListener;
}
private NeoTermFullScreen(Activity activity) {
FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
mChildOfContent = content.getChildAt(0);
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
possiblyResizeChildOfContent();
monitorImeStatus();
}
});
frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();
}
private void monitorImeStatus() {
int currHeight = mChildOfContent.getHeight();
if (currHeight == 0) {
// First time
return;
}
boolean hasChange = false;
if (mPreHeight == 0) {
mPreHeight = currHeight;
mOriginHeight = currHeight;
} else {
if (mPreHeight != currHeight) {
hasChange = true;
mPreHeight = currHeight;
} else {
hasChange = false;
}
}
if (hasChange) {
boolean isShow;
int keyboardHeight = 0;
if (Math.abs(mOriginHeight - currHeight) < 100) {
//hidden
isShow = false;
} else {
//show
keyboardHeight = mOriginHeight - currHeight;
isShow = true;
}
if (mKeyBoardListener != null) {
mKeyBoardListener.onKeyboardChange(isShow, keyboardHeight);
}
}
}
private void possiblyResizeChildOfContent() {
int usableHeightNow = computeUsableHeight();
if (usableHeightNow != usableHeightPrevious) {
int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
int heightDifference = usableHeightSansKeyboard - usableHeightNow;
if (heightDifference > (usableHeightSansKeyboard/4)) {
// keyboard probably just became visible
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
} else {
// keyboard probably just became hidden
frameLayoutParams.height = usableHeightSansKeyboard;
}
mChildOfContent.requestLayout();
usableHeightPrevious = usableHeightNow;
}
}
private int computeUsableHeight() {
Rect r = new Rect();
mChildOfContent.getWindowVisibleDisplayFrame(r);
return (r.bottom - r.top);
}
}

View File

@ -36,7 +36,6 @@ class TermTabDecorator(val context: NeoTermActivity) : TabSwitcherDecorator() {
} }
context.tabSwitcher.showSwitcher() context.tabSwitcher.showSwitcher()
}) })
return view return view
} }

View File

@ -20,6 +20,8 @@
<string name="pref_ui_color_scheme">配色方案</string> <string name="pref_ui_color_scheme">配色方案</string>
<string name="pref_ui_font">字体</string> <string name="pref_ui_font">字体</string>
<string name="pref_ui_fullscreen">全屏</string> <string name="pref_ui_fullscreen">全屏</string>
<string name="pref_ui_hide_toolbar">隐藏标题栏</string>
<string name="pref_ui_hide_toolbar_desc">键盘显示时隐藏标题栏</string>
<string name="pref_ui_suggestions">显示建议 (需要 oh-my-zsh)</string> <string name="pref_ui_suggestions">显示建议 (需要 oh-my-zsh)</string>
<string name="pref_ui_suggestions_desc">使用一些软件时,屏幕下方显示快捷键</string> <string name="pref_ui_suggestions_desc">使用一些软件时,屏幕下方显示快捷键</string>
<string name="pref_ui_wide_char_weight_explicit">为宽字符设置权重</string> <string name="pref_ui_wide_char_weight_explicit">为宽字符设置权重</string>
@ -31,4 +33,9 @@
<string name="ui_settings">界面设置</string> <string name="ui_settings">界面设置</string>
<string name="shell_not_found">Shell %s 未找到, 请先安装.</string> <string name="shell_not_found">Shell %s 未找到, 请先安装.</string>
<string name="installer_message">正在安装</string> <string name="installer_message">正在安装</string>
<string name="fullscreen_mode_changed">全屏模式已改变,请重启 NeoTerm</string>
<string name="permission_denied">NeoTerm 无法取得必需的权限,正在退出</string>
<string name="error">还有这种操作?</string>
<string name="use_system_shell">使用系统Shell</string>
<string name="retry">重试</string>
</resources> </resources>

View File

@ -6,6 +6,7 @@
<string name="key_general_shell" translatable="false">neoterm_general_shell</string> <string name="key_general_shell" translatable="false">neoterm_general_shell</string>
<string name="key_ui_fullscreen" translatable="false">neoterm_ui_fullscreen</string> <string name="key_ui_fullscreen" translatable="false">neoterm_ui_fullscreen</string>
<string name="key_ui_hide_toolbar" translatable="false">neoterm_ui_hide_toolbar</string>
<string name="key_ui_font" translatable="false">neoterm_ui_font</string> <string name="key_ui_font" translatable="false">neoterm_ui_font</string>
<string name="key_ui_color_scheme" translatable="false">neoterm_ui_color_scheme</string> <string name="key_ui_color_scheme" translatable="false">neoterm_ui_color_scheme</string>
<string name="key_ui_next_tab_anim" translatable="false">neoterm_ui_next_tab_anim</string> <string name="key_ui_next_tab_anim" translatable="false">neoterm_ui_next_tab_anim</string>

View File

@ -24,6 +24,8 @@
<string name="pref_general_shell" translatable="false">Shell</string> <string name="pref_general_shell" translatable="false">Shell</string>
<string name="pref_general_shell_desc">Which shell should we use when login</string> <string name="pref_general_shell_desc">Which shell should we use when login</string>
<string name="pref_ui_fullscreen">Full Screen</string> <string name="pref_ui_fullscreen">Full Screen</string>
<string name="pref_ui_hide_toolbar">Hide Toolbar</string>
<string name="pref_ui_hide_toolbar_desc">Hide toolbar when keyboard is showing</string>
<string name="pref_ui_font">Font</string> <string name="pref_ui_font">Font</string>
<string name="pref_ui_color_scheme">Color Scheme</string> <string name="pref_ui_color_scheme">Color Scheme</string>
<string name="pref_ui_close_tab_anim_next_tab">Next tab animation</string> <string name="pref_ui_close_tab_anim_next_tab">Next tab animation</string>
@ -35,6 +37,11 @@
<string name="pref_package_source">Source</string> <string name="pref_package_source">Source</string>
<string name="toggle_ime">Toggle IME</string> <string name="toggle_ime">Toggle IME</string>
<string name="shell_not_found">Shell %s not found, please install it first.</string> <string name="shell_not_found">Shell %s not found, please install it first.</string>
<string name="fullscreen_mode_changed">FullScreen mode changed, please restart NeoTerm.</string>
<string name="permission_denied">NeoTerm cannot get essential permissions, exiting.</string>
<string name="error">Error</string>
<string name="use_system_shell">System Shell</string>
<string name="retry">Retry</string>
<string-array name="pref_ui_color_scheme_entries" translatable="false"> <string-array name="pref_ui_color_scheme_entries" translatable="false">
<item>Default</item> <item>Default</item>

View File

@ -6,6 +6,12 @@
android:key="@string/key_ui_fullscreen" android:key="@string/key_ui_fullscreen"
android:title="@string/pref_ui_fullscreen" /> android:title="@string/pref_ui_fullscreen" />
<CheckBoxPreference
android:defaultValue="false"
android:key="@string/key_ui_hide_toolbar"
android:summary="@string/pref_ui_hide_toolbar_desc"
android:title="@string/pref_ui_hide_toolbar" />
<CheckBoxPreference <CheckBoxPreference
android:defaultValue="false" android:defaultValue="false"
android:key="@string/key_ui_next_tab_anim" android:key="@string/key_ui_next_tab_anim"

View File

@ -52,6 +52,7 @@ import java.util.LinkedHashSet;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Queue; import java.util.Queue;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import de.mrapp.android.tabswitcher.layout.AbstractTabSwitcherLayout; import de.mrapp.android.tabswitcher.layout.AbstractTabSwitcherLayout;
import de.mrapp.android.tabswitcher.layout.AbstractTabSwitcherLayout.LayoutListenerWrapper; import de.mrapp.android.tabswitcher.layout.AbstractTabSwitcherLayout.LayoutListenerWrapper;
@ -207,7 +208,8 @@ public class TabSwitcher extends FrameLayout implements TabSwitcherLayout, Model
@AttrRes final int defaultStyle, @AttrRes final int defaultStyle,
@StyleRes final int defaultStyleResource) { @StyleRes final int defaultStyleResource) {
pendingActions = new LinkedList<>(); pendingActions = new LinkedList<>();
listeners = new LinkedHashSet<>(); // listeners = new LinkedHashSet<>();
listeners = new CopyOnWriteArraySet<>(new LinkedHashSet<TabSwitcherListener>());
model = new TabSwitcherModel(this); model = new TabSwitcherModel(this);
model.addListener(createModelListener()); model.addListener(createModelListener());
getViewTreeObserver().addOnGlobalLayoutListener( getViewTreeObserver().addOnGlobalLayoutListener(