summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorSamuel Holland <samuel@sholland.org>2018-07-28 17:06:39 -0500
committerSamuel Holland <samuel@sholland.org>2018-07-28 17:07:37 -0500
commitca92ac60b755acb7b27a9f74b85d136ac6b45072 (patch)
tree70961ae3ae24d83350318960ea51bb8f01ae0523
parente29c21f8df78b0a855d1fd00fd70c1f429d49954 (diff)
MainActivity: Fix fragment selection logic
Signed-off-by: Samuel Holland <samuel@sholland.org>
-rw-r--r--app/src/main/java/com/wireguard/android/activity/MainActivity.java171
-rw-r--r--app/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.java1
-rw-r--r--app/src/main/res/layout-w600dp/main_activity.xml25
-rw-r--r--app/src/main/res/layout/main_activity.xml16
4 files changed, 104 insertions, 109 deletions
diff --git a/app/src/main/java/com/wireguard/android/activity/MainActivity.java b/app/src/main/java/com/wireguard/android/activity/MainActivity.java
index b3c21133..2d524758 100644
--- a/app/src/main/java/com/wireguard/android/activity/MainActivity.java
+++ b/app/src/main/java/com/wireguard/android/activity/MainActivity.java
@@ -10,12 +10,13 @@ import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
-import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
-import android.util.Log;
+import android.support.v7.app.ActionBar;
import android.view.Menu;
import android.view.MenuItem;
+import android.view.View;
+import android.widget.LinearLayout;
import com.wireguard.android.R;
import com.wireguard.android.fragment.TunnelDetailFragment;
@@ -23,71 +24,46 @@ import com.wireguard.android.fragment.TunnelEditorFragment;
import com.wireguard.android.fragment.TunnelListFragment;
import com.wireguard.android.model.Tunnel;
-import java.util.List;
-
-import java9.util.stream.Stream;
-
/**
* CRUD interface for WireGuard tunnels. This activity serves as the main entry point to the
* WireGuard application, and contains several fragments for listing, viewing details of, and
* editing the configuration and interface state of WireGuard tunnels.
*/
-public class MainActivity extends BaseActivity {
- private static final String KEY_STATE = "fragment_state";
- private static final String TAG = "WireGuard/" + MainActivity.class.getSimpleName();
-
- private State state = State.EMPTY;
-
- private boolean moveToState(final State nextState) {
- if (state == nextState)
- return false;
- final FragmentManager fragmentManager = getSupportFragmentManager();
- Log.i(TAG, "Moving from " + state.name() + " to " + nextState.name());
- if (nextState.layer > state.layer + 1) {
- moveToState(State.ofLayer(state.layer + 1));
- moveToState(nextState);
- return true;
- } else if (nextState.layer == state.layer + 1) {
- final Fragment fragment = Fragment.instantiate(this, nextState.fragment);
- final FragmentTransaction transaction = fragmentManager.beginTransaction()
- .replace(R.id.master_fragment, fragment)
- .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
- if (state.layer > 0)
- transaction.addToBackStack(null);
- transaction.commitAllowingStateLoss(); /* TODO: switch back to .commit() when this function is rewritten. */
- } else if (nextState.layer == state.layer - 1) {
- if (fragmentManager.getBackStackEntryCount() == 0)
- return false;
- fragmentManager.popBackStack();
- } else if (nextState.layer < state.layer - 1) {
- moveToState(State.ofLayer(state.layer - 1));
- moveToState(nextState);
- return true;
- }
- state = nextState;
- if (state.layer <= State.LIST.layer)
- setSelectedTunnel(null);
- updateActionBar();
- return true;
- }
+public class MainActivity extends BaseActivity
+ implements FragmentManager.OnBackStackChangedListener {
+ @Nullable private ActionBar actionBar;
+ private boolean isTwoPaneLayout;
+ @Nullable private TunnelListFragment listFragment;
@Override
public void onBackPressed() {
- final List<Fragment> fragments = getSupportFragmentManager().getFragments();
-
- boolean handled = false;
- if (!fragments.isEmpty() && fragments.get(0) instanceof TunnelListFragment) {
- handled = ((TunnelListFragment) fragments.get(0)).collapseActionMenu();
+ final int backStackEntries = getSupportFragmentManager().getBackStackEntryCount();
+ // If the action menu is visible and expanded, collapse it instead of navigating back.
+ if (isTwoPaneLayout || backStackEntries == 0) {
+ if (listFragment != null && listFragment.collapseActionMenu())
+ return;
}
-
- if (!handled) {
- handled = moveToState(State.ofLayer(state.layer - 1));
+ // If the two-pane layout does not have an editor open, going back should exit the app.
+ if (isTwoPaneLayout && backStackEntries <= 1) {
+ finish();
+ return;
}
-
- if (!handled) {
- super.onBackPressed();
+ // Deselect the current tunnel on navigating back from the detail pane to the one-pane list.
+ if (!isTwoPaneLayout && backStackEntries == 1) {
+ setSelectedTunnel(null);
+ return;
}
+ super.onBackPressed();
+ }
+
+ @Override public void onBackStackChanged() {
+ if (actionBar == null)
+ return;
+ // Do not show the home menu when the two-pane layout is at the detail view (see above).
+ final int backStackEntries = getSupportFragmentManager().getBackStackEntryCount();
+ final int minBackStackEntries = isTwoPaneLayout ? 2 : 1;
+ actionBar.setDisplayHomeAsUpEnabled(backStackEntries >= minBackStackEntries);
}
// We use onTouchListener here to avoid the UI click sound, hence
@@ -97,25 +73,14 @@ public class MainActivity extends BaseActivity {
protected void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
- if (savedInstanceState != null && savedInstanceState.getString(KEY_STATE) != null)
- state = State.valueOf(savedInstanceState.getString(KEY_STATE));
- if (state == State.EMPTY) {
- State initialState = getSelectedTunnel() != null ? State.DETAIL : State.LIST;
- if (getIntent() != null && getIntent().getStringExtra(KEY_STATE) != null)
- initialState = State.valueOf(getIntent().getStringExtra(KEY_STATE));
- moveToState(initialState);
- }
- updateActionBar();
- final int actionBarId = getResources().getIdentifier("action_bar", "id", getPackageName());
- if (actionBarId != 0 && findViewById(actionBarId) != null) {
- findViewById(actionBarId).setOnTouchListener((v, e) -> {
- final List<Fragment> fragments = getSupportFragmentManager().getFragments();
- if (!fragments.isEmpty() && fragments.get(0) instanceof TunnelListFragment) {
- ((TunnelListFragment) fragments.get(0)).collapseActionMenu();
- }
- return false;
- });
- }
+ actionBar = getSupportActionBar();
+ isTwoPaneLayout = findViewById(R.id.master_detail_wrapper) instanceof LinearLayout;
+ listFragment = (TunnelListFragment) getSupportFragmentManager().findFragmentByTag("LIST");
+ getSupportFragmentManager().addOnBackStackChangedListener(this);
+ onBackStackChanged();
+ final View actionBarView = findViewById(R.id.action_bar);
+ if (actionBarView != null)
+ actionBarView.setOnTouchListener((v, e) -> listFragment != null && listFragment.collapseActionMenu());
}
@Override
@@ -129,11 +94,14 @@ public class MainActivity extends BaseActivity {
switch (item.getItemId()) {
case android.R.id.home:
// The back arrow in the action bar should act the same as the back button.
- moveToState(State.ofLayer(state.layer - 1));
+ onBackPressed();
return true;
case R.id.menu_action_edit:
- if (getSelectedTunnel() != null)
- moveToState(State.EDITOR);
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.detail_container, new TunnelEditorFragment())
+ .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
+ .addToBackStack(null)
+ .commit();
return true;
case R.id.menu_action_save:
// This menu item is handled by the editor fragment.
@@ -147,37 +115,26 @@ public class MainActivity extends BaseActivity {
}
@Override
- protected void onSaveInstanceState(final Bundle outState) {
- outState.putString(KEY_STATE, state.name());
- super.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onSelectedTunnelChanged(@Nullable final Tunnel oldTunnel, @Nullable final Tunnel newTunnel) {
- moveToState(newTunnel != null ? State.DETAIL : State.LIST);
- }
-
- private void updateActionBar() {
- if (getSupportActionBar() != null)
- getSupportActionBar().setDisplayHomeAsUpEnabled(state.layer > State.LIST.layer);
- }
-
- private enum State {
- EMPTY(null, 0),
- LIST(TunnelListFragment.class, 1),
- DETAIL(TunnelDetailFragment.class, 2),
- EDITOR(TunnelEditorFragment.class, 3);
-
- @Nullable private final String fragment;
- private final int layer;
-
- State(@Nullable final Class<? extends Fragment> fragment, final int layer) {
- this.fragment = fragment != null ? fragment.getName() : null;
- this.layer = layer;
+ protected void onSelectedTunnelChanged(@Nullable final Tunnel oldTunnel,
+ @Nullable final Tunnel newTunnel) {
+ final FragmentManager fragmentManager = getSupportFragmentManager();
+ final int backStackEntries = fragmentManager.getBackStackEntryCount();
+ if (newTunnel == null) {
+ // Clear everything off the back stack (all editors and detail fragments).
+ fragmentManager.popBackStackImmediate(0, FragmentManager.POP_BACK_STACK_INCLUSIVE);
+ return;
}
-
- private static State ofLayer(final int layer) {
- return Stream.of(State.values()).filter(s -> s.layer == layer).findFirst().get();
+ if (backStackEntries == 2) {
+ // Pop the editor off the back stack to reveal the detail fragment. Use the immediate
+ // method to avoid the editor picking up the new tunnel while it is still visible.
+ fragmentManager.popBackStackImmediate();
+ } else if (backStackEntries == 0) {
+ // Create and show a new detail fragment.
+ fragmentManager.beginTransaction()
+ .add(R.id.detail_container, new TunnelDetailFragment())
+ .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
+ .addToBackStack(null)
+ .commit();
}
}
}
diff --git a/app/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.java b/app/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.java
index 1ab4f2a2..0d432a20 100644
--- a/app/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.java
+++ b/app/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.java
@@ -174,6 +174,7 @@ public class TunnelEditorFragment extends BaseFragment implements AppExclusionLi
}
// Tell the activity to finish itself or go back to the detail view.
getActivity().runOnUiThread(() -> {
+ // TODO(smaeul): Remove this hack when fixing the Config ViewModel
// The selected tunnel has to actually change, but we have to remember this one.
final Tunnel savedTunnel = tunnel;
if (savedTunnel == getSelectedTunnel())
diff --git a/app/src/main/res/layout-w600dp/main_activity.xml b/app/src/main/res/layout-w600dp/main_activity.xml
new file mode 100644
index 00000000..b751c8c1
--- /dev/null
+++ b/app/src/main/res/layout-w600dp/main_activity.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/master_detail_wrapper"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:baselineAligned="false"
+ android:divider="?android:attr/dividerHorizontal"
+ android:orientation="horizontal"
+ android:showDividers="middle"
+ tools:context=".activity.MainActivity">
+
+ <fragment
+ android:name="com.wireguard.android.fragment.TunnelListFragment"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="2"
+ android:tag="LIST" />
+
+ <FrameLayout
+ android:id="@+id/detail_container"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="3" />
+</LinearLayout>
diff --git a/app/src/main/res/layout/main_activity.xml b/app/src/main/res/layout/main_activity.xml
index 0f21e2e8..a4fd7fd1 100644
--- a/app/src/main/res/layout/main_activity.xml
+++ b/app/src/main/res/layout/main_activity.xml
@@ -1,7 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/master_fragment"
+ android:id="@+id/master_detail_wrapper"
android:layout_width="match_parent"
android:layout_height="match_parent"
- tools:ignore="MergeRootFrame" />
+ tools:context=".activity.MainActivity">
+
+ <fragment
+ android:name="com.wireguard.android.fragment.TunnelListFragment"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:tag="LIST" />
+
+ <FrameLayout
+ android:id="@+id/detail_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+</FrameLayout>