diff options
-rw-r--r-- | app/src/androidTest/java/org/connectbot/ConnectbotMatchers.java | 97 | ||||
-rw-r--r-- | app/src/androidTest/java/org/connectbot/StartupTest.java | 118 | ||||
-rw-r--r-- | app/src/main/java/org/connectbot/ConsoleActivity.java | 45 | ||||
-rw-r--r-- | app/src/main/java/org/connectbot/HostListActivity.java | 38 | ||||
-rw-r--r-- | app/src/main/res/layout/item_host.xml | 101 | ||||
-rw-r--r-- | app/src/main/res/values-v11/styles.xml | 12 | ||||
-rw-r--r-- | app/src/main/res/values/styles.xml | 24 |
7 files changed, 289 insertions, 146 deletions
diff --git a/app/src/androidTest/java/org/connectbot/ConnectbotMatchers.java b/app/src/androidTest/java/org/connectbot/ConnectbotMatchers.java new file mode 100644 index 0000000..85a071d --- /dev/null +++ b/app/src/androidTest/java/org/connectbot/ConnectbotMatchers.java @@ -0,0 +1,97 @@ +package org.connectbot; + +import android.support.annotation.ColorInt; +import android.support.annotation.NonNull; +import android.support.test.espresso.matcher.BoundedMatcher; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import org.connectbot.bean.HostBean; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; + +import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant; +import static android.support.test.espresso.matcher.ViewMatchers.withId; +import static org.hamcrest.CoreMatchers.allOf; + +public class ConnectbotMatchers { + /** + * Matches the nickname of a {@link HostBean}. + */ + @NonNull + public static Matcher<Object> withHostNickname(final String content) { + return new BoundedMatcher<Object, HostBean>(HostBean.class) { + @Override + public boolean matchesSafely(HostBean host) { + return host.getNickname().matches(content); + } + + @Override + public void describeTo(Description description) { + description.appendText("with host nickname '" + content + "'"); + } + }; + } + + /** + * Matches the drawable state on an ImageView that is set with setImageState. + */ + @NonNull + public static Matcher<View> withDrawableState(final int expectedState) { + return new TypeSafeMatcher<View>() { + @Override + public boolean matchesSafely(View view) { + if (!(view instanceof ImageView)) { + return false; + } + + int[] states = view.getDrawableState(); + for (int state : states) { + if (state == expectedState) { + return true; + } + } + return false; + } + + @Override + public void describeTo(Description description) { + description.appendText("with drawable state '" + expectedState + "'"); + } + }; + } + + @NonNull + public static Matcher<View> withTextColor(@ColorInt final int expectedColor) { + return new TypeSafeMatcher<View>() { + @Override + public boolean matchesSafely(View view) { + if (!(view instanceof TextView)) { + return false; + } + + TextView tv = (TextView) view; + return tv.getCurrentTextColor() == expectedColor; + } + + @Override + public void describeTo(Description description) { + description.appendText("with color '" + Integer.toHexString(expectedColor) + "'"); + } + }; + } + + @NonNull + public static Matcher<View> hostDisconnected() { + return hasDescendant(allOf(withId(android.R.id.icon), + withDrawableState(android.R.attr.state_expanded))); + } + + @NonNull + public static Matcher<View> hostConnected() { + return hasDescendant(allOf(withId(android.R.id.icon), + withDrawableState(android.R.attr.state_checked))); + } +} diff --git a/app/src/androidTest/java/org/connectbot/StartupTest.java b/app/src/androidTest/java/org/connectbot/StartupTest.java index 0eb9c11..a60d00e 100644 --- a/app/src/androidTest/java/org/connectbot/StartupTest.java +++ b/app/src/androidTest/java/org/connectbot/StartupTest.java @@ -11,7 +11,11 @@ import org.junit.Test; import org.junit.runner.RunWith; import android.content.Intent; +import android.content.res.Resources; +import android.support.annotation.ColorInt; +import android.support.annotation.ColorRes; import android.support.annotation.NonNull; +import android.support.annotation.StringRes; import android.support.test.InstrumentationRegistry; import android.support.test.espresso.intent.Intents; import android.support.test.espresso.matcher.BoundedMatcher; @@ -19,6 +23,7 @@ import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; import android.view.View; import android.widget.ImageView; +import android.widget.TextView; import static android.support.test.espresso.Espresso.onData; import static android.support.test.espresso.Espresso.onView; @@ -29,6 +34,7 @@ import static android.support.test.espresso.action.ViewActions.pressBack; import static android.support.test.espresso.action.ViewActions.pressImeActionButton; import static android.support.test.espresso.action.ViewActions.pressMenuKey; import static android.support.test.espresso.action.ViewActions.typeText; +import static android.support.test.espresso.assertion.ViewAssertions.doesNotExist; import static android.support.test.espresso.assertion.ViewAssertions.matches; import static android.support.test.espresso.intent.Intents.intended; import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent; @@ -36,6 +42,10 @@ import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant; import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; import static android.support.test.espresso.matcher.ViewMatchers.withId; import static android.support.test.espresso.matcher.ViewMatchers.withText; +import static org.connectbot.ConnectbotMatchers.hostConnected; +import static org.connectbot.ConnectbotMatchers.hostDisconnected; +import static org.connectbot.ConnectbotMatchers.withHostNickname; +import static org.connectbot.ConnectbotMatchers.withTextColor; import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; @@ -87,10 +97,60 @@ public class StartupTest { .check(matches(hostDisconnected())); } + @Test + public void localConnectionCanDelete() { + startNewLocalConnectionAndGoBack("Local"); + onData(withHostNickname("Local")).inAdapterView(withId(android.R.id.list)) + .perform(longClick()); + onView(withText(R.string.list_host_delete)).perform(click()); + onView(withText(R.string.delete_pos)).perform(click()); + } + + @Test + public void localConnectionCanChangeToRed() throws Exception { + startNewLocalConnectionAndGoBack("Local1"); + changeColor("Local1", R.color.red, R.string.color_red); + } + + /** + * Changes the color of {@code hostName} from the {@link HostListActivity} to the {@code color} + * from {@code R.color.[color]} with identifying {@code stringForColor} from + * {@code R.string.[colorname]}. + */ + private void changeColor(String hostName, @ColorRes int color, @StringRes int stringForColor) { + // Bring up the context menu. + onData(withHostNickname(hostName)).inAdapterView(withId(android.R.id.list)) + .perform(longClick()); + onView(withText(R.string.list_host_edit)).perform(click()); + + // Click on the color category and select the desired one. + onView(withText(R.string.hostpref_color_title)).perform(click()); + onView(withText(stringForColor)).perform(click()); + + // Go back to the host list. + onView(withText(R.string.hostpref_color_title)).perform(pressBack()); + + Resources res = InstrumentationRegistry.getTargetContext().getResources(); + onData(withHostNickname(hostName)).inAdapterView(withId(android.R.id.list)) + .check(matches(hasDescendant(allOf(withId(android.R.id.text1), + withTextColor(res.getColor(color)))))); + } + + private void startNewLocalConnectionAndGoBack(String name) { + startNewLocalConnection(name); + onView(withId(R.id.console_flip)).perform(closeSoftKeyboard(), pressBack()); + onData(withHostNickname(name)).inAdapterView(withId(android.R.id.list)) + .check(matches(isDisplayed())); + } + private void startNewLocalConnection() { + startNewLocalConnection("Local"); + } + + private void startNewLocalConnection(String name) { onView(withId(R.id.transport_selection)).perform(click()); onData(allOf(is(instanceOf(String.class)), is("local"))).perform(click()); - onView(withId(R.id.front_quickconnect)).perform(typeText("Local")); + onView(withId(R.id.front_quickconnect)).perform(typeText(name)); Intents.init(); try { @@ -103,60 +163,4 @@ public class StartupTest { onView(withId(R.id.console_flip)).check(matches( hasDescendant(allOf(isDisplayed(), withId(R.id.terminal_view))))); } - - /** - * Matches the nickname of a {@link HostBean}. - */ - public static Matcher<Object> withHostNickname(final String content) { - return new BoundedMatcher<Object, HostBean>(HostBean.class) { - @Override - public boolean matchesSafely(HostBean host) { - return host.getNickname().matches(content); - } - - @Override - public void describeTo(Description description) { - description.appendText("with host nickname '" + content + "'"); - } - }; - } - - /** - * Matches the drawable state on an ImageView that is set with setImageState. - */ - public static Matcher<View> withDrawableState(final int expectedState) { - return new TypeSafeMatcher<View>() { - @Override - public boolean matchesSafely(View view) { - if (!(view instanceof ImageView)) { - return false; - } - - int[] states = view.getDrawableState(); - for (int state : states) { - if (state == expectedState) { - return true; - } - } - return false; - } - - @Override - public void describeTo(Description description) { - description.appendText("with drawable state '" + expectedState + "'"); - } - }; - } - - @NonNull - private Matcher<View> hostDisconnected() { - return hasDescendant(allOf(withId(android.R.id.icon), - withDrawableState(android.R.attr.state_expanded))); - } - - @NonNull - private Matcher<View> hostConnected() { - return hasDescendant(allOf(withId(android.R.id.icon), - withDrawableState(android.R.attr.state_checked))); - } } diff --git a/app/src/main/java/org/connectbot/ConsoleActivity.java b/app/src/main/java/org/connectbot/ConsoleActivity.java index 95d21d8..7ed9076 100644 --- a/app/src/main/java/org/connectbot/ConsoleActivity.java +++ b/app/src/main/java/org/connectbot/ConsoleActivity.java @@ -154,7 +154,7 @@ public class ConsoleActivity extends AppCompatActivity implements BridgeDisconne private ImageView mKeyboardButton; - private ActionBar actionBar; + @Nullable private ActionBar actionBar; private boolean inActionBarMenu = false; private boolean titleBarHide; @@ -380,7 +380,11 @@ public class ConsoleActivity extends AppCompatActivity implements BridgeDisconne autoHideEmulatedKeys(); terminal.bridge.tryKeyVibrate(); - if (titleBarHide) { + hideActionBarIfRequested(); + } + + private void hideActionBarIfRequested() { + if (titleBarHide && actionBar != null) { actionBar.hide(); } } @@ -436,9 +440,7 @@ public class ConsoleActivity extends AppCompatActivity implements BridgeDisconne keyboardGroup.startAnimation(keyboard_fade_out); keyboardGroup.setVisibility(View.GONE); - if (titleBarHide) { - actionBar.hide(); - } + hideActionBarIfRequested(); keyboardGroupHider = null; } }; @@ -449,9 +451,7 @@ public class ConsoleActivity extends AppCompatActivity implements BridgeDisconne if (keyboardGroupHider != null) handler.removeCallbacks(keyboardGroupHider); keyboardGroup.setVisibility(View.GONE); - if (titleBarHide) { - actionBar.hide(); - } + hideActionBarIfRequested(); } @Override @@ -488,7 +488,10 @@ public class ConsoleActivity extends AppCompatActivity implements BridgeDisconne if (icicle == null) { requested = getIntent().getData(); } else { - requested = Uri.parse(icicle.getString(STATE_SELECTED_URI)); + String uri = icicle.getString(STATE_SELECTED_URI); + if (uri != null) { + requested = Uri.parse(uri); + } } inflater = LayoutInflater.from(this); @@ -605,18 +608,20 @@ public class ConsoleActivity extends AppCompatActivity implements BridgeDisconne actionBar = getSupportActionBar(); - actionBar.setDisplayHomeAsUpEnabled(true); - if (titleBarHide) { - actionBar.hide(); - } - actionBar.addOnMenuVisibilityListener(new ActionBar.OnMenuVisibilityListener() { - public void onMenuVisibilityChanged(boolean isVisible) { - inActionBarMenu = isVisible; - if (isVisible == false) { - hideEmulatedKeys(); - } + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(true); + if (titleBarHide) { + actionBar.hide(); } - }); + actionBar.addOnMenuVisibilityListener(new ActionBar.OnMenuVisibilityListener() { + public void onMenuVisibilityChanged(boolean isVisible) { + inActionBarMenu = isVisible; + if (isVisible == false) { + hideEmulatedKeys(); + } + } + }); + } final HorizontalScrollView keyboardScroll = (HorizontalScrollView) findViewById(R.id.keyboard_hscroll); if (!hardKeyboard) { diff --git a/app/src/main/java/org/connectbot/HostListActivity.java b/app/src/main/java/org/connectbot/HostListActivity.java index 1fe634e..0e6fa3e 100644 --- a/app/src/main/java/org/connectbot/HostListActivity.java +++ b/app/src/main/java/org/connectbot/HostListActivity.java @@ -43,6 +43,7 @@ import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.preference.PreferenceManager; +import android.support.annotation.StyleRes; import android.text.format.DateUtils; import android.util.Log; import android.view.ContextMenu; @@ -475,6 +476,9 @@ public class HostListActivity extends ListActivity implements OnHostStatusChange intent.setData(uri); startActivity(intent); + // Clear the input box for the next entry. + quickconnect.setText(""); + return true; } @@ -617,26 +621,26 @@ public class HostListActivity extends ListActivity implements OnHostStatusChange break; } - ColorStateList chosen = null; - if (HostDatabase.COLOR_RED.equals(host.getColor())) - chosen = this.red; - else if (HostDatabase.COLOR_GREEN.equals(host.getColor())) - chosen = this.green; - else if (HostDatabase.COLOR_BLUE.equals(host.getColor())) - chosen = this.blue; - - Context context = convertView.getContext(); - - if (chosen != null) { - // set color normally if not selected - holder.nickname.setTextColor(chosen); - holder.caption.setTextColor(chosen); + @StyleRes final int chosenStyleFirstLine; + @StyleRes final int chosenStyleSecondLine; + if (HostDatabase.COLOR_RED.equals(host.getColor())) { + chosenStyleFirstLine = R.style.ListItemFirstLineText_Red; + chosenStyleSecondLine = R.style.ListItemSecondLineText_Red; + } else if (HostDatabase.COLOR_GREEN.equals(host.getColor())) { + chosenStyleFirstLine = R.style.ListItemFirstLineText_Green; + chosenStyleSecondLine = R.style.ListItemSecondLineText_Green; + } else if (HostDatabase.COLOR_BLUE.equals(host.getColor())) { + chosenStyleFirstLine = R.style.ListItemFirstLineText_Blue; + chosenStyleSecondLine = R.style.ListItemSecondLineText_Blue; } else { - // selected, so revert back to default black text - holder.nickname.setTextAppearance(context, android.R.style.TextAppearance_Large); - holder.caption.setTextAppearance(context, android.R.style.TextAppearance_Small); + chosenStyleFirstLine = R.style.ListItemFirstLineText; + chosenStyleSecondLine = R.style.ListItemSecondLineText; } + holder.nickname.setTextAppearance(chosenStyleFirstLine); + holder.caption.setTextAppearance(chosenStyleSecondLine); + + Context context = convertView.getContext(); CharSequence nice = context.getString(R.string.bind_never); if (host.getLastConnect() > 0) { nice = DateUtils.getRelativeTimeSpanString(host.getLastConnect() * 1000); diff --git a/app/src/main/res/layout/item_host.xml b/app/src/main/res/layout/item_host.xml index 56c3d34..8c9f9fc 100644 --- a/app/src/main/res/layout/item_host.xml +++ b/app/src/main/res/layout/item_host.xml @@ -17,60 +17,61 @@ --> <RelativeLayout - android:id="@android:id/content" - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:minHeight="72dp" - > + android:id="@android:id/content" + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:minHeight="72dp" + > - <ImageView - android:id="@android:id/icon" - android:layout_width="40dp" - android:layout_height="40dp" - android:layout_alignParentEnd="true" - android:layout_alignParentRight="true" - android:layout_centerVertical="true" - android:layout_marginEnd="16dp" - android:layout_marginRight="16dp" - android:contentDescription="@string/image_description_connected" - android:src="@drawable/connected" - /> + <ImageView + android:id="@android:id/icon" + android:layout_width="40dp" + android:layout_height="40dp" + android:layout_alignParentEnd="true" + android:layout_alignParentRight="true" + android:layout_centerVertical="true" + android:layout_marginEnd="16dp" + android:layout_marginRight="16dp" + android:contentDescription="@string/image_description_connected" + android:src="@drawable/connected" + /> - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentStart="true" - android:layout_alignParentLeft="true" - android:layout_alignWithParentIfMissing="true" - android:layout_centerVertical="true" - android:layout_marginStart="16dp" - android:layout_marginLeft="16dp" - android:layout_marginEnd="16dp" - android:layout_marginRight="16dp" - android:orientation="vertical" - android:layout_toStartOf="@android:id/icon" - android:layout_toLeftOf="@android:id/icon" - > + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_alignParentStart="true" + android:layout_alignWithParentIfMissing="true" + android:layout_centerVertical="true" + android:layout_marginEnd="16dp" + android:layout_marginLeft="16dp" + android:layout_marginRight="16dp" + android:layout_marginStart="16dp" + android:layout_toLeftOf="@android:id/icon" + android:layout_toStartOf="@android:id/icon" + android:orientation="vertical" + > - <TextView - android:id="@android:id/text1" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:ellipsize="end" - android:singleLine="true" - android:paddingTop="20dp" - android:textAppearance="@style/ListItemFirstLineText" - /> + <TextView + android:id="@android:id/text1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:ellipsize="end" + android:paddingTop="20dp" + android:singleLine="true" + android:textAppearance="@style/ListItemFirstLineText" + tools:text="shell.example.com"/> - <TextView - android:id="@android:id/text2" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingBottom="20dp" - android:textAppearance="@style/ListItemSecondLineText" - /> + <TextView + android:id="@android:id/text2" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingBottom="20dp" + android:textAppearance="@style/ListItemSecondLineText" + tools:text="2 minutes ago"/> - </LinearLayout> + </LinearLayout> </RelativeLayout> diff --git a/app/src/main/res/values-v11/styles.xml b/app/src/main/res/values-v11/styles.xml index 9f8d6a0..8522aae 100644 --- a/app/src/main/res/values-v11/styles.xml +++ b/app/src/main/res/values-v11/styles.xml @@ -17,6 +17,14 @@ */ --> <resources> - <style name="ListItemFirstLineText" parent="TextAppearance.AppCompat" /> - <style name="ListItemSecondLineText" parent="TextAppearance.AppCompat" /> + + <style name="ListItemFirstLineText" parent="TextAppearance.AppCompat"> + <item name="android:textColor">?android:textColorPrimary</item> + <item name="android:textSize">16sp</item> + </style> + + <style name="ListItemSecondLineText" parent="TextAppearance.AppCompat"> + <item name="android:textColor">?android:textColorSecondary</item> + <item name="android:textSize">14sp</item> + </style> </resources> diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index a850d38..13a424f 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -39,4 +39,28 @@ <item name="android:textColor">?android:textColorSecondary</item> <item name="android:textSize">14sp</item> </style> + + <style name="ListItemFirstLineText.Red" parent="ListItemFirstLineText"> + <item name="android:textColor">@color/red</item> + </style> + + <style name="ListItemSecondLineText.Red" parent="ListItemSecondLineText"> + <item name="android:textColor">@color/red</item> + </style> + + <style name="ListItemFirstLineText.Green" parent="ListItemFirstLineText"> + <item name="android:textColor">@color/green</item> + </style> + + <style name="ListItemSecondLineText.Green" parent="ListItemSecondLineText"> + <item name="android:textColor">@color/green</item> + </style> + + <style name="ListItemFirstLineText.Blue" parent="ListItemFirstLineText"> + <item name="android:textColor">@color/blue</item> + </style> + + <style name="ListItemSecondLineText.Blue" parent="ListItemSecondLineText"> + <item name="android:textColor">@color/blue</item> + </style> </resources> |