package com.wireguard.android.activity; import android.app.Fragment; import android.app.FragmentTransaction; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import com.wireguard.android.R; import com.wireguard.android.fragment.ConfigEditorFragment; import com.wireguard.android.fragment.TunnelDetailFragment; import com.wireguard.android.fragment.TunnelListFragment; import com.wireguard.android.model.Tunnel; 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 = MainActivity.class.getSimpleName(); private State state = State.EMPTY; private boolean moveToState(final State nextState) { Log.i(TAG, "Moving from " + state.name() + " to " + nextState.name()); if (nextState == state) { return false; } else 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 = getFragmentManager().beginTransaction() .replace(R.id.master_fragment, fragment) .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); if (state.layer > 0) transaction.addToBackStack(null); transaction.commit(); } else if (nextState.layer == state.layer - 1) { if (getFragmentManager().getBackStackEntryCount() == 0) return false; getFragmentManager().popBackStack(); } else if (nextState.layer < state.layer - 1) { moveToState(State.ofLayer(state.layer - 1)); moveToState(nextState); return true; } state = nextState; if (state.layer > 1) { if (getActionBar() != null) getActionBar().setDisplayHomeAsUpEnabled(true); } else { if (getActionBar() != null) getActionBar().setDisplayHomeAsUpEnabled(false); setSelectedTunnel(null); } return true; } @Override public void onBackPressed() { if (!moveToState(State.ofLayer(state.layer - 1))) super.onBackPressed(); } @Override protected void onCreate(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); } } @Override public boolean onCreateOptionsMenu(final Menu menu) { getMenuInflater().inflate(R.menu.main_activity, menu); return true; } @Override public boolean onOptionsItemSelected(final MenuItem item) { 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)); return true; case R.id.menu_action_edit: if (getSelectedTunnel() != null) moveToState(State.EDITOR); return true; case R.id.menu_action_save: // This menu item is handled by the editor fragment. return false; case R.id.menu_settings: startActivity(new Intent(this, SettingsActivity.class)); return true; default: return super.onOptionsItemSelected(item); } } @Override protected void onSaveInstanceState(final Bundle outState) { outState.putString(KEY_STATE, state.name()); super.onSaveInstanceState(outState); } @Override protected void onSelectedTunnelChanged(final Tunnel oldTunnel, final Tunnel newTunnel) { moveToState(newTunnel != null ? State.DETAIL : State.LIST); } private enum State { EMPTY(null, 0), LIST(TunnelListFragment.class, 1), DETAIL(TunnelDetailFragment.class, 2), EDITOR(ConfigEditorFragment.class, 3); private final String fragment; private final int layer; State(final Class fragment, final int layer) { this.fragment = fragment != null ? fragment.getName() : null; this.layer = layer; } private static State ofLayer(final int layer) { return Stream.of(State.values()).filter(s -> s.layer == layer).findFirst().get(); } } }