/** * Copyright (C) 2013 by Raphael Michel under the MIT license: * * 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 de.geeksfactory.opacclient.frontend; import android.annotation.TargetApi; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.NavigationView; import android.support.v4.app.ActivityOptionsCompat; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.support.v4.view.ViewCompat; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.transition.TransitionInflater; import android.util.DisplayMetrics; import android.view.Gravity; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.TextView; import java.io.IOException; import java.io.InputStream; import java.util.List; import java.util.Map; import de.geeksfactory.opacclient.OpacClient; import de.geeksfactory.opacclient.R; import de.geeksfactory.opacclient.objects.Account; import de.geeksfactory.opacclient.reminder.ReminderHelper; import de.geeksfactory.opacclient.storage.AccountDataSource; import de.geeksfactory.opacclient.ui.AccountSwitcherNavigationView; import de.geeksfactory.opacclient.utils.Utils; public abstract class OpacActivity extends AppCompatActivity implements DrawerAccountsAdapter.Listener { protected OpacClient app; protected AlertDialog adialog; protected AccountDataSource aData; protected int selectedItemId; protected AccountSwitcherNavigationView drawer; protected DrawerLayout drawerLayout; protected ActionBarDrawerToggle drawerToggle; protected FloatingActionButton fab; protected CharSequence title; protected Fragment fragment; protected boolean hasDrawer = false; protected Toolbar toolbar; private boolean twoPane; private boolean fabVisible; protected List<Account> accounts; protected ImageView accountExpand; protected TextView accountTitle; protected TextView accountSubtitle; protected TextView accountWarning; protected LinearLayout accountData; protected boolean accountSwitcherVisible = false; protected DrawerAccountsAdapter accountsAdapter; protected static void unbindDrawables(View view) { if (view == null) { return; } if (view.getBackground() != null) { view.getBackground().setCallback(null); } if (view instanceof ViewGroup) { for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) { unbindDrawables(((ViewGroup) view).getChildAt(i)); } if (!(view instanceof AdapterView)) { ((ViewGroup) view).removeAllViews(); } } } @Override public void onCreate(Bundle savedInstanceState) { supportRequestWindowFeature(android.view.Window.FEATURE_INDETERMINATE_PROGRESS); super.onCreate(savedInstanceState); setContentView(getContentView()); app = (OpacClient) getApplication(); aData = new AccountDataSource(this); toolbar = (Toolbar) findViewById(R.id.toolbar); if (toolbar != null) { setSupportActionBar(toolbar); } fab = (FloatingActionButton) findViewById(R.id.search_fab); setupDrawer(); setupAccountSwitcher(); if (savedInstanceState != null) { setTwoPane(savedInstanceState.getBoolean("twoPane")); setFabVisible(savedInstanceState.getBoolean("fabVisible")); selectedItemId = savedInstanceState.getInt("selectedItemId"); setFabOnClickListener(selectedItemId); if (savedInstanceState.containsKey("title")) { setTitle(savedInstanceState.getCharSequence("title")); } if (savedInstanceState.containsKey("fragment")) { fragment = getSupportFragmentManager().getFragment( savedInstanceState, "fragment"); getSupportFragmentManager().beginTransaction() .replace(R.id.content_frame, fragment).commit(); } } fixStatusBarFlashing(); } protected void setupAccountSwitcher() { if (drawer == null || app.getAccount() == null) return; accounts = aData.getAllAccounts(); Account selectedAccount = app.getAccount(); final View header = drawer.getHeaderView(0); accountExpand = (ImageView) header.findViewById(R.id.account_expand); accountTitle = (TextView) header.findViewById(R.id.account_title); accountSubtitle = (TextView) header.findViewById(R.id.account_subtitle); accountWarning = (TextView) header.findViewById(R.id.account_warning); accountData = (LinearLayout) header.findViewById(R.id.account_data); View.OnClickListener l = new View.OnClickListener() { @Override public void onClick(View v) { toggleAccountSwitcher(); } }; accountData.setOnClickListener(l); //accountExpand.setOnClickListener(l); final SharedPreferences sp = PreferenceManager .getDefaultSharedPreferences(OpacActivity.this); boolean show_toggle_notice = !sp.contains("seen_drawer_toggle_notice"); header.findViewById(R.id.toggle_notice) .setVisibility(show_toggle_notice ? View.VISIBLE : View.GONE); header.findViewById(R.id.btToggleNotice).setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { sp.edit().putBoolean("seen_drawer_toggle_notice", true).commit(); header.findViewById(R.id.toggle_notice) .setVisibility(View.GONE); } }); accountsAdapter = new DrawerAccountsAdapter(this, accounts, app.getAccount()); drawer.setAccountsAdapter(accountsAdapter); accountsAdapter.setListener(this); updateAccountSwitcher(selectedAccount); } protected void toggleAccountSwitcher() { setAccountSwitcherVisible(!accountSwitcherVisible); } protected void setAccountSwitcherVisible(boolean accountSwitcherVisible) { if (accountSwitcherVisible == this.accountSwitcherVisible) return; this.accountSwitcherVisible = accountSwitcherVisible; drawer.setAccountsVisible(accountSwitcherVisible); if (Build.VERSION.SDK_INT >= 11) accountExpand.setActivated(accountSwitcherVisible); if (!accountSwitcherVisible) { fixNavigationSelection(); } } /** * Fix status bar flashing problem during transitions by excluding the status bar background * from transitions */ @TargetApi(21) private void fixStatusBarFlashing() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { getWindow().getEnterTransition().excludeTarget(android.R.id.statusBarBackground, true); getWindow().getReenterTransition() .excludeTarget(android.R.id.statusBarBackground, true); getWindow().getReturnTransition().excludeTarget(android.R.id.statusBarBackground, true); getWindow().getExitTransition().excludeTarget(android.R.id.statusBarBackground, true); } } protected abstract int getContentView(); protected void setupDrawer() { SharedPreferences sp = PreferenceManager .getDefaultSharedPreferences(this); drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); if (drawerLayout != null) { hasDrawer = true; drawerLayout.setStatusBarBackground(R.color.primary_red_dark); drawerLayout.setDrawerShadow(R.drawable.drawer_shadow, Gravity.LEFT); drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.drawer_open, R.string.drawer_close) { /** * Called when a drawer has settled in a completely closed * state. */ @Override public void onDrawerClosed(View view) { super.onDrawerClosed(view); getSupportActionBar().setTitle(title); } /** Called when a drawer has settled in a completely open state. */ @Override public void onDrawerOpened(View drawerView) { super.onDrawerOpened(drawerView); getSupportActionBar().setTitle( app.getResources().getString(R.string.app_name)); if (getCurrentFocus() != null) { InputMethodManager imm = (InputMethodManager) getSystemService( Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(getCurrentFocus() .getWindowToken(), 0); } } }; // Set the drawer toggle as the DrawerListener drawerLayout.setDrawerListener(new DrawerLayout.DrawerListener() { @Override public void onDrawerSlide(View drawerView, float slideOffset) { drawerToggle.onDrawerSlide(drawerView, slideOffset); } @Override public void onDrawerOpened(final View drawerView) { drawerToggle.onDrawerOpened(drawerView); } @Override public void onDrawerClosed(View drawerView) { drawerToggle.onDrawerClosed(drawerView); setAccountSwitcherVisible(false); } @Override public void onDrawerStateChanged(int newState) { drawerToggle.onDrawerStateChanged(newState); } }); getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setHomeButtonEnabled(true); drawer = (AccountSwitcherNavigationView) findViewById(R.id.navdrawer); drawer.setNavigationItemSelectedListener( new NavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(MenuItem item) { selectItem(item); return true; } }); if (!sp.getBoolean("version2.0.0-introduced", false) && app.getSlidingMenuEnabled()) { final Handler handler = new Handler(); // Just show the menu to explain that is there if people start // version 2 for the first time. // We need a handler because if we just put this in onCreate // nothing // happens. I don't have any idea, why. handler.postDelayed(new Runnable() { @Override public void run() { SharedPreferences sp = PreferenceManager .getDefaultSharedPreferences(OpacActivity.this); drawerLayout.openDrawer(drawer); sp.edit().putBoolean("version2.0.0-introduced", true) .commit(); } }, 500); } } } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); setSupportProgressBarIndeterminateVisibility(false); if (hasDrawer) { drawerToggle.syncState(); } } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); if (hasDrawer) { drawerToggle.onConfigurationChanged(newConfig); } } @Override protected void onResume() { setupDrawer(); setupAccountSwitcher(); fragment = getSupportFragmentManager().findFragmentById(R.id.content_frame); if (hasDrawer) { drawerToggle.syncState(); } setTwoPane(twoPane); super.onResume(); fixNavigationSelection(); Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { setFabVisible(fragment instanceof SearchFragment); } }, 300); } protected void fixNavigationSelection() { if (fragment == null) { return; } if (fragment instanceof SearchFragment) { drawer.setCheckedItem(R.id.nav_search); } else if (fragment instanceof AccountFragment) { drawer.setCheckedItem(R.id.nav_account); } else if (fragment instanceof StarredFragment) { drawer.setCheckedItem(R.id.nav_starred); } else if (fragment instanceof InfoFragment) { drawer.setCheckedItem(R.id.nav_info); } if (app.getLibrary() != null) { getSupportActionBar().setSubtitle(app.getLibrary().getDisplayName()); } } /** * Swaps fragments in the main content view */ protected void selectItem(MenuItem item) { try { setSupportProgressBarIndeterminateVisibility(false); } catch (Exception e) { } int itemId = item.getItemId(); if (selectItemById(itemId)) return; // Highlight the selected item, update the title, and close the drawer drawer.setCheckedItem(itemId); drawerLayout.closeDrawer(drawer); setAccountSwitcherVisible(false); return; } protected boolean selectItemById(int id) { Fragment previousFragment = fragment; // we cannot use a switch statement here because it breaks compatibility to the Plus Edition if (id == R.id.nav_search) { fragment = new SearchFragment(); setTwoPane(false); setFabVisible(true); } else if (id == R.id.nav_account) { fragment = new AccountFragment(); setTwoPane(false); setFabVisible(false); } else if (id == R.id.nav_starred) { fragment = new StarredFragment(); setTwoPane(true); setFabVisible(false); } else if (id == R.id.nav_info) { fragment = new InfoFragment(); setTwoPane(false); setFabVisible(false); } else if (id == R.id.nav_settings) { Intent intent = new Intent(this, MainPreferenceActivity.class); startActivity(intent); return true; } else if (id == R.id.nav_about) { Intent intent = new Intent(this, AboutActivity.class); startActivity(intent); return true; } setFabOnClickListener(id); // Insert the fragment by replacing any existing fragment FragmentManager fragmentManager = getSupportFragmentManager(); FragmentTransaction transaction = fragmentManager.beginTransaction() .replace(R.id.content_frame, fragment); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { fragment.setSharedElementEnterTransition( TransitionInflater.from(this).inflateTransition (android.R.transition.move)); fragment.setEnterTransition(TransitionInflater.from(this).inflateTransition (android.R.transition.fade)); } try { if (previousFragment instanceof SearchFragment && fragment instanceof AccountFragment && previousFragment.getView() != null) { transaction.addSharedElement(previousFragment.getView().findViewById(R.id .rlSimpleSearch), getString(R.string.transition_gray_box)); } else if (previousFragment instanceof AccountFragment && fragment instanceof SearchFragment && previousFragment.getView() != null) { transaction.addSharedElement(previousFragment.getView().findViewById(R.id .rlAccHeader), getString(R.string.transition_gray_box)); } } catch (NullPointerException e) { e.printStackTrace(); } transaction.commit(); selectedItemId = id; setTitle(getTitleForItem(id)); return false; } protected CharSequence getTitleForItem(int id) { return drawer.getMenu().findItem(id).getTitle(); } protected void selectItem(String tag) { int id; switch (tag) { case "search": id = R.id.nav_search; break; case "account": id = R.id.nav_account; break; case "starred": id = R.id.nav_starred; break; case "info": id = R.id.nav_info; break; default: return; } selectItemById(id); } protected void selectItem(int pos) { selectItem(drawer.getMenu().getItem(pos)); } protected void setFabOnClickListener(int id) { if (isTablet()) { if (id == R.id.nav_search) { fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ActivityOptionsCompat options = ActivityOptionsCompat.makeScaleUpAnimation (v, Math.round(v.getLeft()), Math.round(v.getTop()), v.getWidth(), v.getHeight()); ((SearchFragment) fragment).go(options.toBundle()); } }); } else { fab.setOnClickListener(null); } } } @Override public void setTitle(CharSequence title) { super.setTitle(title); this.title = title; } @Override protected void onStart() { super.onStart(); if (app.getLibrary() == null) { // Create new if (app.getAccount() != null) { try { InputStream stream = getAssets().open( OpacClient.ASSETS_BIBSDIR + "/" + app.getAccount().getLibrary() + ".json"); stream.close(); } catch (IOException e) { AccountDataSource data = new AccountDataSource(this); data.remove(app.getAccount()); List<Account> available_accounts = data.getAllAccounts(); if (available_accounts.size() > 0) { ((OpacClient) getApplication()) .setAccount(available_accounts.get(0).getId()); } new ReminderHelper(app).generateAlarms(); if (app.getLibrary() != null) { return; } } } app.addFirstAccount(this); } } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater mi = new MenuInflater(this); mi.inflate(R.menu.activity_opac, menu); return super.onCreateOptionsMenu(menu); } public void accountSelected(Account account) { updateAccountSwitcher(account); } protected void updateAccountSwitcher(Account account) { if (account == null) return; SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); int tolerance = Integer.parseInt(sp.getString("notification_warning", "3")); int expiring = aData.getExpiring(account, tolerance); accountTitle.setText(Utils.getAccountTitle(account, this)); accountSubtitle.setText(Utils.getAccountSubtitle(account, this)); if (expiring > 0) { accountWarning.setText(String.valueOf(expiring)); accountWarning.setVisibility(View.VISIBLE); } else { accountWarning.setVisibility(View.GONE); } accountsAdapter.setCurrentAccount(account); } public void selectaccount() { AlertDialog.Builder builder = new AlertDialog.Builder(this); // Get the layout inflater LayoutInflater inflater = getLayoutInflater(); View view = inflater.inflate(R.layout.dialog_simple_list, null); ListView lv = (ListView) view.findViewById(R.id.lvBibs); AccountDataSource data = new AccountDataSource(this); final List<Account> accounts = data.getAllAccounts(); AccountListAdapter adapter = new AccountListAdapter(this, accounts); lv.setAdapter(adapter); lv.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { app.setAccount(accounts.get(position).getId()); adialog.dismiss(); ((AccountSelectedListener) fragment).accountSelected(accounts .get(position)); } }); builder.setTitle(R.string.account_select) .setView(view) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { adialog.cancel(); } }) .setNeutralButton(R.string.accounts_edit, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { dialog.dismiss(); Intent intent = new Intent(OpacActivity.this, AccountListActivity.class); startActivity(intent); } }); adialog = builder.create(); adialog.show(); } public void selectaccount(long id) { app.setAccount(id); accountSelected(app.getAccount()); } @Override public boolean onOptionsItemSelected(MenuItem item) { // Pass the event to ActionBarDrawerToggle, if it returns // true, then it has handled the app icon touch event return hasDrawer && drawerToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item); } protected boolean isTablet() { return findViewById(R.id.content_frame_right) != null; } protected void setTwoPane(boolean active) { twoPane = active; if (isTablet()) { findViewById(R.id.content_frame_right).setVisibility( active ? View.VISIBLE : View.GONE); findViewById(R.id.twopane_wrapper).getLayoutParams().width = active ? LinearLayout.LayoutParams.MATCH_PARENT : getResources().getDimensionPixelSize(R.dimen.single_pane_max_width); } } protected void setFabVisible(boolean visible) { fabVisible = visible; if (isTablet()) { fab.setVisibility(visible ? View.VISIBLE : View.GONE); if (visible) { DisplayMetrics displayMetrics = getResources().getDisplayMetrics(); float density = getResources().getDisplayMetrics().density; float dpWidth = displayMetrics.widthPixels / density; RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams (Math.round(72 * density), Math.round(72 * density)); if (dpWidth >= 864) { params.addRule(RelativeLayout.BELOW, R.id.toolbar); params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); params.setMargins(0, Math.round(-36 * density), Math.round(36 * density), 0); ViewCompat.setElevation(fab, 4 * density); } else { params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); params.setMargins(0, 0, Math.round(36 * density), Math.round(36 * density)); ViewCompat.setElevation(fab, 12 * density); } fab.setLayoutParams(params); } } } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putBoolean("twoPane", twoPane); outState.putBoolean("fabVisible", fabVisible); outState.putInt("selectedItemId", selectedItemId); if (fragment != null) { getSupportFragmentManager().putFragment(outState, "fragment", fragment); } if (title != null) { outState.putCharSequence("title", title); } } @Override public void onAccountClicked(Account account) { selectaccount(account.getId()); drawerLayout.closeDrawer(drawer); setAccountSwitcherVisible(false); } @Override public void onAddAccountClicked() { Intent intent = new Intent(this, LibraryListActivity.class); startActivity(intent); } @Override public void onManageAccountsClicked() { Intent intent = new Intent(this, AccountListActivity.class); startActivity(intent); } public interface AccountSelectedListener { void accountSelected(Account account); } public class MetaAdapter<T extends Map.Entry<?, String>> extends ArrayAdapter<T> { private List<T> objects; private int spinneritem; public MetaAdapter(Context context, List<T> objects, int spinneritem) { super(context, R.layout.simple_spinner_item, objects); this.objects = objects; this.spinneritem = spinneritem; } @Override public View getDropDownView(int position, View contentView, ViewGroup viewGroup) { View view; if (objects.get(position) == null) { LayoutInflater layoutInflater = (LayoutInflater) getContext() .getSystemService(Context.LAYOUT_INFLATER_SERVICE); view = layoutInflater .inflate(R.layout.simple_spinner_dropdown_item, viewGroup, false); return view; } T item = objects.get(position); if (contentView == null) { LayoutInflater layoutInflater = (LayoutInflater) getContext() .getSystemService(Context.LAYOUT_INFLATER_SERVICE); view = layoutInflater .inflate(R.layout.simple_spinner_dropdown_item, viewGroup, false); } else { view = contentView; } TextView tvText = (TextView) view.findViewById(android.R.id.text1); tvText.setText(item.getValue()); return view; } @Override public View getView(int position, View contentView, ViewGroup viewGroup) { View view; if (objects.get(position) == null) { LayoutInflater layoutInflater = (LayoutInflater) getContext() .getSystemService(Context.LAYOUT_INFLATER_SERVICE); view = layoutInflater.inflate(spinneritem, viewGroup, false); return view; } T item = objects.get(position); if (contentView == null) { LayoutInflater layoutInflater = (LayoutInflater) getContext() .getSystemService(Context.LAYOUT_INFLATER_SERVICE); view = layoutInflater.inflate(spinneritem, viewGroup, false); } else { view = contentView; } TextView tvText = (TextView) view.findViewById(android.R.id.text1); tvText.setText(item.getValue()); return view; } } }