Java tutorial
/* * Copyright (C) 2016 Source Allies, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.sourceallies.android.zonebeacon.activity; import android.app.Activity; //import android.app.FragmentManager; //import android.app.FragmentTransaction; import android.content.Intent; import android.content.res.Configuration; import android.content.res.Resources; import android.os.Bundle; import android.support.annotation.VisibleForTesting; import android.support.design.widget.CoordinatorLayout; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.widget.AdapterView; import android.widget.Spinner; import com.getbase.floatingactionbutton.FloatingActionButton; import com.getbase.floatingactionbutton.FloatingActionsMenu; import com.google.inject.Inject; import com.sourceallies.android.zonebeacon.R; import com.sourceallies.android.zonebeacon.adapter.GatewaySpinnerAdapter; import com.sourceallies.android.zonebeacon.adapter.MainAdapter; import com.sourceallies.android.zonebeacon.api.QueryLoadsCallback; import com.sourceallies.android.zonebeacon.api.executor.Executor; import com.sourceallies.android.zonebeacon.data.DataSource; import com.sourceallies.android.zonebeacon.data.model.Button; import com.sourceallies.android.zonebeacon.data.model.Gateway; import com.sourceallies.android.zonebeacon.data.model.Zone; import com.sourceallies.android.zonebeacon.util.ControllerUtil; import java.util.List; import java.util.Map; import lombok.Getter; import lombok.Setter; import roboguice.inject.ContentView; /** * Main activity that the user will be interacting with when using the app. */ @ContentView(R.layout.activity_main) public class MainActivity extends RoboAppCompatActivity { public static final int RESULT_INTRO = 1; @Inject private ControllerUtil controllerUtil; @Getter private CoordinatorLayout rootLayout; @Setter @Getter private RecyclerView recycler; @Getter private SwipeRefreshLayout swipeRefreshLayout; @Getter private Toolbar toolbar; @Getter private Spinner spinner; @Getter private View dim; @Getter private FloatingActionsMenu fabMenu; @Getter private FloatingActionButton getHelp; @Getter private FloatingActionButton addZone; @Getter private FloatingActionButton addButton; @Getter private FloatingActionButton addCommand; @Getter private MainAdapter mainAdapter; @Setter private GatewaySpinnerAdapter spinnerAdapter; @Getter private int currentSpinnerSelection = 0; @Getter private boolean startedIntro = false; @Getter @Setter private boolean justCreated = true; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Find the layout information rootLayout = (CoordinatorLayout) findViewById(R.id.root_layout); recycler = (RecyclerView) findViewById(R.id.recycler_view); swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout); toolbar = (Toolbar) findViewById(R.id.toolbar); spinner = (Spinner) findViewById(R.id.toolbar_spinner); dim = findViewById(R.id.dim); fabMenu = (FloatingActionsMenu) findViewById(R.id.fab_menu); getHelp = (FloatingActionButton) findViewById(R.id.get_help); addZone = (FloatingActionButton) findViewById(R.id.add_zone); addButton = (FloatingActionButton) findViewById(R.id.add_button); addCommand = (FloatingActionButton) findViewById(R.id.add_command); // set up the app bar with a spinner, toolbar, and no title setSpinnerAdapter(); setSupportActionBar(toolbar); setTitle(""); // put the labels on the floating action buttons. setFabButtons(); swipeRefreshLayout.setOnRefreshListener(getRefreshListener(this)); swipeRefreshLayout.setColorSchemeResources(R.color.colorAccent, R.color.colorPrimary); } @VisibleForTesting protected SwipeRefreshLayout.OnRefreshListener getRefreshListener(final MainActivity activity) { return new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { activity.setRecycler(); swipeRefreshLayout.setRefreshing(false); } }; } /** * Set the FAB labels to their defaults (containing %s placeholder) */ private void resetFabButtons() { addZone.setTitle(getString(R.string.add_zone)); addButton.setTitle(getString(R.string.add_button)); addCommand.setTitle(getString(R.string.add_command)); } /** * Resets the FAB labels and replaces them with the current gateway information */ private void setFabButtons() { resetFabButtons(); setFabTitle(addZone); setFabTitle(addButton); setFabTitle(addCommand); fabMenu.setOnFloatingActionsMenuUpdateListener( new FloatingActionsMenu.OnFloatingActionsMenuUpdateListener() { @Override public void onMenuCollapsed() { dim.setVisibility(View.GONE); } @Override public void onMenuExpanded() { dim.setVisibility(View.VISIBLE); } }); dim.setOnTouchListener(getDimListener()); getHelp.setOnClickListener(getHelpListener()); } @VisibleForTesting protected View.OnClickListener getHelpListener() { return new View.OnClickListener() { @Override public void onClick(View v) { collapseFab(); openOption(GetHelpActivity.class); } }; } @VisibleForTesting protected View.OnTouchListener getDimListener() { return new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { fabMenu.collapse(); return true; } }; } /** * Replaces the %s placeholder in the Floating Action Buttons with the current gateway * * @param fab floating action button that we are setting the text for */ private void setFabTitle(final FloatingActionButton fab) { fab.setTitle(fab.getTitle().replace("%s", getGatewayName())); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { collapseFab(); if (fab == addZone) { addZone(); } else if (fab == addButton) { addButton(); } else if (fab == addCommand) { addCommand(); } } }); } /** * Creates the adapter for the toolbar spinner that displays the gateways * * @param dataSource The database for the adapter * @return Spinner adapter holding the gateway information */ @VisibleForTesting protected GatewaySpinnerAdapter createSpinnerAdapter(DataSource dataSource) { return new GatewaySpinnerAdapter(this, dataSource.findGateways()); } /** * creates the recycler view to display all the information about zones and buttons * on the current gateway */ @VisibleForTesting public void setRecycler() { final Gateway currentGateway = spinnerAdapter.getItem(getCurrentSpinnerSelection()); DataSource source = getDataSource(); source.open(); GridLayoutManager manager = getLayoutManager(); recycler.setLayoutManager(manager); if (currentGateway != null) { final List<Zone> zones = source.findZones(currentGateway); final List<Button> buttons = source.findButtons(currentGateway); mainAdapter = new MainAdapter(MainActivity.this, currentGateway); loadOnOffStatusesToAdapter(zones, buttons, null); mainAdapter.setLayoutManager(manager); Executor executor = getExecutor(); executor.queryActiveLoads(currentGateway, controllerUtil.getControllerNumbersByZones(zones), getQueryCallback(this, currentGateway, zones, buttons)); } else { mainAdapter = null; } recycler.setAdapter(mainAdapter); source.close(); } /** * Apply the load status map to the adapter * * @param zones List of zones in the current gateway * @param buttons list of buttons in the current gateway * @param map map of the load statuses. Load number is key, status is the value */ @VisibleForTesting protected void loadOnOffStatusesToAdapter(List<Zone> zones, List<Button> buttons, Map<Integer, Map<Integer, Executor.LoadStatus>> map) { mainAdapter.loadOnOffStatuses(zones, buttons, map); } /** * Get the instance of the DataSource. * * @return singleton of the DataSource */ @VisibleForTesting protected DataSource getDataSource() { return DataSource.getInstance(this); } /** * Get the instance of the Executor * * @return singleton of the Executor */ @VisibleForTesting protected Executor getExecutor() { return Executor.createForGateway(getCurrentGateway()); } /** * Get the callback that used for querying the gateway for which loads are on and off * * @param currentGateway the currently selected gateway * @param zones list of zones on the gateway * @param buttons list of buttons on the gateway * @return callback that will reset the RecyclerView to have a list of load statuses */ @VisibleForTesting protected QueryLoadsCallback getQueryCallback(final Activity activity, final Gateway currentGateway, final List<Zone> zones, final List<Button> buttons) { return new QueryLoadsCallback() { @Override public void onResponse(final Map<Integer, Map<Integer, Executor.LoadStatus>> loadStatusMap) { activity.runOnUiThread(setLoadStatusRunnable(currentGateway, zones, buttons, loadStatusMap)); } }; } /** * Get the callback that used for querying the gateway for which loads are on and off * * @param currentGateway the currently selected gateway * @param zones list of zones on the gateway * @param buttons list of buttons on the gateway * @param map the map of load statuses with the load number as the key, and the status as the value * @return Runnable that will reset the RecyclerView to have a list of load statuses */ @VisibleForTesting protected Runnable setLoadStatusRunnable(final Gateway currentGateway, final List<Zone> zones, final List<Button> buttons, final Map<Integer, Map<Integer, Executor.LoadStatus>> map) { return new Runnable() { @Override public void run() { loadOnOffStatusesToAdapter(zones, buttons, map); } }; } /** * Grab the layout manager for the list and adapter; * * @return GridLayoutManager for the view */ @VisibleForTesting protected GridLayoutManager getLayoutManager() { return new GridLayoutManager(this, getColumnCount()); } /** * Get the number of columns in the grid. * * @return integer for the number of columns used in the grid */ @VisibleForTesting protected int getColumnCount() { Resources res = getResources(); if (res.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) return 2; if (res.getBoolean(R.bool.tablet)) return 2; return 1; } /** * Adds the current gateways in the database to the toolbar spinner. */ @VisibleForTesting protected void setSpinnerAdapter() { DataSource dataSource = getDataSource(); dataSource.open(); spinnerAdapter = createSpinnerAdapter(dataSource); spinner.setAdapter(spinnerAdapter); dataSource.close(); startIntro(); spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onNothingSelected(AdapterView<?> parent) { } @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { if (position == spinner.getCount() - 1 && spinner.getCount() > 1) { createNewGateway(); spinner.setSelection(currentSpinnerSelection); } else { currentSpinnerSelection = position; setFabButtons(); setRecycler(); } } }); } /** * Use the creation activity to create a new gateway */ public void createNewGateway() { CreationActivity.startCreation(this, CreationActivity.TYPE_GATEWAY); } /** * Use the creation activity to add a zone. */ public void addZone() { startCreationActivity(CreationActivity.TYPE_ZONE); } /** * Use the creation activity to add a button. */ public void addButton() { startCreationActivity(CreationActivity.TYPE_BUTTON); } /** * Use the creation activity to add a command. */ public void addCommand() { startCreationActivity(CreationActivity.TYPE_COMMAND); } private void startCreationActivity(int type) { CreationActivity.startCreation(this, type, getCurrentGatewayId()); } /** * Get the name of whatever gateway the user has currently selected * * @return String of the gateway name */ private String getGatewayName() { return spinnerAdapter.getTitle(getCurrentSpinnerSelection()); } /** * Get the name of whatever gateway the user has currently selected * * @return String of the gateway name */ private Gateway getCurrentGateway() { return spinnerAdapter.getItem(getCurrentSpinnerSelection()); } protected long getCurrentGatewayId() { return getCurrentGateway().getId(); } /** * Start the intro activity if necessary */ public boolean startIntro() { if (spinnerAdapter.getCount() == 1) { startActivityForResult(new Intent(this, IntroActivity.class), RESULT_INTRO); return true; } return false; } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == RESULT_INTRO && (resultCode == RESULT_OK || resultCode == RESULT_CANCELED)) { // if the user is just returning from the intro, then we will recreate everything recreate(); } else if (requestCode == CreationActivity.TYPE_GATEWAY && resultCode == RESULT_OK) { // if the user just added a gateway, it should be put into the spinner setSpinnerAdapter(); } else if ((requestCode == CreationActivity.TYPE_BUTTON || requestCode == CreationActivity.TYPE_ZONE) && resultCode == RESULT_OK) { // if the user added a button or zone, we want to update the list setRecycler(); } else { super.onActivityResult(requestCode, resultCode, data); } } @Override public void onResume() { super.onResume(); if (!justCreated) { // Add the data to the spinnerAdapter and display it in the recycler setRecycler(); } justCreated = false; } @Override public void onBackPressed() { if (fabMenu.isExpanded()) { collapseFab(); } else { super.onBackPressed(); } } /** * Collapse the fab menu. */ @VisibleForTesting protected void collapseFab() { fabMenu.collapse(); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.activity_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.settings: openOption(IntroActivity.class); return true; case R.id.diagnosis: openOption(null); return true; case R.id.transfer_settings: openOption(TransferActivity.class); return true; default: return super.onOptionsItemSelected(item); } } /** * Start the activity for the class you want to open * * @param toOpen the class that should be opened. */ public void openOption(Class toOpen) { if (toOpen != null) { Intent option = new Intent(this, toOpen); startActivity(option); } } }