/*
* Artcodes recognises a different marker scheme that allows the
* creation of aesthetically pleasing, even beautiful, codes.
* Copyright (C) 2013-2016 The University of Nottingham
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package uk.ac.horizon.artcodes.activity;
import android.accounts.AccountManager;
import android.app.SearchManager;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.design.widget.NavigationView;
import android.support.v4.app.Fragment;
import android.support.v4.view.GravityCompat;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.SearchView;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.widget.BaseAdapter;
import android.widget.HeaderViewListAdapter;
import android.widget.ListView;
import com.google.android.gms.auth.UserRecoverableAuthException;
import com.google.android.gms.auth.api.Auth;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.gms.auth.api.signin.GoogleSignInResult;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import java.util.List;
import uk.ac.horizon.artcodes.Feature;
import uk.ac.horizon.artcodes.GoogleAnalytics;
import uk.ac.horizon.artcodes.R;
import uk.ac.horizon.artcodes.account.Account;
import uk.ac.horizon.artcodes.databinding.NavigationBinding;
import uk.ac.horizon.artcodes.fragment.ExperienceLibraryFragment;
import uk.ac.horizon.artcodes.fragment.ExperienceRecentFragment;
import uk.ac.horizon.artcodes.fragment.ExperienceRecommendFragment;
import uk.ac.horizon.artcodes.fragment.ExperienceSearchFragment;
import uk.ac.horizon.artcodes.fragment.ExperienceStarFragment;
import uk.ac.horizon.artcodes.fragment.FeatureListFragment;
import uk.ac.horizon.artcodes.server.LoadCallback;
public class NavigationActivity extends ArtcodeActivityBase implements
NavigationView.OnNavigationItemSelectedListener
{
private static final int REQUEST_CODE_SIGN_IN = 63;
private static final int REQUEST_CODE_PICK_ACCOUNT = 67;
private static final long DRAWER_CLOSE_DELAY_MS = 250;
private static final String NAV_ITEM_ID = "navItemId";
private final Handler drawerActionHandler = new Handler();
private NavigationBinding binding;
private ActionBarDrawerToggle drawerToggle;
private MenuItem selected;
private static final String FRAGMENT_TAG = "fragment";
private static final Handler searchHandler = new Handler();
private GoogleApiClient apiClient;
@Override
public void onBackPressed()
{
if (binding.drawer.isDrawerOpen(GravityCompat.START))
{
binding.drawer.closeDrawer(GravityCompat.START);
}
else
{
super.onBackPressed();
}
}
@Override
public void onConfigurationChanged(final Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
drawerToggle.onConfigurationChanged(newConfig);
}
@Override
public boolean onNavigationItemSelected(final MenuItem menuItem)
{
if (menuItem != null)
{
if (menuItem.isCheckable())
{
if (selected != null)
{
selected.setChecked(false);
}
selected = menuItem;
menuItem.setChecked(true);
}
drawerActionHandler.postDelayed(new Runnable()
{
@Override
public void run()
{
navigate(menuItem, true);
}
}, DRAWER_CLOSE_DELAY_MS);
}
binding.drawer.closeDrawer(GravityCompat.START);
return true;
}
@Override
public boolean onOptionsItemSelected(final MenuItem item)
{
if (item.getItemId() == android.support.v7.appcompat.R.id.home)
{
return drawerToggle.onOptionsItemSelected(item);
}
return super.onOptionsItemSelected(item);
}
private void updateAccounts()
{
final Menu menu = binding.navigation.getMenu();
final MenuItem libraries = menu.findItem(R.id.nav_libraries);
final SubMenu subMenu = libraries.getSubMenu();
while (subMenu.size() > 0)
{
subMenu.removeItem(subMenu.getItem(0).getItemId());
}
final List<Account> accounts = getServer().getAccounts();
for (int index = 0; index < accounts.size(); index++)
{
final Account account = accounts.get(index);
final MenuItem menuItem = subMenu.add(R.id.navigation, index, Menu.NONE, account.getDisplayName());
if (account.getId().equals("local"))
{
menuItem.setIcon(R.drawable.ic_folder_24dp);
}
else
{
menuItem.setIcon(R.drawable.ic_cloud_24dp);
}
menuItem.setCheckable(true);
}
final MenuItem menuItem = subMenu.add(R.id.navigation, R.id.nav_addaccount, Menu.NONE, R.string.nav_addaccount);
menuItem.setIcon(R.drawable.ic_add_24dp);
for (int i = 0, count = binding.navigation.getChildCount(); i < count; i++)
{
final View child = binding.navigation.getChildAt(i);
if (child != null && child instanceof ListView)
{
final ListView menuView = (ListView) child;
final HeaderViewListAdapter adapter = (HeaderViewListAdapter) menuView.getAdapter();
final BaseAdapter wrapped = (BaseAdapter) adapter.getWrappedAdapter();
wrapped.notifyDataSetChanged();
}
}
getServer().loadRecent(new LoadCallback<List<String>>()
{
@Override
public void loaded(List<String> item)
{
final MenuItem recent = menu.findItem(R.id.nav_recent);
if (recent != null)
{
recent.setVisible(!item.isEmpty());
}
}
@Override
public void error(Throwable e)
{
GoogleAnalytics.trackException(e);
}
});
getServer().loadStarred(new LoadCallback<List<String>>()
{
@Override
public void loaded(List<String> item)
{
final MenuItem starred = menu.findItem(R.id.nav_starred);
if (starred != null)
{
starred.setVisible(!item.isEmpty());
}
}
@Override
public void error(Throwable e)
{
GoogleAnalytics.trackException(e);
}
});
}
@Override
protected void onNewIntent(Intent intent)
{
Log.i("a", "New intent " + intent);
super.onNewIntent(intent);
}
public void navigate(Fragment fragment, boolean addToBackStack)
{
if (addToBackStack)
{
getSupportFragmentManager().beginTransaction()
.replace(R.id.content, fragment, FRAGMENT_TAG)
.addToBackStack(null)
.commit();
}
else
{
getSupportFragmentManager().beginTransaction()
.replace(R.id.content, fragment, FRAGMENT_TAG)
.commit();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
final MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.search_menu, menu);
final MenuItem searchItem = menu.findItem(R.id.search);
final SearchView searchView = (SearchView) searchItem.getActionView();
final SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener()
{
private Runnable delayedAction = null;
@Override
public boolean onQueryTextSubmit(String query)
{
search(query);
return true;
}
@Override
public boolean onQueryTextChange(final String newText)
{
if (delayedAction != null)
{
searchHandler.removeCallbacks(delayedAction);
delayedAction = null;
}
if (newText.trim().length() > 3)
{
delayedAction = new Runnable()
{
@Override
public void run()
{
search(newText);
}
};
searchHandler.postDelayed(delayedAction, 1000);
}
return true;
}
});
MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener()
{
@Override
public boolean onMenuItemActionExpand(final MenuItem item)
{
return true;
}
@Override
public boolean onMenuItemActionCollapse(final MenuItem item)
{
Log.i("a", "Closed");
Fragment fragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG);
if (fragment instanceof ExperienceSearchFragment)
{
getSupportFragmentManager().popBackStack();
}
return true;
}
});
return true;
}
private void search(String query)
{
Fragment fragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG);
if (fragment instanceof ExperienceSearchFragment)
{
((ExperienceSearchFragment) fragment).search(query);
}
else
{
ExperienceSearchFragment experienceSearchFragment = new ExperienceSearchFragment();
experienceSearchFragment.setArguments(new Bundle());
experienceSearchFragment.getArguments().putString("query", query);
navigate(experienceSearchFragment, true);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
if (requestCode == REQUEST_CODE_PICK_ACCOUNT)
{
if (resultCode == RESULT_OK)
{
if (data != null)
{
if (data.hasExtra(AccountManager.KEY_ACCOUNT_NAME))
{
String accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
if (accountName != null)
{
tryGoogleAccount(accountName, null);
}
}
}
}
}
else if (requestCode == REQUEST_CODE_SIGN_IN)
{
final GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
if (result.getSignInAccount() != null)
{
tryGoogleAccount(result.getSignInAccount().getEmail(), result.getSignInAccount().getDisplayName());
}
}
}
@Override
protected void onCreate(final Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (Feature.get(this, R.bool.feature_show_welcome).isEnabled())
{
startActivity(new Intent(this, AboutArtcodesActivity.class));
return;
}
binding = DataBindingUtil.setContentView(this, R.layout.navigation);
setSupportActionBar(binding.toolbar);
binding.navigation.setNavigationItemSelectedListener(this);
final View headerView = binding.navigation.inflateHeaderView(R.layout.navigation_header);
final MenuItem featureItem = binding.navigation.getMenu().findItem(R.id.nav_features);
if (Feature.get(this, R.bool.feature_edit_features).isEnabled())
{
featureItem.setVisible(true);
}
else
{
headerView.setLongClickable(true);
headerView.setOnLongClickListener(new View.OnLongClickListener()
{
@Override
public boolean onLongClick(View v)
{
featureItem.setVisible(true);
Feature.get(getBaseContext(), R.bool.feature_edit_features).setEnabled(true);
return false;
}
});
}
updateAccounts();
drawerToggle = new ActionBarDrawerToggle(this, binding.drawer, binding.toolbar, R.string.open, R.string.close)
{
@Override
public void onDrawerOpened(View drawerView)
{
super.onDrawerOpened(drawerView);
updateAccounts();
}
};
binding.drawer.addDrawerListener(drawerToggle);
drawerToggle.syncState();
int navigationIndex = R.id.nav_home;
if (savedInstanceState != null)
{
navigationIndex = savedInstanceState.getInt(NAV_ITEM_ID, R.id.nav_home);
}
MenuItem item = binding.navigation.getMenu().findItem(navigationIndex);
if (item == null)
{
item = binding.navigation.getMenu().findItem(R.id.nav_home);
}
navigate(item, false);
final GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.oauth_client_id))
.requestEmail()
.requestProfile()
.build();
apiClient = new GoogleApiClient.Builder(this)
.enableAutoManage(this, new GoogleApiClient.OnConnectionFailedListener()
{
@Override
public void onConnectionFailed(@NonNull final ConnectionResult connectionResult)
{
Log.i("Signin", "Failed " + connectionResult);
}
})
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.build();
apiClient.connect();
}
@Override
protected void onSaveInstanceState(final Bundle outState)
{
super.onSaveInstanceState(outState);
if (selected != null)
{
outState.putInt(NAV_ITEM_ID, selected.getItemId());
}
}
private void tryGoogleAccount(final String name, final String displayName)
{
new Thread(new Runnable()
{
@Override
public void run()
{
try
{
final Account account = getServer().createAccount("google:" + name);
if (account.validates())
{
account.setDisplayName(displayName);
getServer().add(account);
new Handler(Looper.getMainLooper()).post(new Runnable()
{
@Override
public void run()
{
updateAccounts();
final Bundle bundle = new Bundle();
bundle.putString("account", account.getId());
final Fragment fragment = new ExperienceLibraryFragment();
fragment.setArguments(bundle);
navigate(fragment, true);
}
});
}
}
catch (UserRecoverableAuthException userRecoverableException)
{
Log.i("intent", "Intent = " + userRecoverableException.getIntent());
startActivityForResult(userRecoverableException.getIntent(), REQUEST_CODE_PICK_ACCOUNT);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}).start();
}
private void selectAccount()
{
apiClient.clearDefaultAccountAndReconnect();
Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(apiClient);
startActivityForResult(signInIntent, REQUEST_CODE_SIGN_IN);
}
private void navigate(MenuItem item, boolean addToBackStack)
{
switch (item.getItemId())
{
case R.id.nav_home:
navigate(new ExperienceRecommendFragment(), addToBackStack);
break;
case R.id.nav_starred:
navigate(new ExperienceStarFragment(), addToBackStack);
break;
case R.id.nav_features:
navigate(new FeatureListFragment(), addToBackStack);
break;
case R.id.nav_recent:
navigate(new ExperienceRecentFragment(), addToBackStack);
break;
case R.id.nav_about_artcodes:
startActivity(new Intent(this, AboutArtcodesActivity.class));
break;
case R.id.nav_addaccount:
selectAccount();
break;
default:
final List<Account> accounts = getServer().getAccounts();
if (item.getItemId() < accounts.size())
{
final Account account = accounts.get(item.getItemId());
Bundle bundle = new Bundle();
bundle.putString("account", account.getId());
final Fragment fragment = new ExperienceLibraryFragment();
fragment.setArguments(bundle);
navigate(fragment, addToBackStack);
}
break;
}
}
}