package org.edx.mobile.base; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Configuration; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBarDrawerToggle; import android.util.TypedValue; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.TextView; import com.google.inject.Inject; import org.edx.mobile.R; import org.edx.mobile.core.IEdxEnvironment; import org.edx.mobile.event.LogoutEvent; import org.edx.mobile.event.NetworkConnectivityChangeEvent; import org.edx.mobile.interfaces.NetworkObserver; import org.edx.mobile.interfaces.NetworkSubject; import org.edx.mobile.interfaces.OnActivityResultListener; import org.edx.mobile.logger.Logger; import org.edx.mobile.util.NetworkUtil; import org.edx.mobile.util.ViewAnimationUtil; import org.edx.mobile.view.ICommonUI; import org.edx.mobile.view.NavigationFragment; import org.edx.mobile.view.dialog.AlertDialogFragment; import java.util.ArrayList; import java.util.List; import de.greenrobot.event.EventBus; public abstract class BaseFragmentActivity extends BaseAppActivity implements NetworkSubject, ICommonUI, OnActivityResultListener { private MenuItem offlineMenuItem; protected ActionBarDrawerToggle mDrawerToggle; //FIXME - we should not set a separate flag to indicate the status of UI component private boolean isUiOnline = true; private boolean isConnectedToWifi = false; private boolean isActivityStarted = false; @Inject protected IEdxEnvironment environment; private List<NetworkObserver> networkObservers = new ArrayList<>(); public void registerNetworkObserver(NetworkObserver observer) { if (observer != null && !networkObservers.contains(observer)) { networkObservers.add(observer); } } public void unregisterNetworkObserver(NetworkObserver observer) { if (observer != null && networkObservers.contains(observer)) { networkObservers.remove(observer); } } @Override public void notifyNetworkDisconnect() { for (NetworkObserver o : networkObservers) { o.onOffline(); } } @Override public void notifyNetworkConnect() { for (NetworkObserver o : networkObservers) { o.onOnline(); } } private final Handler handler = new Handler(); protected final Logger logger = new Logger(getClass().getName()); @Override protected void onCreate(Bundle arg0) { super.onCreate(arg0); updateActionBarShadow(); ActionBar bar = getSupportActionBar(); if (bar != null) { bar.setDisplayShowHomeEnabled(true); bar.setDisplayHomeAsUpEnabled(true); bar.setIcon(android.R.color.transparent); } } @Override protected void onStart() { super.onStart(); isActivityStarted = true; } @Override protected void onResume() { super.onResume(); EventBus.getDefault().registerSticky(this); DrawerLayout mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); if (mDrawerLayout != null) { Fragment frag = getSupportFragmentManager().findFragmentByTag("NavigationFragment"); if (frag == null) { getSupportFragmentManager().beginTransaction().replace( R.id.slider_menu, new NavigationFragment(), "NavigationFragment").commit(); } } } private void updateActionBarShadow() { //Check for JellyBeans version if (Build.VERSION.SDK_INT == 18) { // Get the content view View contentView = findViewById(android.R.id.content); // Make sure it's a valid instance of a FrameLayout if (contentView instanceof FrameLayout) { TypedValue tv = new TypedValue(); // Get the windowContentOverlay value of the current theme if (getTheme().resolveAttribute( android.R.attr.windowContentOverlay, tv, true)) { // If it's a valid resource, set it as the foreground drawable // for the content view if (tv.resourceId != 0) { ((FrameLayout) contentView).setForeground( getResources().getDrawable(tv.resourceId)); } } } } } @Override protected void onStop() { super.onStop(); isActivityStarted = false; } @Override protected void onPause() { EventBus.getDefault().unregister(this); super.onPause(); } @Override protected void onDestroy() { super.onDestroy(); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { try { // App crashes on a few devices (mostly 4.0.+) on super method call // This is a workaround to avoid app crash, app still works even if Exception occurs super.onRestoreInstanceState(savedInstanceState); } catch (Exception ex) { logger.error(ex); } } @Override public void onBackPressed() { DrawerLayout mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); if (mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START)) { mDrawerLayout.closeDrawers(); } else { super.onBackPressed(); } } //this is configure the Navigation Drawer of the application protected void configureDrawer() { DrawerLayout mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); if (mDrawerLayout != null) { getSupportFragmentManager().beginTransaction() .replace(R.id.slider_menu, new NavigationFragment(), "NavigationFragment").commit(); mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.string.label_open_navigation_menu, R.string.label_close_navigation_menu) { public void onDrawerClosed(View view) { super.onDrawerClosed(view); invalidateOptionsMenu(); } public void onDrawerOpened(View drawerView) { super.onDrawerOpened(drawerView); Fragment frag = getSupportFragmentManager(). findFragmentByTag("NavigationFragment"); if (frag == null) { getSupportFragmentManager().beginTransaction() .replace(R.id.slider_menu, new NavigationFragment(), "NavigationFragment").commit(); } invalidateOptionsMenu(); } @Override public void onDrawerSlide(View drawerView, float slideOffset) { super.onDrawerSlide(drawerView, 0); // this disables the animation } }; mDrawerLayout.setDrawerListener(mDrawerToggle); } } /** * Call this function if you do not want to allow * opening/showing the drawer(Navigation Fragment) on swiping left to right */ protected void blockDrawerFromOpening() { DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); if (drawerLayout != null) { drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); } } //Closing the Navigation Drawer public void closeDrawer() { DrawerLayout mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); if (mDrawerLayout != null) { mDrawerLayout.closeDrawers(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { return super.onCreateOptionsMenu(menu) | createOptionsMenu(menu); } /** * Initialize the options menu. This is called from * {@link #onCreateOptionsMenu(Menu)}, so that subclasses can override * the base menu implementation while still calling back to the system * implementation. The selection handling for menu items defined here * should be performed in {@link #handleOptionsItemSelected(MenuItem)}, * and any these methods should both be overriden together. * * @param menu The options menu. * @return Return true if the menu should be displayed. */ protected boolean createOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); offlineMenuItem = menu.findItem(R.id.offline); offlineMenuItem.setVisible(!NetworkUtil.isConnected(this)); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Toggle navigation drawer when the app icon or title on the action bar // is clicked if (mDrawerToggle != null && mDrawerToggle.onOptionsItemSelected(item)) { return true; } if (handleOptionsItemSelected(item)) { return true; } // Handle action bar buttons click switch (item.getItemId()) { case android.R.id.home: finish(); return true; } return super.onOptionsItemSelected(item); } /** * Handle options menu item selection. This is called from * {@link #onOptionsItemSelected(MenuItem)} to provide a menu * selection handler that can be overridden by subclass that override * {@link #createOptionsMenu(Menu)}, and should only be used to handle * selections of the menu items that are initialized from that method. * * @param item The menu item that was selected. * @return boolean Return false to allow normal menu processing to * proceed, true to consume it here. */ protected boolean handleOptionsItemSelected(MenuItem item) { return false; } public void setActionBarVisible(boolean visible) { try { ActionBar bar = getSupportActionBar(); if (bar != null) { if (visible) bar.show(); else bar.hide(); } } catch (Exception ex) { logger.error(ex); } } /** * When using the ActionBarDrawerToggle, you must call it during * onPostCreate() and onConfigurationChanged()... */ @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); // Sync the toggle state after onRestoreInstanceState has occurred. if (mDrawerToggle != null) { mDrawerToggle.syncState(); } } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // Pass any configuration change to the drawer toggles if (mDrawerToggle != null) { mDrawerToggle.onConfigurationChanged(newConfig); } } public void animateLayouts(View view) { if (view == null) { logger.warn("Null view cannot be animated!"); return; } ViewAnimationUtil.showMessageBar(view); } public void stopAnimation(View view) { if (view != null) { ViewAnimationUtil.stopAnimation(view); } } /** * Animate / show the download started message * * @param message - Message to display on the Download Panel * @return boolean - Returns true if message shown, false otherwise. */ public boolean showInfoMessage(String message) { TextView infoMessageTv = (TextView) findViewById(R.id.flying_message); if (infoMessageTv != null) { infoMessageTv.setText(message); animateLayouts(infoMessageTv); return true; } else { logger.warn("TextView not available, so couldn't show flying message"); } return false; } /** * Hides the info message view if its visible with animation * * @return <code>true<code/> if the view was hidden successfully otherwise <code>false</code> */ public boolean hideInfoMessage() { View messageView = findViewById(R.id.flying_message); if (messageView == null) { logger.warn("Message view not available, so couldn't hide flying message"); return false; } ViewAnimationUtil.hideMessageBar(messageView); return true; } /** * Call this method to inform user about going offline */ public void showOfflineAccessMessage() { try { animateLayouts(findViewById(R.id.offline_access_panel)); } catch (Exception e) { logger.error(e); } } public boolean isActivityStarted() { return isActivityStarted; } /** * Returns true if current orientation is LANDSCAPE, false otherwise. */ protected boolean isLandscape() { return (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE); } /** * callback from EventBus * * @param event */ @SuppressWarnings("unused") public void onEvent(LogoutEvent event) { finish(); } /** * callback from EventBus * * @param event */ @SuppressWarnings("unused") public void onEvent(NetworkConnectivityChangeEvent event) { logger.debug("network state changed"); if (NetworkUtil.isConnected(this)) { if (!isUiOnline) { // only notify if previous state was NOT same isUiOnline = true; handler.post(new Runnable() { public void run() { onOnline(); notifyNetworkConnect(); } }); } if (NetworkUtil.isConnectedWifi(this)) { if (!isConnectedToWifi) { isConnectedToWifi = true; handler.post(new Runnable() { @Override public void run() { onConnectedToWifi(); } }); } } else if (NetworkUtil.isConnectedMobile(this)) { if (isConnectedToWifi) { isConnectedToWifi = false; handler.post(new Runnable() { @Override public void run() { onConnectedToMobile(); } }); } } } else { if (isUiOnline) { isUiOnline = false; handler.post(new Runnable() { public void run() { onOffline(); notifyNetworkDisconnect(); } }); } } } public boolean showErrorMessage(String header, String message) { return showErrorMessage(header, message, true); } public boolean showErrorMessage(String header, String message, boolean isPersistent) { LinearLayout error_layout = (LinearLayout) findViewById(R.id.error_layout); if (error_layout == null) { logger.warn("Error Layout not available, so couldn't show flying message"); return false; } TextView errorHeader = (TextView) findViewById(R.id.error_header); TextView errorMessageView = (TextView) findViewById(R.id.error_message); if (header == null || header.isEmpty()) { errorHeader.setVisibility(View.GONE); } else { errorHeader.setVisibility(View.VISIBLE); errorHeader.setText(header); } if (message != null) { errorMessageView.setText(message); } ViewAnimationUtil.showMessageBar(error_layout, isPersistent); return true; } /** * Sub-classes may override this method to handle connected state. */ protected void onOnline() { if (offlineMenuItem != null) { offlineMenuItem.setVisible(false); } logger.debug("You are now online"); } /** * Sub-classes may override this method to handle disconnected state. */ protected void onOffline() { if (offlineMenuItem != null) { offlineMenuItem.setVisible(true); } logger.debug("You are now offline"); } /** * Gets called whenever network state is changed and device is now connected to mobile data. * Sub-classes may override this method to handle when mobile data is connected. * This method is called after {@link #onOnline()} method. */ protected void onConnectedToMobile() { } /** * Gets called whenever network state is changed and device is now connected to wifi. * Sub-classes may override this method to handle when wifi is connected. * This method is called after {@link #onOnline()} method. */ protected void onConnectedToWifi() { } /** * Blocks touch event for this activity. * Use {@link #unblockTouch()} method to unblock and activate touch events. */ protected void blockTouch() { getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); } /** * Unblocks touch event for this activity. * This might should be called to unblock touch events that were blocked by {@link #blockTouch()} method. */ protected void unblockTouch() { getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE); } protected void hideSoftKeypad() { getWindow().setSoftInputMode( WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); } public void showAlertDialog(@Nullable String title, @NonNull String message) { showAlertDialog(title, message, null); } public void showAlertDialog(@Nullable String title, @NonNull String message, @Nullable DialogInterface.OnClickListener onPositiveClick) { AlertDialogFragment.newInstance(title, message, onPositiveClick).show(getSupportFragmentManager(), null); } @Override public boolean tryToSetUIInteraction(boolean enable) { return false; } /** * {@inheritDoc} * * <p>To conform with the {@link OnActivityResultListener} interface this function has been * implemented emptily, making it publicly accessible.</p> */ @Override public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); } }