@@ -66,7 +66,7 @@ public final class MainActivity extends Activity {
private static final int MAX_TERMINAL_COLS = 140 ;
private static final int MIN_TERMINAL_ROWS = 8 ;
private static final int MAX_TERMINAL_ROWS = 80 ;
private static final int TERMINAL_KEYS_HEIGHT_DP = 44 ;
private static final int TERMINAL_KEYS_HEIGHT_DP = 34 ;
private static final int STATUS_NORMAL = 0 ;
private static final int STATUS_BUSY = 1 ;
private static final int STATUS_SUCCESS = 2 ;
@@ -126,7 +126,8 @@ public final class MainActivity extends Activity {
private TextView terminalMetaText ;
private ScrollView terminalScroll ;
private EditText inputField ;
private Button terminalKeyPageButton ;
private final Button [ ] terminalAccessoryTabButtons = new Button [ 4 ] ;
private View terminalAccessoryBar ;
private View terminalComposerBar ;
private LinearLayout terminalGroupRow ;
private String activeSessionName ;
@@ -135,6 +136,8 @@ public final class MainActivity extends Activity {
private String terminalEventStatus = " " ;
private String activeMainPage = PAGE_SERVERS ;
private String pendingImageUploadSession ;
private int lastSessionCount = - 1 ;
private int lastActiveSessionCount = - 1 ;
private TerminalScreenBuffer terminalScreen = new TerminalScreenBuffer ( DEFAULT_TERMINAL_COLS , DEFAULT_TERMINAL_ROWS ) ;
private final StringBuilder queuedTerminalInput = new StringBuilder ( ) ;
private boolean terminalConnected ;
@@ -261,17 +264,13 @@ public final class MainActivity extends Activity {
content . addView ( serverProfileCard ( url , ! isPresetServer ( url ) ) , matchWrap ( ) ) ;
}
content . addView ( createServerFooterActions ( ) ) ;
content . addView ( createServerUtilityRow ( ) ) ;
scroll . addView ( content ) ;
root . addView ( scroll , new LinearLayout . LayoutParams (
ViewGroup . LayoutParams . MATCH_PARENT ,
0 ,
1
) ) ;
root . addView ( statusText , new LinearLayout . LayoutParams (
ViewGroup . LayoutParams . MATCH_PARENT ,
dp ( 28 )
) ) ;
setStatus ( " Servers " ) ;
}
@@ -281,7 +280,7 @@ public final class MainActivity extends Activity {
LinearLayout card = new LinearLayout ( this ) ;
card . setOrientation ( LinearLayout . HORIZONTAL ) ;
card . setGravity ( Gravity . CENTER_VERTICAL ) ;
card . setPadding ( dp ( 12 ) , dp ( 11 ) , dp ( 8 ) , dp ( 11 ) ) ;
card . setPadding ( dp ( 13 ) , 0 , 0 , 0 ) ;
card . setBackground ( cardBackground ( ) ) ;
card . setHapticFeedbackEnabled ( true ) ;
card . setOnClickListener ( view - > {
@@ -291,50 +290,56 @@ public final class MainActivity extends Activity {
} ) ;
TextView icon = new TextView ( this ) ;
icon . setText ( " ▣ " ) ;
icon . setTextColor ( selected ? COLOR_ACCENT : COLOR_TEXT_MUTED ) ;
icon . setTextSize ( 18 ) ;
icon . setText ( " ▥ " ) ;
icon . setTextColor ( COLOR_TEXT_MUTED ) ;
icon . setTextSize ( 20 ) ;
icon . setGravity ( Gravity . CENTER ) ;
icon . setBackground ( rounded ( selected ? COLOR_ACCENT_DARK : COLOR_CARD_ALT , 8 , Color . TRANSPARENT , 0 ) ) ;
icon . setBackground ( rounded ( COLOR_CARD_ALT , 8 , Color . TRANSPARENT , 0 ) ) ;
card . addView ( icon , new LinearLayout . LayoutParams ( dp ( 40 ) , dp ( 40 ) ) ) ;
LinearLayout textBlock = new LinearLayout ( this ) ;
textBlock . setOrientation ( LinearLayout . VERTICAL ) ;
textBlock . setPadding ( dp ( 11 ) , 0 , dp ( 6 ) , 0 ) ;
LinearLayout nameRow = new LinearLayout ( this ) ;
nameRow . setOrientation ( LinearLayout . HORIZONTAL ) ;
nameRow . setGravity ( Gravity . CENTER_VERTICAL ) ;
TextView dot = new TextView ( this ) ;
dot . setText ( " ● " ) ;
dot . setTextColor ( selected ? COLOR_ACCENT : COLOR_TEXT_DIM ) ;
dot . setTextSize ( 10 ) ;
nameRow . addView ( dot ) ;
TextView title = new TextView ( this ) ;
title . setText ( ( selected ? " ● " : " ○ " ) + label ) ;
title . setText ( " " + serverDisplayName ( url ) ) ;
title . setTextColor ( COLOR_TEXT ) ;
title . setTextSize ( 15 ) ;
title . setTextSize ( 14 ) ;
title . setTypeface ( Typeface . DEFAULT_BOLD ) ;
title . setSingleLine ( true ) ;
nameRow . addView ( title , new LinearLayout . LayoutParams ( 0 , ViewGroup . LayoutParams . WRAP_CONTENT , 1 ) ) ;
TextView meta = bodyText ( url ) ;
meta . setTextSize ( 11 ) ;
meta . setTypeface ( Typeface . MONOSPACE ) ;
meta . setSingleLine ( true ) ;
textBlock . addView ( title ) ;
meta . setEllipsize ( TextUtils . TruncateAt . MIDDLE ) ;
textBlock . addView ( nameRow ) ;
textBlock . addView ( meta ) ;
card . addView ( textBlock , new LinearLayout . LayoutParams ( 0 , ViewGroup . LayoutParams . WRAP_CONTENT , 1 ) ) ;
LinearLayout stateBlock = new LinearLayout ( this ) ;
stateBlock . setOrientation ( LinearLayout . VERTICAL ) ;
stateBlock . setGravity ( Gravity . END ) ;
TextView port = new TextView ( this ) ;
port . setText ( " 3000 " ) ;
port . setTextColor ( selected ? COLOR_ACCENT : COLOR_TEXT_MUTED ) ;
port . setTextSize ( 12 ) ;
port . setTypeface ( Typeface . MONOSPACE ) ;
TextView state = new TextView ( this ) ;
state . setText ( selected ? " ACTIVE " : ( removable ? " SAVED " : " PRESET " ) ) ;
state . setTextColor ( COLOR_TEXT_DIM ) ;
state . setTextSize ( 9 ) ;
state . setTypeface ( Typeface . MONOSPACE ) ;
stateBlock . addView ( port ) ;
stateBlock . addView ( state ) ;
TextView active = new TextView ( this ) ;
active . setText ( selected & & lastActiveSessionCount > = 0 ? String . valueOf ( lastActiveSessionCount ) : " 0 " ) ;
active . setTextColor ( COLOR_ACCENT ) ;
active . setTextSize ( 13 ) ;
active . setTypeface ( Typeface . MONOSPACE ) ;
TextView total = new TextView ( this ) ;
total . setText ( " of " + ( selected & & lastSessionCount > = 0 ? lastSessionCount : 0 ) ) ;
total . setTextColor ( COLOR_TEXT_DIM ) ;
total . setTextSize ( 9 ) ;
total . setTypeface ( Typeface . MONOSPACE ) ;
stateBlock . addView ( active ) ;
stateBlock . addView ( total ) ;
card . addView ( stateBlock ) ;
Button probe = terminalToolButton ( " ↻ " , view - > probeSingleServer ( url ) ) ;
probe . setContentDescription ( " Probe " + label ) ;
card . addView ( probe ) ;
if ( removable ) {
Button remove = terminalToolButton ( " × " , view - > confirmRemoveSavedServer ( url ) ) ;
remove . setContentDescription ( " Remove " + label ) ;
@@ -351,7 +356,7 @@ public final class MainActivity extends Activity {
LinearLayout . LayoutParams params = new LinearLayout . LayoutParams (
ViewGroup . LayoutParams . MATCH_PARENT ,
ViewGroup . LayoutParams . WRAP_CONTENT
dp ( 68 )
) ;
params . bottomMargin = dp ( 10 ) ;
card . setLayoutParams ( params ) ;
@@ -383,10 +388,6 @@ public final class MainActivity extends Activity {
0 ,
1
) ) ;
root . addView ( statusText , new LinearLayout . LayoutParams (
ViewGroup . LayoutParams . MATCH_PARENT ,
dp ( 28 )
) ) ;
setStatus ( " Sessions " ) ;
}
@@ -440,35 +441,14 @@ public final class MainActivity extends Activity {
return header ;
}
private View createServerFooterActions ( ) {
LinearLayout card = new LinearLayout ( this ) ;
card . setOrientation ( LinearLayout . VERTICAL ) ;
card . setPadding ( dp ( 12 ) , dp ( 10 ) , dp ( 12 ) , dp ( 10 ) ) ;
card . setBackground ( cardBackground ( ) ) ;
TextView label = new TextView ( this ) ;
label . setText ( " ACTIVE API " ) ;
label . setTextColor ( COLOR_TEXT_DIM ) ;
label . setTextSize ( 9 ) ;
label . setTypeface ( Typeface . MONOSPACE ) ;
TextView value = bodyText ( getServerUrl ( ) ) ;
value . setTextColor ( COLOR_TEXT ) ;
value . setTextSize ( 12 ) ;
value . setTypeface ( Typeface . MONOSPACE ) ;
value . setSingleLine ( true ) ;
value . setEllipsize ( TextUtils . TruncateAt . MIDDLE ) ;
private View createServerUtilityRow ( ) {
LinearLayout row = new LinearLayout ( this ) ;
row . setOrientation ( LinearLayout . HORIZONTAL ) ;
row . setPadding ( 0 , dp ( 10 ) , 0 , 0 ) ;
row . setPadding ( 0 , dp ( 4 ) , 0 , 0 ) ;
row . addView ( compactButton ( " Probe all " , view - > probeServerProfiles ( ) ) ) ;
row . addView ( compactButton ( " Update " , view - > renderUpdateScreen ( ) ) ) ;
row . addView ( compactButton ( " More " , view - > showMainNavigation ( ) ) ) ;
card . addView ( label ) ;
card . addView ( value ) ;
card . addView ( row ) ;
return card ;
return row ;
}
private View createSessionPageHeader ( ) {
@@ -520,24 +500,41 @@ public final class MainActivity extends Activity {
private View createNewGroupCard ( ) {
LinearLayout card = new LinearLayout ( this ) ;
card . setOrientation ( LinearLayout . HORIZONTAL ) ;
card . setGravity ( Gravity . CENTER_VERTICAL ) ;
card . setPadding ( dp ( 12 ) , dp ( 11 ) , dp ( 10 ) , dp ( 11 ) ) ;
card . setOrientation ( LinearLayout . VERTICAL ) ;
card . setPadding ( dp ( 12 ) , dp ( 11 ) , dp ( 12 ) , dp ( 12 ) ) ;
card . setBackground ( cardBackground ( ) ) ;
LinearLayout textBlock = new LinearLayout ( this ) ;
textBlock . setOrientation ( LinearLayout . VERTICAL ) ;
TextView label = new TextView ( this ) ;
label . setText ( " NEW PROJECT GROUP " ) ;
label . setText ( " NEW GROUP " ) ;
label . setTextColor ( COLOR_TEXT_DIM ) ;
label . setTextSize ( 9 ) ;
label . setTypeface ( Typeface . MONOSPACE ) ;
TextView detail = bodyText ( " Group related tmux sessions " ) ;
detail . setTextSize ( 12 ) ;
textBlock . addView ( label ) ;
textBlock . addView ( detail ) ;
card . addView ( textBlock , new LinearLayout . LayoutParams ( 0 , ViewGroup . LayoutParams . WRAP_CONTENT , 1 ) ) ;
card . addView ( compactButton ( " Create " , view - > promptCreateKanbanProject ( ) ) ) ;
card . addView ( label ) ;
LinearLayout row = new LinearLayout ( this ) ;
row . setOrientation ( LinearLayout . HORIZONTAL ) ;
row . setPadding ( 0 , dp ( 7 ) , 0 , 0 ) ;
EditText input = new EditText ( this ) ;
input . setHint ( " group name " ) ;
input . setSingleLine ( true ) ;
input . setInputType ( InputType . TYPE_CLASS_TEXT ) ;
styleInput ( input ) ;
row . addView ( input , new LinearLayout . LayoutParams ( 0 , dp ( 40 ) , 1 ) ) ;
Button create = compactButton ( " Create " , view - > {
String name = input . getText ( ) . toString ( ) . trim ( ) ;
if ( name . isEmpty ( ) ) {
showMessage ( " Group name is empty " ) ;
return ;
}
runApiAction ( " Create kanban project " , ( ) - > api . createKanbanProject ( name , " ~ " , " " ) ) ;
} ) ;
LinearLayout . LayoutParams createParams = new LinearLayout . LayoutParams (
ViewGroup . LayoutParams . WRAP_CONTENT ,
dp ( 40 )
) ;
createParams . leftMargin = dp ( 8 ) ;
row . addView ( create , createParams ) ;
card . addView ( row , matchWrap ( ) ) ;
LinearLayout . LayoutParams params = new LinearLayout . LayoutParams (
ViewGroup . LayoutParams . MATCH_PARENT ,
@@ -1171,6 +1168,14 @@ public final class MainActivity extends Activity {
}
String finalProjects = projects ;
runOnUiThread ( ( ) - > {
lastSessionCount = sessions . size ( ) ;
lastActiveSessionCount = 0 ;
for ( SessionSummary session : sessions ) {
String status = session . status = = null ? " " : session . status . toLowerCase ( java . util . Locale . ROOT ) ;
if ( status . contains ( " attach " ) | | status . contains ( " active " ) ) {
lastActiveSessionCount + + ;
}
}
if ( sessionGroupList ! = null ) {
renderGroupedSessionList ( sessions , finalProjects ) ;
} else {
@@ -1458,6 +1463,11 @@ public final class MainActivity extends Activity {
row . setBackground ( cardBackground ( ) ) ;
row . setHapticFeedbackEnabled ( true ) ;
row . setOnClickListener ( view - > openTerminal ( session . name ) ) ;
row . setOnLongClickListener ( view - > {
view . performHapticFeedback ( HapticFeedbackConstants . LONG_PRESS ) ;
showSessionActions ( session . name ) ;
return true ;
} ) ;
boolean attached = session . status ! = null
& & ( session . status . toLowerCase ( java . util . Locale . ROOT ) . contains ( " attach " )
@@ -1508,9 +1518,9 @@ public final class MainActivity extends Activity {
textBlock . addView ( meta ) ;
row . addView ( textBlock , new LinearLayout . LayoutParams ( 0 , ViewGroup . LayoutParams . WRAP_CONTENT , 1 ) ) ;
Button more = terminalToolButton ( " ⋯ " , view - > showSessionActions ( session . name ) ) ;
more . setContentDescription ( " Session actions " ) ;
row . addView ( more ) ;
Button move = terminalToolButton ( " ⇄ " , view - > promptAddKanbanSession ( session . name ) ) ;
move . setContentDescription ( " Move " + session . name + " to project " ) ;
row . addView ( move ) ;
Button kill = terminalToolButton ( " × " , view - > confirmKill ( session . name ) ) ;
kill . setContentDescription ( " Kill session " ) ;
kill . setTextColor ( COLOR_DANGER ) ;
@@ -1625,16 +1635,13 @@ public final class MainActivity extends Activity {
0 ,
1
) ) ;
root . addView ( statusText , new LinearLayout . LayoutParams (
ViewGroup . LayoutParams . MATCH_PARENT ,
dp ( 28 )
) ) ;
terminalComposerBar = createComposerBar ( ) ;
root . addView ( terminalComposerBar , matchWrap ( ) ) ;
root . addView ( createAccessoryBar ( ) , new LinearLayout . LayoutParams (
terminalAccessoryBar = createAccessoryBar ( ) ;
root . addView ( terminalAccessoryBar , new LinearLayout . LayoutParams (
ViewGroup . LayoutParams . MATCH_PARENT ,
dp ( TERMINAL_KEYS_HEIGHT_DP )
) ) ;
terminalComposerBar = createComposerBar ( ) ;
root . addView ( terminalComposerBar , matchWrap ( ) ) ;
terminalScroll . post ( ( ) - > {
resizeTerminalToViewport ( false ) ;
connectTerminal ( sessionName ) ;
@@ -1687,8 +1694,7 @@ public final class MainActivity extends Activity {
dp ( 13 )
) ) ;
bar . addView ( titleBlock , new LinearLayout . LayoutParams ( 0 , dp ( 32 ) , 1 ) ) ;
bar . addView ( terminalToolButton ( " ↻ " , view - > connectTerminal ( sessionName ) ) ) ;
bar . addView ( terminalToolButton ( " ⋯ " , view - > showTerminalActions ( sessionName ) ) ) ;
bar . addView ( terminalToolButton ( " ☰ " , view - > showTerminalActions ( sessionName ) ) ) ;
return bar ;
}
@@ -1982,6 +1988,7 @@ public final class MainActivity extends Activity {
private void showTerminalActions ( String sessionName ) {
String [ ] items = {
" Reconnect " ,
" Clear local view and tmux history " ,
" Split horizontal " ,
" Split vertical " ,
@@ -2000,47 +2007,50 @@ public final class MainActivity extends Activity {
. setItems ( items , ( dialog , which ) - > {
switch ( which ) {
case 0 :
connectTerminal ( sessionName ) ;
break ;
case 1 :
terminalScreen . clear ( ) ;
terminalText . setText ( " " ) ;
if ( terminalSocket ! = null ) {
terminalSocket . clearHistory ( ) ;
}
break ;
case 1 :
case 2 :
runApiAction ( " Split horizontal " , ( ) - > api . splitPane ( sessionName , " horizontal " ) ) ;
break ;
case 2 :
case 3 :
runApiAction ( " Split vertical " , ( ) - > api . splitPane ( sessionName , " vertical " ) ) ;
break ;
case 3 :
case 4 :
if ( terminalSocket ! = null ) {
terminalSocket . scroll ( - terminalRows ) ;
}
break ;
case 4 :
case 5 :
if ( terminalSocket ! = null ) {
terminalSocket . scroll ( terminalRows ) ;
}
break ;
case 5 :
case 6 :
showRaw ( " Session status " , ( ) - > api . sessionStatus ( sessionName ) ) ;
break ;
case 6 :
case 7 :
promptSendCommand ( sessionName ) ;
break ;
case 7 :
case 8 :
sendTerminalInput ( " \ u0002 " ) ;
break ;
case 8 :
case 9 :
sendTerminalInput ( " \ u0002d " ) ;
break ;
case 9 :
case 10 :
sendTerminalInput ( " \ u0002c " ) ;
break ;
case 10 :
case 11 :
sendTerminalInput ( " \ u0002n " ) ;
break ;
case 11 :
case 12 :
sendTerminalInput ( " \ u0002p " ) ;
break ;
default :
@@ -2053,28 +2063,45 @@ public final class MainActivity extends Activity {
private LinearLayout createComposerBar ( ) {
LinearLayout bar = new LinearLayout ( this ) ;
bar . setOrientation ( LinearLayout . VERTICAL ) ;
bar . setPadding ( dp ( 9 ) , dp ( 7 ) , dp ( 9 ) , dp ( 8 ) ) ;
bar . setPadding ( dp ( 8 ) , dp ( 5 ) , dp ( 8 ) , dp ( 7 ) ) ;
bar . setBackgroundColor ( COLOR_BAR ) ;
LinearLayout row = new LinearLayout ( this ) ;
row . setOrientation ( LinearLayout . HORIZONTAL ) ;
row . setGravity ( Gravity . BOTTOM ) ;
row . setGravity ( Gravity . CENTER_VERTICAL ) ;
terminalKeyPageButton = terminalPageButton ( ) ;
LinearLayout . LayoutParams pageParams = new LinearLayout . LayoutParams (
dp ( 42 ) ,
dp ( 40 )
LinearLayout tabGrid = new LinearLayout ( this ) ;
tabGrid . setOrientation ( LinearLayout . VERTICAL ) ;
LinearLayout firstTabRow = terminalKeyRow ( ) ;
LinearLayout secondTabRow = terminalKeyRow ( ) ;
addAccessoryTab ( firstTabRow , " Edit " , 0 ) ;
addAccessoryTab ( firstTabRow , " Ctrl " , 1 ) ;
addAccessoryTab ( secondTabRow , " Nav " , 2 ) ;
addAccessoryTab ( secondTabRow , " Sym " , 3 ) ;
tabGrid . addView ( firstTabRow , new LinearLayout . LayoutParams (
ViewGroup . LayoutParams . MATCH_PARENT ,
0 ,
1
) ) ;
LinearLayout . LayoutParams secondTabParams = new LinearLayout . LayoutParams (
ViewGroup . LayoutParams . MATCH_PARENT ,
0 ,
1
) ;
pageParams . rightMargin = dp ( 6 ) ;
row . addView ( terminalKeyPageButton , pageParams ) ;
secondTabParams . topMargin = dp ( 3 ) ;
tabGrid . addView ( secondTabRow , secondTabParams ) ;
row . addView ( tabGrid , new LinearLayout . LayoutParams (
dp ( 72 ) ,
ViewGroup . LayoutParams . MATCH_PARENT
) ) ;
inputField = new EditText ( this ) ;
inputField . setTextColor ( COLOR_TEXT ) ;
inputField . setHintTextColor ( COLOR_TEXT_DIM ) ;
inputField . setHint ( " type a command… " ) ;
inputField . setHint ( " type command or text " ) ;
inputField . setSingleLine ( false ) ;
inputField . setMinLines ( 1 ) ;
inputField . setMaxLines ( 2 ) ;
inputField . setMinLines ( 3 ) ;
inputField . setMaxLines ( 5 ) ;
inputField . setCursorVisible ( true ) ;
inputField . setFocusableInTouchMode ( true ) ;
inputField . setGravity ( Gravity . TOP | Gravity . START ) ;
@@ -2099,21 +2126,37 @@ public final class MainActivity extends Activity {
}
return false ;
} ) ;
Button image = terminalToolButton ( " ▧ " , view - > pickImageForSession ( activeSessionName ) ) ;
image . setContentDescription ( " Upload image " ) ;
row . addView ( image , new LinearLayout . LayoutParams ( dp ( 40 ) , dp ( 40 ) ) ) ;
LinearLayout . LayoutParams inputParams = new LinearLayout . LayoutParams (
0 ,
ViewGroup . LayoutParams . WRAP_CONTENT ,
1
) ;
inputParams . leftMargin = dp ( 6 ) ;
inputParams . rightMargin = dp ( 6 ) ;
inputParams . leftMargin = dp ( 5 ) ;
inputParams . rightMargin = dp ( 5 ) ;
row . addView ( inputField , inputParams ) ;
Button send = primaryButton ( " ↵ " , view - > sendLine ( ) ) ;
send . setTextSize ( 18 ) ;
LinearLayout actions = new LinearLayout ( this ) ;
actions . setOrientation ( LinearLayout . VERTICAL ) ;
Button send = composerPrimaryButton ( " Send " , view - > sendLine ( ) ) ;
send . setContentDescription ( " Send input " ) ;
row . addView ( send , new LinearLayout . LayoutParams ( dp ( 40 ) , dp ( 40 ) ) ) ;
actions . addView ( send , new LinearLayout . LayoutParams (
ViewGroup . LayoutParams . MATCH_PARENT ,
0 ,
1
) ) ;
Button image = composerButton ( " Img " , view - > pickImageForSession ( activeSessionName ) ) ;
image . setContentDescription ( " Upload image " ) ;
LinearLayout . LayoutParams imageParams = new LinearLayout . LayoutParams (
ViewGroup . LayoutParams . MATCH_PARENT ,
0 ,
1
) ;
imageParams . topMargin = dp ( 3 ) ;
actions . addView ( image , imageParams ) ;
row . addView ( actions , new LinearLayout . LayoutParams (
dp ( 48 ) ,
ViewGroup . LayoutParams . MATCH_PARENT
) ) ;
bar . addView ( row , matchWrap ( ) ) ;
return bar ;
}
@@ -2676,19 +2719,26 @@ public final class MainActivity extends Activity {
return input ;
}
private HorizontalScrollView createAccessoryBar ( ) {
HorizontalScrollView scroller = new HorizontalScrollView ( this ) ;
scroller . setHorizontalScrollBarEnabled ( false ) ;
scroller . setBackgroundColor ( COLOR_PANEL ) ;
private LinearLayout createAccessoryBar ( ) {
LinearLayout panel = new LinearLayout ( this ) ;
panel . setOrientation ( LinearLayout . VERTICAL ) ;
panel . setPadding ( dp ( 4 ) , dp ( 3 ) , dp ( 4 ) , dp ( 3 ) ) ;
panel . setBackgroundColor ( COLOR_PANEL ) ;
LinearLayout row = terminalKeyRow ( ) ;
row . setPadding ( dp ( 8 ) , dp ( 5 ) , dp ( 8 ) , dp ( 5 ) ) ;
addAccessoryPageKeys ( row ) ;
scroller . addView ( row , new HorizontalScrollView . LayoutParams (
HorizontalScrollView keyScroller = new HorizontalScrollView ( this ) ;
keyScroller . setHorizontalScrollBarEnabled ( false ) ;
keyScroller . setFillViewport ( true ) ;
LinearLayout keyRow = terminalKeyRow ( ) ;
addAccessoryPageKeys ( keyRow ) ;
keyScroller . addView ( keyRow , new HorizontalScrollView . LayoutParams (
ViewGroup . LayoutParams . WRAP_CONTENT ,
ViewGroup . LayoutParams . MATCH_PARENT
) ) ;
return scroller ;
panel . addView ( keyScroller , new LinearLayout . LayoutParams (
ViewGroup . LayoutParams . MATCH_PARENT ,
ViewGroup . LayoutParams . MATCH_PARENT
) ) ;
return panel ;
}
private LinearLayout terminalKeyRow ( ) {
@@ -2710,6 +2760,9 @@ public final class MainActivity extends Activity {
addSoftKey ( row , " C-r " , " \ u0012 " ) ;
addSoftKey ( row , " C-a " , " \ u0001 " ) ;
addSoftKey ( row , " C-e " , " \ u0005 " ) ;
addSoftKey ( row , " C-u " , " \ u0015 " ) ;
addSoftKey ( row , " C-k " , " \ u000b " ) ;
addSoftKey ( row , " C-b " , " \ u0002 " ) ;
break ;
case 2 :
addSoftKey ( row , " ← " , " \ u001b[D " ) ;
@@ -2720,8 +2773,8 @@ public final class MainActivity extends Activity {
addSoftKey ( row , " End " , " \ u001b[F " ) ;
addSoftKey ( row , " Pg↑ " , " \ u001b[5~ " ) ;
addSoftKey ( row , " Pg↓ " , " \ u001b[6~ " ) ;
addSoftKey ( row , " Prev " , " \ u0002p " ) ;
addSoftKey ( row , " Next " , " \ u0002n " ) ;
addSoftKey ( row , " Clear " , " \ u000c " ) ;
addSoftKey ( row , " Detach " , " \ u0002d " ) ;
break ;
case 3 :
addTextKey ( row , " ~ " , " ~ " ) ;
@@ -2733,6 +2786,8 @@ public final class MainActivity extends Activity {
addTextKey ( row , " & " , " & " ) ;
addTextKey ( row , " ; " , " ; " ) ;
addTextKey ( row , " $ " , " $ " ) ;
addTextKey ( row , " \" " , " \" " ) ;
addTextKey ( row , " ' " , " ' " ) ;
addTextKey ( row , " Space " , " " ) ;
break ;
case 0 :
@@ -2743,30 +2798,22 @@ public final class MainActivity extends Activity {
addSoftKey ( row , " ↓ " , " \ u001b[B " ) ;
addSoftKey ( row , " Esc " , " \ u001b " ) ;
addSoftKey ( row , " Tab " , " \ t " ) ;
addSoftKey ( row , " Enter " , TERMINAL_ENTER ) ;
addSoftKey ( row , " ↵ " , TERMINAL_ENTER ) ;
addSoftButton ( row , " Paste " , view - > pasteClipboard ( ) ) ;
addAccessoryButton ( row , " ⌫ " , view - > backspaceComposerText ( ) ) ;
addAccessoryButton ( row , " ⌄ " , view - > hideKeyboard ( ) ) ;
addAccessoryButton ( row , " ⇣ " , view - > scrollTerminalBottom ( ) ) ;
addAccessoryButton ( row , " Sel " , view - > toggleTerminalSelection ( ) ) ;
addComposerButton ( row , " ⌫ " , this : : backspaceComposerText ) ;
addTextKey ( row , " NL " , " \ n " ) ;
addComposerButton ( row , " Bot " , this : : scrollTerminalBottom ) ;
addComposerButton ( row , " Sel " , ( ) - > {
terminalSelectionEnabled = ! terminalSelectionEnabled ;
if ( terminalText ! = null ) {
terminalText . setTextIsSelectable ( terminalSelectionEnabled ) ;
}
setStatus ( terminalSelectionEnabled ? " Terminal selection on " : " Terminal selection off " ) ;
} ) ;
break ;
}
}
private String accessoryPageName ( ) {
switch ( terminalKeyPage ) {
case 1 :
return " CTRL " ;
case 2 :
return " NAV " ;
case 3 :
return " SYM " ;
case 0 :
default :
return " EDIT " ;
}
}
private void connectTerminal ( String sessionName ) {
terminalReconnectAttempt = 0 ;
terminalConnectionGeneration + + ;
@@ -3033,19 +3080,20 @@ public final class MainActivity extends Activity {
private void setTerminalKeyPage ( int page ) {
terminalKeyPage = ( page + 4 ) % 4 ;
updateTerminalPageButton ( ) ;
updateAccessoryTabs ( ) ;
if ( activeSessionName ! = null ) {
renderTerminalControlsOnly ( ) ;
}
}
private void renderTerminalControlsOnly ( ) {
int composerIndex = terminalComposerBar = = null ? - 1 : root . indexOfChild ( terminalComposerBar ) ;
if ( composerIndex < 0 | | composerIndex + 1 > = root . getChildCount ( ) ) {
int accessoryIndex = terminalAccessoryBar = = null ? - 1 : root . indexOfChild ( terminalAccessoryBar ) ;
if ( accessoryIndex < 0 ) {
return ;
}
root . removeViewAt ( composerIndex + 1 ) ;
root . addView ( createAccessoryBar ( ) , composerIndex + 1 , new LinearLayout . LayoutParams (
root . removeViewAt ( accessoryIndex ) ;
terminalAccessoryBar = createAccessoryBar ( ) ;
root . addView ( terminalAccessoryBar , accessoryIndex , new LinearLayout . LayoutParams (
ViewGroup . LayoutParams . MATCH_PARENT ,
dp ( TERMINAL_KEYS_HEIGHT_DP )
) ) ;
@@ -3198,29 +3246,23 @@ public final class MainActivity extends Activity {
return button ;
}
private Button terminalPageButton ( ) {
Button button = toolbarButton ( " " , view - > setTerminalKeyPage ( terminalKeyPage + 1 ) ) ;
button . setTextSize ( 9 ) ;
button . setPadding ( dp ( 4 ) , 0 , dp ( 4 ) , 0 ) ;
button . setMinWidth ( dp ( 42 ) ) ;
button . setMinimumWidth ( dp ( 42 ) ) ;
button . setOnLongClickListener ( view - > {
view . performHapticFeedback ( HapticFeedbackConstants . LONG_PRESS ) ;
setTerminalKeyPage ( terminalKeyPage - 1 ) ;
return true ;
} ) ;
updateTerminalPageButton ( button ) ;
private Button composerButton ( String label , View . OnClickListener listener ) {
Button button = toolbarButton ( label , listener ) ;
button . setTextSize ( 10 ) ;
button . setPadding ( dp ( 5 ) , 0 , dp ( 5 ) , 0 ) ;
button . setMinWidth ( dp ( 38 ) ) ;
button . setMinimumWidth ( dp ( 38 ) ) ;
button . setMinHeight ( 0 ) ;
button . setMinimumHeight ( 0 ) ;
return button ;
}
private void updateTerminalPageButton ( ) {
updateTerminalPageButton ( terminalKeyPageButton ) ;
}
private void updateTerminalPageButton ( Button button ) {
if ( button ! = null ) {
button . setText ( accessoryPageName ( ) ) ;
}
private Button composerPrimaryButton ( String label , View . OnClickListener listener ) {
Button button = composerButton ( label , listener ) ;
button . setTextColor ( Color . rgb ( 14 , 38 , 24 ) ) ;
button . setTextSize ( 11 ) ;
button . setBackground ( rounded ( COLOR_ACCENT , 7 , COLOR_ACCENT , 1 ) ) ;
return button ;
}
private void styleInput ( EditText input ) {
@@ -3235,8 +3277,8 @@ public final class MainActivity extends Activity {
private void styleComposerInput ( EditText input ) {
input . setTextColor ( COLOR_TEXT ) ;
input . setHintTextColor ( COLOR_TEXT_DIM ) ;
input . setTextSize ( 14 ) ;
input . setPadding ( dp ( 9 ) , dp ( 6 ) , dp ( 9 ) , dp ( 6 ) ) ;
input . setTextSize ( 13 ) ;
input . setPadding ( dp ( 8 ) , dp ( 5 ) , dp ( 8 ) , dp ( 5 ) ) ;
input . setBackground ( inputBackground ( ) ) ;
}
@@ -3288,32 +3330,64 @@ public final class MainActivity extends Activity {
addSoftButton ( row , label , view - > insertComposerText ( text ) ) ;
}
private void addAccessoryButton ( LinearLayout row , String label , View . OnClickListener listener ) {
addSoftButton ( row , label , listener ) ;
}
private void addComposerButton ( LinearLayout row , String label , Runnable action ) {
addSoftButton ( row , label , view - > action . run ( ) ) ;
}
private void addSoftButton ( LinearLayout row , String label , View . OnClickListener listener ) {
Button button = toolbarButton ( label , listener ) ;
button . setTextSize ( isArrowLabel ( label ) ? 15 : 9 ) ;
button . setPadding ( dp ( 4 ) , 0 , dp ( 4 ) , 0 ) ;
int width = " Space " . equals ( label ) ? 56 : ( isArrowLabel ( label ) ? 32 : 38 ) ;
button . setMinWidth ( dp ( width ) ) ;
button . setMinimumWidth ( dp ( width ) ) ;
button . setTextSize ( isArrowLabel ( label ) ? 14 : 9 ) ;
button . setPadding ( dp ( 2 ) , 0 , dp ( 2 ) , 0 ) ;
button . setMinWidth ( dp ( 24 ) ) ;
button . setMinimumWidth ( dp ( 24 ) ) ;
button . setMinHeight ( 0 ) ;
button . setMinimumHeight ( 0 ) ;
LinearLayout . LayoutParams params = new LinearLayout . LayoutParams (
ViewGroup . LayoutParams . WRAP_CONTENT ,
dp ( 27 )
ViewGroup . LayoutParams . MATCH_PARENT ,
0
) ;
params . leftMargin = dp ( 1 ) ;
params . rightMargin = dp ( 1 ) ;
params . topMargin = dp ( 2 ) ;
params . bottomMargin = dp ( 1 ) ;
row . addView ( button , params ) ;
}
private void addAccessoryTab ( LinearLayout row , String label , int page ) {
Button button = toolbarButton ( label , view - > setTerminalKeyPage ( page ) ) ;
button . setTextSize ( 8 ) ;
button . setPadding ( dp ( 3 ) , 0 , dp ( 3 ) , 0 ) ;
button . setMinWidth ( 0 ) ;
button . setMinimumWidth ( 0 ) ;
button . setMinHeight ( 0 ) ;
button . setMinimumHeight ( 0 ) ;
terminalAccessoryTabButtons [ page ] = button ;
styleAccessoryTab ( button , terminalKeyPage = = page ) ;
LinearLayout . LayoutParams params = new LinearLayout . LayoutParams (
0 ,
ViewGroup . LayoutParams . MATCH_PARENT ,
1
) ;
if ( row . getChildCount ( ) > 0 ) {
params . leftMargin = dp ( 3 ) ;
}
row . addView ( button , params ) ;
}
private void updateAccessoryTabs ( ) {
for ( int page = 0 ; page < terminalAccessoryTabButtons . length ; page + + ) {
Button button = terminalAccessoryTabButtons [ page ] ;
if ( button ! = null ) {
styleAccessoryTab ( button , terminalKeyPage = = page ) ;
}
}
}
private void styleAccessoryTab ( Button button , boolean selected ) {
button . setTextColor ( selected ? Color . rgb ( 14 , 38 , 24 ) : COLOR_TEXT_MUTED ) ;
button . setBackground ( selected
? rounded ( COLOR_ACCENT , 6 , COLOR_ACCENT , 1 )
: rounded ( COLOR_CARD_ALT , 6 , COLOR_BORDER_SOFT , 1 ) ) ;
}
private boolean isArrowLabel ( String label ) {
return " ← " . equals ( label ) | | " → " . equals ( label ) | | " ↑ " . equals ( label ) | | " ↓ " . equals ( label ) ;
}
@@ -3621,9 +3695,14 @@ public final class MainActivity extends Activity {
throw new IllegalStateException ( " Cannot open selected image " ) ;
}
String response = api . uploadImage ( sessionName , readAllBytes ( input ) ) ;
String imagePath = new JSONObject ( response ) . optString ( " absolutePath " , " " ) . trim ( ) ;
if ( imagePath . isEmpty ( ) ) {
throw new IllegalStateException ( " Upload response did not include absolutePath " ) ;
}
runOnUiThread ( ( ) - > {
progressBar . setVisibility ( View . GONE ) ;
showTextDialog ( " Image upload " , response ) ;
insertComposerPath ( imagePath ) ;
setStatus ( " Image path inserted " ) ;
} ) ;
} catch ( Exception error ) {
runOnUiThread ( ( ) - > {
@@ -3645,6 +3724,21 @@ public final class MainActivity extends Activity {
return output . toByteArray ( ) ;
}
private void insertComposerPath ( String path ) {
if ( inputField = = null ) {
return ;
}
int start = Math . max ( 0 , inputField . getSelectionStart ( ) ) ;
int end = Math . max ( 0 , inputField . getSelectionEnd ( ) ) ;
int from = Math . min ( start , end ) ;
int to = Math . max ( start , end ) ;
CharSequence current = inputField . getText ( ) ;
String prefix = from > 0 & & ! Character . isWhitespace ( current . charAt ( from - 1 ) ) ? " " : " " ;
String suffix = to < current . length ( ) & & Character . isWhitespace ( current . charAt ( to ) ) ? " " : " " ;
inputField . getText ( ) . replace ( from , to , prefix + path + suffix ) ;
inputField . requestFocus ( ) ;
}
@Override
public void onBackPressed ( ) {
if ( activeSessionName ! = null ) {