/*
* The MIT License (MIT)
*
* Copyright (c) 2015 Curt Binder
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package info.curtbinder.reefangel.phone;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import info.curtbinder.reefangel.wizard.SetupWizardActivity;
public class MainActivity extends ActionBarActivity
implements ActionBar.OnNavigationListener,
FragmentManager.OnBackStackChangedListener {
// public static final int REQUEST_EXIT = 1;
// public static final int RESULT_EXIT = 1024;
private static final String OPENED_KEY = "OPENED_KEY";
private static final String STATE_CHECKED = "DRAWER_CHECKED";
private static final String PREVIOUS_CHECKED = "PREVIOUS";
// do not switch selected profile when restoring the application state
private static boolean fRestoreState = false;
public final String TAG = MainActivity.class.getSimpleName();
private RAApplication raApp;
private String[] mNavTitles;
private Toolbar mToolbar;
private DrawerLayout mDrawerLayout;
private ListView mDrawerList;
private ActionBarDrawerToggle mDrawerToggle;
private Boolean opened = null;
private int mOldPosition = -1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
raApp = (RAApplication) getApplication();
raApp.raprefs.setDefaultPreferences();
// Set the Theme before the layout is instantiated
//Utils.onActivityCreateSetTheme(this, raApp.raprefs.getSelectedTheme());
setContentView(R.layout.activity_main);
// Check for first run
if (raApp.isFirstRun()) {
Intent i = new Intent(this, SetupWizardActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(i);
finish();
}
// Load any saved position
int position = 0;
if (savedInstanceState != null) {
position = savedInstanceState.getInt(STATE_CHECKED, 0);
Log.d(TAG, "Restore, position: " + position);
}
setupToolbar();
setupNavDrawer();
updateActionBar();
getSupportFragmentManager().addOnBackStackChangedListener(this);
selectItem(position);
// launch a new thread to show the drawer on very first app launch
new Thread(new Runnable() {
@Override
public void run() {
opened = raApp.raprefs.getBoolean(OPENED_KEY, false);
if (!opened) {
mDrawerLayout.openDrawer(mDrawerList);
}
}
}).start();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// get the checked item and subtract off one to get the actual position
// the same logic applies that is used in the DrawerItemClickedListener.onItemClicked
outState.putInt(STATE_CHECKED, mDrawerList.getCheckedItemPosition() - 1);
}
@Override
protected void onResume() {
super.onResume();
fRestoreState = true;
setNavigationList();
if (raApp.raprefs.isKeepScreenOnEnabled()) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
// last thing we do is display the changelog if necessary
// TODO add in a preference check for displaying changelog on app startup
raApp.displayChangeLog(this);
}
@Override
protected void onPause() {
super.onPause();
}
private void setupToolbar() {
mToolbar = (Toolbar) findViewById(R.id.toolbar);
mToolbar.setTitle("");
setSupportActionBar(mToolbar);
}
private void setupNavDrawer() {
// get the string array for the navigation items
mNavTitles = getResources().getStringArray(R.array.nav_items);
// locate the navigation drawer items in the layout
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerList = (ListView) findViewById(R.id.left_drawer);
// set a custom shadow that overlays the main content when the drawer
// opens
mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow,
GravityCompat.START);
// add in the logo header
View header = getLayoutInflater().inflate(R.layout.drawer_list_header, null);
mDrawerList.addHeaderView(header, null, false);
// set the adapter for the navigation list view
ArrayAdapter<String> adapter =
new ArrayAdapter<String>(this, R.layout.drawer_list_item,
mNavTitles);
mDrawerList.setAdapter(adapter);
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
// setup the toggling for the drawer
mDrawerToggle =
new MyDrawerToggle(this, mDrawerLayout, mToolbar,
R.string.drawer_open, R.string.drawer_close);
mDrawerLayout.setDrawerListener(mDrawerToggle);
}
private void setNavigationList() {
// set list navigation items
final ActionBar ab = getSupportActionBar();
Context context = ab.getThemedContext();
int arrayID;
if (raApp.isAwayProfileEnabled()) {
arrayID = R.array.profileLabels;
} else {
arrayID = R.array.profileLabelsHomeOnly;
}
ArrayAdapter<CharSequence> list =
ArrayAdapter.createFromResource(context, arrayID,
R.layout.support_simple_spinner_dropdown_item);
ab.setListNavigationCallbacks(list, this);
ab.setSelectedNavigationItem(raApp.getSelectedProfile());
}
private void updateActionBar() {
// update actionbar
final ActionBar ab = getSupportActionBar();
ab.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
}
@Override
public boolean onNavigationItemSelected(int itemPosition, long itemId) {
// only switch profiles when the user changes the navigation item,
// not when the navigation list state is restored
if (!fRestoreState) {
raApp.setSelectedProfile(itemPosition);
} else {
fRestoreState = false;
}
return true;
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mDrawerToggle.onConfigurationChanged(newConfig);
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// sync the toggle state after onRestoreInstanceState has occurred
mDrawerToggle.syncState();
}
@Override
public void onBackPressed() {
/*
When the back button is pressed, this function is called.
If the drawer is open, check it and cancel it here.
Calling super.onBackPressed() causes the BackStackChangeListener to be called
*/
// Log.d(TAG, "onBackPressed");
if (mDrawerLayout.isDrawerOpen(mDrawerList)) {
// Log.d(TAG, "drawer open, closing");
mDrawerLayout.closeDrawer(mDrawerList);
return;
}
super.onBackPressed();
}
@Override
public void onBackStackChanged() {
// Log.d(TAG, "onBackStackChanged");
/*
This is called when there is a change to the backstack.
The call is when items are added or removed from the backstack.
Mostly just need to update the UI in this function.
Since we are replacing all the fragments in our example, we need to check
for an empty backstack and exit the app when it is encountered. If we do not,
we are left with a blank screen in the activity and the last fragment is removed.
We are then required to press back one more time to actually exit the application.
This check combines the extra step to make it function more naturally.
*/
FragmentManager manager = getSupportFragmentManager();
//dumpBackStack(manager);
int count = manager.getBackStackEntryCount();
if (count == 0) {
// Log.d(TAG, "empty backstack, exit");
finish();
return;
}
/*
Our fragment backstack uses the position number in the navigation drawer as
the fragment tag for simplicity with highlighting the item.
uncertain how this will be handled if we have additional fragments added to the
backstack that are not referenced in the navigation drawer
*/
FragmentManager.BackStackEntry entry = manager.getBackStackEntryAt(count - 1);
int last = Integer.parseInt(entry.getName());
highlightItem(last);
}
/*private void dumpBackStack(FragmentManager fm) {
// dumps the contents of the backstack in a string
int count = fm.getBackStackEntryCount();
// 0 based index, last item is 1 less than count
FragmentManager.BackStackEntry e;
String s = "";
for (int i = count - 1; i >= 0; i--) {
e = fm.getBackStackEntryAt(i);
s += e.getName() + ", ";
}
s += "null";
Log.d(TAG, "BS Dump (" + count + "): " + s);
}*/
private void updateContent(int position) {
if (position != mOldPosition) {
// update the main content by replacing fragments
Fragment fragment;
switch (position) {
default:
case 0:
fragment = StatusFragment.newInstance();
break;
case 1:
fragment = MemoryFragment.newInstance(raApp.raprefs.useOldPre10MemoryLocations());
break;
case 2:
fragment = NotificationsFragment.newInstance();
break;
case 3:
fragment = HistoryFragment.newInstance();
break;
case 4:
fragment = ErrorsFragment.newInstance();
break;
case 5:
fragment = DateTimeFragment.newInstance();
break;
}
Log.d(TAG, "UpdateContent: " + position);
FragmentTransaction ft =
getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame, fragment);
ft.addToBackStack("" + position);
ft.commit();
mOldPosition = position;
}
}
public void selectItem(int position) {
// Log.d(TAG, "selectItem: " + position);
updateContent(position);
mDrawerLayout.closeDrawer(mDrawerList);
}
public void highlightItem(int position) {
// Log.d(TAG, "highlightItem: " + position);
// since we are using a header for the list, the first
// item/position in the list is the header. our header is non-selectable
// so in order for us to have the proper item in our list selected, we must
// increase the position by 1. this same logic is applied to the
// DrawerItemClickedListener.onItemClicked
mDrawerList.setItemChecked(position + 1, true);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.global, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// pass the event to ActionBarDrawerToggle, if it returns true,
// then it has handled the app icon touch event
if (mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
// handle the rest of the action bar items here
switch (item.getItemId()) {
case R.id.action_settings:
Fragment f = getSupportFragmentManager().findFragmentById(R.id.content_frame);
if (f instanceof StatusFragment) {
// the current fragment is the status fragment
Log.d(TAG, "Status Fragment is current");
((StatusFragment) f).reloadPages();
}
startActivity(new Intent(this, SettingsActivity.class));
// startActivityForResult(new Intent(this, SettingsActivity.class), REQUEST_EXIT);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
// @Override
// protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// Log.d(TAG, "onActivityResult");
// if (requestCode == REQUEST_EXIT) {
// if (resultCode == RESULT_EXIT) {
// this.finish();
// }
// }
// }
// called whenever we call invalidateOptionsMenu()
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
/*
This function is called after invalidateOptionsMenu is called.
This happens when the Navigation drawer is opened and closed.
*/
boolean open = mDrawerLayout.isDrawerOpen(mDrawerList);
hideMenuItems(menu, open);
return super.onPrepareOptionsMenu(menu);
}
private void hideMenuItems(Menu menu, boolean open) {
// hide the menu item(s) when the drawer is open
// Refresh button on Status page
MenuItem mi = menu.findItem(R.id.action_refresh);
if ( mi != null )
mi.setVisible(!open);
// Add button on Notification page
mi = menu.findItem(R.id.action_add_notification);
if ( mi != null )
mi.setVisible(!open);
// Delete button on Notification page
mi = menu.findItem(R.id.action_delete_notification);
if ( mi != null )
mi.setVisible(!open);
// Delete button on History and Error pages
mi = menu.findItem(R.id.menu_delete);
if ( mi != null )
mi.setVisible(!open);
}
private class MyDrawerToggle extends ActionBarDrawerToggle {
public MyDrawerToggle(Activity activity, DrawerLayout drawerLayout,
Toolbar toolbar,
int openDrawerContentDescRes,
int closeDrawerContentDescRes) {
super(activity, drawerLayout, toolbar,
openDrawerContentDescRes, closeDrawerContentDescRes);
}
@Override
public void onDrawerClosed(View drawerView) {
super.onDrawerClosed(drawerView);
// Log.d(TAG, "DrawerClosed");
invalidateOptionsMenu();
if (opened != null && !opened) {
// drawer closed for the first time ever,
// set that it has been closed
opened = true;
raApp.raprefs.set(OPENED_KEY, true);
}
}
@Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
// getSupportActionBar().setTitle(R.string.app_name);
invalidateOptionsMenu();
}
}
private class DrawerItemClickListener implements
ListView.OnItemClickListener {
@Override
public void onItemClick(
AdapterView<?> parent,
View view,
int position,
long id) {
// Perform action when a drawer item is selected
// Log.d(TAG, "onDrawerItemClick: " + position);
// when we have a list header, it counts as a position in the list
// the first position to be exact. so we have to decrease the
// position by 1 to get the proper item chosen in our list
selectItem(position - 1);
}
}
}