Feature: NeoTermService holds TermSessions
This commit is contained in:
parent
cad1304d3d
commit
a6bf16bc83
@ -11,7 +11,7 @@
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:name=".NeoTermActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:theme="@style/AppTheme.NoActionBar"
|
||||
android:windowSoftInputMode="adjustResize|stateAlwaysVisible">
|
||||
@ -21,6 +21,10 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".NeoTermService"
|
||||
android:enabled="true" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -1,138 +0,0 @@
|
||||
package io.neoterm
|
||||
|
||||
import android.os.Bundle
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.support.v4.view.OnApplyWindowInsetsListener
|
||||
import android.support.v4.view.ViewCompat
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.support.v7.widget.Toolbar
|
||||
import android.view.View
|
||||
import android.widget.ImageButton
|
||||
import de.mrapp.android.tabswitcher.*
|
||||
import io.neoterm.tab.TermTab
|
||||
import io.neoterm.tab.TermTabDecorator
|
||||
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
lateinit var tabSwitcher: TabSwitcher
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.tab_main)
|
||||
|
||||
tabSwitcher = findViewById(R.id.tab_switcher) as TabSwitcher
|
||||
tabSwitcher.decorator = TermTabDecorator(this)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(tabSwitcher, createWindowInsetsListener())
|
||||
tabSwitcher.showToolbars(true)
|
||||
tabSwitcher
|
||||
.setToolbarNavigationIcon(R.drawable.ic_add_box_white_24dp, createAddTabListener())
|
||||
tabSwitcher.inflateToolbarMenu(R.menu.tab_switcher, createToolbarMenuListener())
|
||||
tabSwitcher.addListener(object : TabSwitcherListener {
|
||||
override fun onSwitcherShown(tabSwitcher: TabSwitcher) {
|
||||
}
|
||||
|
||||
override fun onSwitcherHidden(tabSwitcher: TabSwitcher) {
|
||||
}
|
||||
|
||||
override fun onSelectionChanged(tabSwitcher: TabSwitcher, selectedTabIndex: Int, selectedTab: Tab?) {
|
||||
}
|
||||
|
||||
override fun onTabAdded(tabSwitcher: TabSwitcher, index: Int, tab: Tab, animation: Animation) {
|
||||
}
|
||||
|
||||
override fun onTabRemoved(tabSwitcher: TabSwitcher, index: Int, tab: Tab, animation: Animation) {
|
||||
if (tab is TermTab) {
|
||||
tab.termSession?.finishIfRunning()
|
||||
tab.viewClient?.termView = null
|
||||
tab.viewClient?.extraKeysView = null
|
||||
tab.sessionCallback?.termView = null
|
||||
tab.termSession = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAllTabsRemoved(tabSwitcher: TabSwitcher, tabs: Array<out Tab>, animation: Animation) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun createAddTabListener(): View.OnClickListener {
|
||||
return View.OnClickListener {
|
||||
val index = tabSwitcher.count
|
||||
val animation = createRevealAnimation()
|
||||
tabSwitcher.addTab(createTab(index), 0, animation)
|
||||
}
|
||||
}
|
||||
|
||||
fun createToolbarMenuListener(): Toolbar.OnMenuItemClickListener {
|
||||
return Toolbar.OnMenuItemClickListener { item ->
|
||||
when (item.itemId) {
|
||||
R.id.add_tab_menu_item -> {
|
||||
val index = tabSwitcher.count
|
||||
val tab = createTab(index)
|
||||
|
||||
if (tabSwitcher.isSwitcherShown) {
|
||||
tabSwitcher.addTab(tab, 0, createRevealAnimation())
|
||||
} else {
|
||||
tabSwitcher.addTab(tab, 0, createPeekAnimation())
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createTab(index: Int): Tab {
|
||||
val tab = TermTab("Neo Term #" + index)
|
||||
tab.isCloseable = true
|
||||
tab.parameters = Bundle()
|
||||
tab.setBackgroundColor(ContextCompat.getColor(this, R.color.tab_background_color))
|
||||
tab.setTitleTextColor(ContextCompat.getColor(this, R.color.tab_title_text_color))
|
||||
return tab
|
||||
}
|
||||
|
||||
private fun createRevealAnimation(): Animation {
|
||||
var x = 0f
|
||||
var y = 0f
|
||||
val view = getNavigationMenuItem()
|
||||
|
||||
if (view != null) {
|
||||
val location = IntArray(2)
|
||||
view.getLocationInWindow(location)
|
||||
x = location[0] + view.width / 2f
|
||||
y = location[1] + view.height / 2f
|
||||
}
|
||||
|
||||
return RevealAnimation.Builder().setX(x).setY(y).create()
|
||||
}
|
||||
|
||||
private fun createPeekAnimation(): Animation {
|
||||
return PeekAnimation.Builder().setX(tabSwitcher.width / 2f).create()
|
||||
}
|
||||
|
||||
private fun getNavigationMenuItem(): View? {
|
||||
val toolbars = tabSwitcher.toolbars
|
||||
|
||||
if (toolbars != null) {
|
||||
val toolbar = if (toolbars.size > 1) toolbars[1] else toolbars[0]
|
||||
val size = toolbar.childCount
|
||||
|
||||
(0..size - 1)
|
||||
.map { toolbar.getChildAt(it) }
|
||||
.filterIsInstance<ImageButton>()
|
||||
.forEach { return it }
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun createWindowInsetsListener(): OnApplyWindowInsetsListener {
|
||||
return OnApplyWindowInsetsListener { v, insets ->
|
||||
tabSwitcher.setPadding(insets.systemWindowInsetLeft,
|
||||
insets.systemWindowInsetTop, insets.systemWindowInsetRight,
|
||||
insets.systemWindowInsetBottom)
|
||||
insets
|
||||
}
|
||||
}
|
||||
}
|
260
app/src/main/java/io/neoterm/NeoTermActivity.kt
Normal file
260
app/src/main/java/io/neoterm/NeoTermActivity.kt
Normal file
@ -0,0 +1,260 @@
|
||||
package io.neoterm
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.ServiceConnection
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.support.v4.view.OnApplyWindowInsetsListener
|
||||
import android.support.v4.view.ViewCompat
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.support.v7.widget.Toolbar
|
||||
import android.view.View
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.ImageButton
|
||||
import de.mrapp.android.tabswitcher.*
|
||||
import de.mrapp.android.tabswitcher.view.TabSwitcherButton
|
||||
import io.neoterm.preference.NeoTermPreference
|
||||
import io.neoterm.tab.TermSessionChangedCallback
|
||||
import io.neoterm.tab.TermTab
|
||||
import io.neoterm.tab.TermTabDecorator
|
||||
import io.neoterm.tab.TermViewClient
|
||||
import io.neoterm.terminal.TerminalSession
|
||||
|
||||
|
||||
class NeoTermActivity : AppCompatActivity(), ServiceConnection {
|
||||
lateinit var tabSwitcher: TabSwitcher
|
||||
var termService: NeoTermService? = null
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if (!termService!!.sessions.isEmpty()) {
|
||||
for (session in termService!!.sessions) {
|
||||
addNewSession(session)
|
||||
}
|
||||
switchToSession(getStoredCurrentSessionOrLast())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.tab_main)
|
||||
|
||||
tabSwitcher = findViewById(R.id.tab_switcher) as TabSwitcher
|
||||
tabSwitcher.decorator = TermTabDecorator(this)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(tabSwitcher, createWindowInsetsListener())
|
||||
tabSwitcher.showToolbars(true)
|
||||
tabSwitcher
|
||||
.setToolbarNavigationIcon(R.drawable.ic_add_box_white_24dp, createAddSessionListener())
|
||||
tabSwitcher.inflateToolbarMenu(R.menu.tab_switcher, createToolbarMenuListener())
|
||||
tabSwitcher.addListener(object : TabSwitcherListener {
|
||||
override fun onSwitcherShown(tabSwitcher: TabSwitcher) {
|
||||
}
|
||||
|
||||
override fun onSwitcherHidden(tabSwitcher: TabSwitcher) {
|
||||
}
|
||||
|
||||
override fun onSelectionChanged(tabSwitcher: TabSwitcher, selectedTabIndex: Int, selectedTab: Tab?) {
|
||||
if (selectedTab is TermTab && selectedTab.termSession != null) {
|
||||
NeoTermPreference.storeCurrentSession(this@NeoTermActivity, selectedTab.termSession!!)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTabAdded(tabSwitcher: TabSwitcher, index: Int, tab: Tab, animation: Animation) {
|
||||
updateTabSwitcherButton()
|
||||
}
|
||||
|
||||
override fun onTabRemoved(tabSwitcher: TabSwitcher, index: Int, tab: Tab, animation: Animation) {
|
||||
if (tab is TermTab) {
|
||||
tab.termSession?.finishIfRunning()
|
||||
tab.viewClient?.termView = null
|
||||
tab.viewClient?.extraKeysView = null
|
||||
tab.sessionCallback?.termView = null
|
||||
|
||||
removeFinishedSession(tab.termSession)
|
||||
tab.termSession = null
|
||||
}
|
||||
updateTabSwitcherButton()
|
||||
}
|
||||
|
||||
override fun onAllTabsRemoved(tabSwitcher: TabSwitcher, tabs: Array<out Tab>, animation: Animation) {
|
||||
}
|
||||
})
|
||||
|
||||
val serviceIntent = Intent(this, NeoTermService::class.java)
|
||||
startService(serviceIntent)
|
||||
bindService(serviceIntent, this, 0)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
if (termService != null) {
|
||||
if (termService!!.sessions.isEmpty()) {
|
||||
termService!!.stopSelf()
|
||||
}
|
||||
termService = null
|
||||
}
|
||||
unbindService(this)
|
||||
}
|
||||
|
||||
private fun addNewSession(session: TerminalSession?) {
|
||||
if (session == null) {
|
||||
return
|
||||
}
|
||||
|
||||
val tab = createTab(session.mSessionName) as TermTab
|
||||
tab.sessionCallback = session.sessionChangedCallback as TermSessionChangedCallback
|
||||
tab.viewClient = TermViewClient(this)
|
||||
tab.termSession = session
|
||||
|
||||
addNewTab(tab, createRevealAnimation())
|
||||
switchToSession(tab)
|
||||
}
|
||||
|
||||
private fun addNewSession(sessionName: String?, animation: Animation) {
|
||||
val tab = createTab(sessionName) as TermTab
|
||||
tab.sessionCallback = TermSessionChangedCallback()
|
||||
tab.viewClient = TermViewClient(this)
|
||||
tab.termSession = termService!!.createTermSession(null, null, "/", null, tab.sessionCallback)
|
||||
|
||||
if (sessionName != null) {
|
||||
tab.termSession!!.mSessionName = sessionName
|
||||
}
|
||||
|
||||
addNewTab(tab, animation)
|
||||
switchToSession(tab)
|
||||
}
|
||||
|
||||
private fun switchToSession(session: TerminalSession?) {
|
||||
if (session == null) {
|
||||
return
|
||||
}
|
||||
|
||||
for (i in 0..tabSwitcher.count - 1) {
|
||||
val tab = tabSwitcher.getTab(i)
|
||||
if (tab is TermTab && tab.termSession == session) {
|
||||
switchToSession(tab)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun switchToSession(tab: Tab?) {
|
||||
if (tab == null) {
|
||||
return
|
||||
}
|
||||
tabSwitcher.selectTab(tab)
|
||||
}
|
||||
|
||||
private fun addNewTab(tab: Tab, animation: Animation) {
|
||||
tabSwitcher.addTab(tab, 0, animation)
|
||||
}
|
||||
|
||||
private fun removeFinishedSession(finishedSession: TerminalSession?) {
|
||||
if (termService == null || finishedSession == null) {
|
||||
return
|
||||
}
|
||||
|
||||
termService!!.removeTermSession(finishedSession)
|
||||
}
|
||||
|
||||
private fun updateTabSwitcherButton() {
|
||||
val menu = tabSwitcher.toolbarMenu
|
||||
if (menu != null) {
|
||||
val switcherButton = menu.findItem(R.id.toggle_tab_switcher_menu_item).actionView as TabSwitcherButton
|
||||
switcherButton.setCount(tabSwitcher.count)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getStoredCurrentSessionOrLast(): TerminalSession? {
|
||||
val stored = NeoTermPreference.getCurrentSession(this)
|
||||
if (stored != null) return stored
|
||||
val numberOfSessions = termService!!.sessions.size
|
||||
if (numberOfSessions == 0) return null
|
||||
return termService!!.sessions[numberOfSessions - 1]
|
||||
}
|
||||
|
||||
fun createAddSessionListener(): View.OnClickListener {
|
||||
return View.OnClickListener {
|
||||
val index = tabSwitcher.count
|
||||
addNewSession("NeoTerm #" + index, createRevealAnimation())
|
||||
}
|
||||
}
|
||||
|
||||
fun createToolbarMenuListener(): Toolbar.OnMenuItemClickListener {
|
||||
return Toolbar.OnMenuItemClickListener { item ->
|
||||
when (item.itemId) {
|
||||
R.id.menu_item_settings -> {
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createTab(tabTitle: String?): Tab {
|
||||
val tab = TermTab(tabTitle ?: "NeoTerm")
|
||||
tab.isCloseable = true
|
||||
tab.parameters = Bundle()
|
||||
tab.setBackgroundColor(ContextCompat.getColor(this, R.color.tab_background_color))
|
||||
tab.setTitleTextColor(ContextCompat.getColor(this, R.color.tab_title_text_color))
|
||||
return tab
|
||||
}
|
||||
|
||||
private fun createRevealAnimation(): Animation {
|
||||
var x = 0f
|
||||
var y = 0f
|
||||
val view = getNavigationMenuItem()
|
||||
|
||||
if (view != null) {
|
||||
val location = IntArray(2)
|
||||
view.getLocationInWindow(location)
|
||||
x = location[0] + view.width / 2f
|
||||
y = location[1] + view.height / 2f
|
||||
}
|
||||
|
||||
return RevealAnimation.Builder().setX(x).setY(y).create()
|
||||
}
|
||||
|
||||
private fun createPeekAnimation(): Animation {
|
||||
return PeekAnimation.Builder().setX(tabSwitcher.width / 2f).create()
|
||||
}
|
||||
|
||||
private fun getNavigationMenuItem(): View? {
|
||||
val toolbars = tabSwitcher.toolbars
|
||||
|
||||
if (toolbars != null) {
|
||||
val toolbar = if (toolbars.size > 1) toolbars[1] else toolbars[0]
|
||||
val size = toolbar.childCount
|
||||
|
||||
(0..size - 1)
|
||||
.map { toolbar.getChildAt(it) }
|
||||
.filterIsInstance<ImageButton>()
|
||||
.forEach { return it }
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun createWindowInsetsListener(): OnApplyWindowInsetsListener {
|
||||
return OnApplyWindowInsetsListener { _, insets ->
|
||||
tabSwitcher.setPadding(insets.systemWindowInsetLeft,
|
||||
insets.systemWindowInsetTop, insets.systemWindowInsetRight,
|
||||
insets.systemWindowInsetBottom)
|
||||
insets
|
||||
}
|
||||
}
|
||||
}
|
136
app/src/main/java/io/neoterm/NeoTermService.java
Normal file
136
app/src/main/java/io/neoterm/NeoTermService.java
Normal file
@ -0,0 +1,136 @@
|
||||
package io.neoterm;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.WakefulBroadcastReceiver;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.neoterm.tab.TermSessionChangedCallback;
|
||||
import io.neoterm.terminal.EmulatorDebug;
|
||||
import io.neoterm.terminal.TerminalSession;
|
||||
|
||||
/**
|
||||
* @author kiva
|
||||
*/
|
||||
|
||||
public class NeoTermService extends Service {
|
||||
public class NeoTermBinder extends Binder {
|
||||
public NeoTermService service = NeoTermService.this;
|
||||
}
|
||||
|
||||
public static final String ACTION_SERVICE_STOP = "neoterm.action.service.stop";
|
||||
|
||||
private static final int NOTIFICATION_ID = 52019;
|
||||
|
||||
private final NeoTermBinder neoTermBinder = new NeoTermBinder();
|
||||
private final List<TerminalSession> mTerminalSessions = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
startForeground(NOTIFICATION_ID, createNotification());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return neoTermBinder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
String action = intent.getAction();
|
||||
if (ACTION_SERVICE_STOP.equals(action)) {
|
||||
for (int i = 0; i < mTerminalSessions.size(); i++)
|
||||
mTerminalSessions.get(i).finishIfRunning();
|
||||
stopSelf();
|
||||
} else if (action != null) {
|
||||
Log.e(EmulatorDebug.LOG_TAG, "Unknown NeoTermService action: '" + action + "'");
|
||||
}
|
||||
|
||||
if ((flags & START_FLAG_REDELIVERY) == 0) {
|
||||
// Service is started by WBR, not restarted by system, so release the WakeLock from WBR.
|
||||
WakefulBroadcastReceiver.completeWakefulIntent(intent);
|
||||
}
|
||||
|
||||
return Service.START_NOT_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
stopForeground(true);
|
||||
|
||||
for (int i = 0; i < mTerminalSessions.size(); i++)
|
||||
mTerminalSessions.get(i).finishIfRunning();
|
||||
mTerminalSessions.clear();
|
||||
}
|
||||
|
||||
public List<TerminalSession> getSessions() {
|
||||
return mTerminalSessions;
|
||||
}
|
||||
|
||||
TerminalSession createTermSession(String executablePath, String[] arguments, String cwd, String[] env, TermSessionChangedCallback sessionCallback) {
|
||||
if (cwd == null) cwd = getFilesDir().getAbsolutePath();
|
||||
|
||||
boolean isLoginShell = false;
|
||||
|
||||
if (executablePath == null) {
|
||||
// Fall back to system shell as last resort:
|
||||
executablePath = "/system/bin/sh";
|
||||
isLoginShell = true;
|
||||
}
|
||||
|
||||
if (arguments == null) {
|
||||
arguments = new String[]{executablePath};
|
||||
}
|
||||
|
||||
int lastSlashIndex = executablePath.lastIndexOf('/');
|
||||
String processName = (isLoginShell ? "-" : "") +
|
||||
(lastSlashIndex == -1 ? executablePath : executablePath.substring(lastSlashIndex + 1));
|
||||
|
||||
TerminalSession session = new TerminalSession(executablePath, cwd, arguments, env, sessionCallback);
|
||||
mTerminalSessions.add(session);
|
||||
updateNotification();
|
||||
return session;
|
||||
}
|
||||
|
||||
public int removeTermSession(TerminalSession sessionToRemove) {
|
||||
int indexOfRemoved = mTerminalSessions.indexOf(sessionToRemove);
|
||||
mTerminalSessions.remove(indexOfRemoved);
|
||||
updateNotification();
|
||||
return indexOfRemoved;
|
||||
}
|
||||
|
||||
private void updateNotification() {
|
||||
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).notify(NOTIFICATION_ID, createNotification());
|
||||
}
|
||||
|
||||
private Notification createNotification() {
|
||||
Intent notifyIntent = new Intent(this, NeoTermActivity.class);
|
||||
notifyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notifyIntent, 0);
|
||||
|
||||
int sessionCount = mTerminalSessions.size();
|
||||
String contentText = sessionCount + " session" + (sessionCount == 1 ? "" : "s");
|
||||
|
||||
Notification.Builder builder = new Notification.Builder(this);
|
||||
builder.setContentTitle(getText(R.string.app_name));
|
||||
builder.setContentText(contentText);
|
||||
builder.setSmallIcon(R.drawable.ic_service_notification);
|
||||
builder.setContentIntent(pendingIntent);
|
||||
builder.setOngoing(true);
|
||||
builder.setShowWhen(false);
|
||||
builder.setColor(0xFF000000);
|
||||
return builder.build();
|
||||
}
|
||||
}
|
31
app/src/main/java/io/neoterm/preference/NeoTermPreference.kt
Normal file
31
app/src/main/java/io/neoterm/preference/NeoTermPreference.kt
Normal file
@ -0,0 +1,31 @@
|
||||
package io.neoterm.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.preference.PreferenceManager
|
||||
|
||||
import io.neoterm.NeoTermActivity
|
||||
import io.neoterm.terminal.TerminalSession
|
||||
|
||||
/**
|
||||
* @author kiva
|
||||
*/
|
||||
|
||||
object NeoTermPreference {
|
||||
var CURRENT_SESSION_KEY = "neoterm_current_session"
|
||||
|
||||
fun storeCurrentSession(context: Context, session: TerminalSession) {
|
||||
PreferenceManager.getDefaultSharedPreferences(context).edit().putString(NeoTermPreference.CURRENT_SESSION_KEY, session.mHandle).apply()
|
||||
}
|
||||
|
||||
fun getCurrentSession(termActivity: NeoTermActivity): TerminalSession? {
|
||||
val sessionHandle = PreferenceManager.getDefaultSharedPreferences(termActivity).getString(CURRENT_SESSION_KEY, "")
|
||||
var i = 0
|
||||
val len = termActivity.termService!!.sessions.size
|
||||
while (i < len) {
|
||||
val session = termActivity.termService!!.sessions[i]
|
||||
if (session.mHandle == sessionHandle) return session
|
||||
i++
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@ import android.view.ViewGroup
|
||||
import de.mrapp.android.tabswitcher.Tab
|
||||
import de.mrapp.android.tabswitcher.TabSwitcher
|
||||
import de.mrapp.android.tabswitcher.TabSwitcherDecorator
|
||||
import io.neoterm.MainActivity
|
||||
import io.neoterm.NeoTermActivity
|
||||
import io.neoterm.R
|
||||
import io.neoterm.terminal.TerminalSession
|
||||
import io.neoterm.view.ExtraKeysView
|
||||
@ -20,7 +20,7 @@ import io.neoterm.view.TerminalView
|
||||
/**
|
||||
* @author kiva
|
||||
*/
|
||||
class TermTabDecorator(val context: MainActivity) : TabSwitcherDecorator() {
|
||||
class TermTabDecorator(val context: NeoTermActivity) : TabSwitcherDecorator() {
|
||||
override fun onInflateView(inflater: LayoutInflater, parent: ViewGroup?, viewType: Int): View {
|
||||
val view = inflater.inflate(R.layout.term, parent, false)
|
||||
val toolbar = view.findViewById(R.id.terminal_toolbar) as Toolbar
|
||||
@ -57,19 +57,9 @@ class TermTabDecorator(val context: MainActivity) : TabSwitcherDecorator() {
|
||||
val termTab = tab as TermTab
|
||||
|
||||
// 复用前一次的 TermSession
|
||||
if (termTab.sessionCallback == null) {
|
||||
termTab.sessionCallback = TermSessionChangedCallback()
|
||||
}
|
||||
|
||||
termTab.sessionCallback?.termView = view
|
||||
termTab.termSession = termTab.termSession ?: TerminalSession("/system/bin/sh", "/",
|
||||
arrayOf("/system/bin/sh"),
|
||||
arrayOf("TERM=screen", "HOME=" + context.filesDir), termTab.sessionCallback)
|
||||
|
||||
// 复用上一次的 TermViewClient
|
||||
if (termTab.viewClient == null) {
|
||||
termTab.viewClient = TermViewClient(context)
|
||||
}
|
||||
termTab.viewClient?.termView = view
|
||||
termTab.viewClient?.extraKeysView = extraKeysView
|
||||
|
||||
|
@ -5,6 +5,6 @@ import android.util.Log;
|
||||
public final class EmulatorDebug {
|
||||
|
||||
/** The tag to use with {@link Log}. */
|
||||
public static final String LOG_TAG = "neoterm-termux";
|
||||
public static final String LOG_TAG = "neoterm";
|
||||
|
||||
}
|
||||
|
@ -85,6 +85,10 @@ public final class TerminalSession extends TerminalOutput {
|
||||
/** Buffer to write translate code points into utf8 before writing to mTerminalToProcessIOQueue */
|
||||
private final byte[] mUtf8InputBuffer = new byte[5];
|
||||
|
||||
public SessionChangedCallback getSessionChangedCallback() {
|
||||
return mChangeCallback;
|
||||
}
|
||||
|
||||
/** Callback which gets notified when a session finishes or changes title. */
|
||||
final SessionChangedCallback mChangeCallback;
|
||||
|
||||
|
33
app/src/main/res/drawable/ic_service_notification.xml
Executable file
33
app/src/main/res/drawable/ic_service_notification.xml
Executable file
@ -0,0 +1,33 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="48dp"
|
||||
android:width="48dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<!--
|
||||
https://material.google.com/style/icons.html
|
||||
-->
|
||||
|
||||
<!-- Screen border. -->
|
||||
<path android:fillColor="#00000000"
|
||||
android:strokeColor="#000"
|
||||
android:strokeWidth="3"
|
||||
android:pathData="M7,4
|
||||
l34,0
|
||||
q3 0,3 3
|
||||
l0,34
|
||||
q0 3, -3 3
|
||||
l-34,0
|
||||
q-3 0, -3-3
|
||||
l0 -34
|
||||
q0 -3, 3 -3"
|
||||
/>
|
||||
|
||||
<!-- Block cursor. -->
|
||||
<path android:fillColor="#000"
|
||||
android:pathData="M14,14
|
||||
l5,0
|
||||
l0,10
|
||||
l-5,0"
|
||||
/>
|
||||
|
||||
</vector>
|
@ -23,8 +23,8 @@ License.
|
||||
app:showAsAction="ifRoom"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/add_tab_menu_item"
|
||||
android:title="@string/add_tab_menu_item"
|
||||
android:id="@+id/menu_item_settings"
|
||||
android:title="@string/menu_settings"
|
||||
app:showAsAction="never"/>
|
||||
|
||||
</menu>
|
@ -5,6 +5,5 @@
|
||||
<string name="text_selection_more">More</string>
|
||||
|
||||
<string name="toggle_tab_switcher_menu_item">Toggle switcher</string>
|
||||
<string name="add_tab_menu_item">New tab</string>
|
||||
<string name="clear_tabs_menu_item">Clear all tabs</string>
|
||||
<string name="menu_settings">Settings</string>
|
||||
</resources>
|
||||
|
Loading…
x
Reference in New Issue
Block a user