/* * Copyright 2016 Gleb Godonoga. * * 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.andrada.sitracker.ui; import android.app.Activity; import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.app.backup.BackupManager; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Color; import android.net.Uri; import android.os.Bundle; import android.support.design.widget.AppBarLayout; import android.support.design.widget.Snackbar; import android.support.v4.app.NavUtils; import android.support.v4.app.TaskStackBuilder; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.style.ForegroundColorSpan; import android.view.MenuItem; import android.view.ViewGroup; import com.andrada.sitracker.BuildConfig; import com.andrada.sitracker.Constants; import com.andrada.sitracker.R; import com.andrada.sitracker.analytics.AnalyticsManager; import com.andrada.sitracker.analytics.MarkAsReadEvent; import com.andrada.sitracker.contracts.OnBackAware; import com.andrada.sitracker.events.AuthorMarkedAsReadEvent; import com.andrada.sitracker.events.AuthorsExported; import com.andrada.sitracker.events.PublicationMarkedAsReadEvent; import com.andrada.sitracker.ui.fragment.AboutDialog; import com.andrada.sitracker.ui.fragment.AuthorsFragment; import com.andrada.sitracker.ui.fragment.AuthorsFragment_; import com.andrada.sitracker.ui.fragment.NewPublicationsFragment; import com.andrada.sitracker.ui.fragment.NewPublicationsFragment_; import com.andrada.sitracker.util.ActivityFragmentNavigator; import com.andrada.sitracker.util.NavDrawerManager; import com.andrada.sitracker.util.PlayServicesUtils; import com.andrada.sitracker.util.UIUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Timer; import java.util.TimerTask; import static com.andrada.sitracker.util.LogUtils.makeLogTag; /** * A base activity that handles common functionality in the app. */ public abstract class BaseActivity extends AppCompatActivity implements NavDrawerManager.NavDrawerListener { private static final String TAG = makeLogTag(BaseActivity.class); private static final long BACK_UP_DELAY = 30000L; private NavDrawerManager mDrawerManager; // Primary toolbar and drawer toggle private Toolbar mActionBarToolbar; private AppBarLayout appBarLayout; private ExportAuthorsController mExportCtrl; protected Fragment currentFragment; private ViewGroup cabContainer; private TimerTask backUpTask; @NotNull private final Timer backUpTimer = new Timer(); /** * Converts an intent into a {@link Bundle} suitable for use as fragment arguments. */ @NotNull public static Bundle intentToFragmentArguments(@Nullable Intent intent) { Bundle arguments = new Bundle(); if (intent == null) { return arguments; } final Uri data = intent.getData(); if (data != null) { arguments.putParcelable("_uri", data); } final Bundle extras = intent.getExtras(); if (extras != null) { arguments.putAll(extras); } return arguments; } /** * Converts a fragment arguments bundle into an intent. */ @NotNull public static Intent fragmentArgumentsToIntent(@Nullable Bundle arguments) { Intent intent = new Intent(); if (arguments == null) { return intent; } final Uri data = arguments.getParcelable("_uri"); if (data != null) { intent.setData(data); } intent.putExtras(arguments); intent.removeExtra("_uri"); return intent; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Enable or disable each Activity depending on the form factor. This is necessary // because this app uses many implicit intents where we don't name the exact Activity // in the Intent, so there should only be one enabled Activity that handles each // Intent in the app. UIUtils.enableDisableActivitiesByFormFactor(this); ActionBar ab = getSupportActionBar(); if (ab != null) { ab.setDisplayHomeAsUpEnabled(true); } mExportCtrl = new ExportAuthorsController(this); } @Override protected void onResume() { super.onResume(); if (!BuildConfig.DEBUG) { // Verifies the proper version of Google Play Services exists on the device. PlayServicesUtils.checkGooglePlaySevices(this); } } protected void afterViews() { mDrawerManager = new NavDrawerManager(this); } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); if (mDrawerManager == null) { afterViews(); } if (appBarLayout == null) { appBarLayout = (AppBarLayout) findViewById(R.id.appbar_layout); } if (cabContainer == null) { cabContainer = (ViewGroup) findViewById(R.id.si_cab_container); } } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); switch (id) { case android.R.id.home: getDrawerManager().openNavDrawer(); return true; } //Handle default options return super.onOptionsItemSelected(item); } @Override public void setContentView(int layoutResID) { super.setContentView(layoutResID); getActionBarToolbar(); } public void trySetToolbarScrollable(boolean scrollable) { if (cabContainer != null) { AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) cabContainer.getLayoutParams(); if (scrollable) { params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS | AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL); } else { if (appBarLayout != null) { appBarLayout.setExpanded(true, true); } params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP); } cabContainer.setLayoutParams(params); } } @Override public void goToNavDrawerItem(int item) { currentFragment = null; switch (item) { case R.id.navigation_item_my_authors: AuthorsFragment authFrag = AuthorsFragment_.builder().build(); if (appBarLayout != null) { appBarLayout.setExpanded(true, false); } currentFragment = authFrag; ActivityFragmentNavigator.switchMainFragmentInMainActivity(this, authFrag); mDrawerManager.tryFadeInMainContent(); break; case R.id.navigation_item_new_pubs: NewPublicationsFragment newPubsFrag = NewPublicationsFragment_.builder().build(); if (appBarLayout != null) { appBarLayout.setExpanded(true, false); } currentFragment = newPubsFrag; ActivityFragmentNavigator.switchMainFragmentInMainActivity(this, newPubsFrag); mDrawerManager.tryFadeInMainContent(); break; case R.id.navigation_item_export: mExportCtrl.showDialog(); break; case R.id.navigation_item_import: ImportAuthorsActivity_.intent(this).start(); break; case R.id.navigation_item_settings: SettingsActivity_.intent(this).start(); break; case R.id.navigation_item_about: FragmentManager fm = this.getFragmentManager(); FragmentTransaction ft = fm.beginTransaction(); Fragment prev = fm.findFragmentByTag(AboutDialog.FRAGMENT_TAG); if (prev != null) { ft.remove(prev); } ft.addToBackStack(null); new AboutDialog().show(ft, AboutDialog.FRAGMENT_TAG); break; } } public Toolbar getActionBarToolbar() { if (mActionBarToolbar == null) { mActionBarToolbar = (Toolbar) findViewById(R.id.toolbar_actionbar); if (mActionBarToolbar != null) { setSupportActionBar(mActionBarToolbar); } } return mActionBarToolbar; } public NavDrawerManager getDrawerManager() { return mDrawerManager; } @Override public void onBackPressed() { if (mDrawerManager != null && mDrawerManager.isNavDrawerOpen()) { mDrawerManager.closeNavDrawer(); return; } else if (currentFragment != null && currentFragment instanceof OnBackAware) { boolean handled = ((OnBackAware) currentFragment).onBackPressed(); if (handled) { return; } } else if (getFragmentManager().getBackStackEntryCount() > 0) { getFragmentManager().popBackStack(); return; } super.onBackPressed(); } public void onEvent(@NotNull AuthorsExported event) { String message = event.getMessage(); SpannableStringBuilder snackbarText = new SpannableStringBuilder(); if (message.length() == 0) { //This is success snackbarText.append(getResources().getString(R.string.author_export_success_crouton_message)); } else { snackbarText.append(message); snackbarText.setSpan(new ForegroundColorSpan(Color.RED), 0, snackbarText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } Snackbar.make(findViewById(R.id.drawer_layout), snackbarText, Snackbar.LENGTH_LONG).show(); } public void onEvent(AuthorMarkedAsReadEvent event) { this.scheduleBackup(); } public void onEvent(PublicationMarkedAsReadEvent event) { AnalyticsManager.getInstance().logEvent(new MarkAsReadEvent()); this.scheduleBackup(); } private void scheduleBackup() { if (this.backUpTask != null) { this.backUpTask.cancel(); } this.backUpTask = new TimerTask() { @Override public void run() { BackupManager bm = new BackupManager(getApplicationContext()); bm.dataChanged(); } }; backUpTimer.schedule(this.backUpTask, BACK_UP_DELAY); } /** * This utility method handles Up navigation intents by searching for a parent activity and * navigating there if defined. When using this for an activity make sure to define both the * native parentActivity as well as the AppCompat one when supporting API levels less than 16. * when the activity has a single parent activity. If the activity doesn't have a single parent * activity then don't define one and this method will use back button functionality. If "Up" * functionality is still desired for activities without parents then use * {@code syntheticParentActivity} to define one dynamically. * <p/> * Note: Up navigation intents are represented by a back arrow in the top left of the Toolbar * in Material Design guidelines. * * @param currentActivity Activity in use when navigate Up action occurred. * @param syntheticParentActivity Parent activity to use when one is not already configured. */ public static void navigateUpOrBack(Activity currentActivity, Class<? extends Activity> syntheticParentActivity) { // Retrieve parent activity from AndroidManifest. Intent intent = NavUtils.getParentActivityIntent(currentActivity); // Synthesize the parent activity when a natural one doesn't exist. if (intent == null && syntheticParentActivity != null) { try { intent = NavUtils.getParentActivityIntent(currentActivity, syntheticParentActivity); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } } if (intent == null) { // No parent defined in manifest. This indicates the activity may be used by // in multiple flows throughout the app and doesn't have a strict parent. In // this case the navigation up button should act in the same manner as the // back button. This will result in users being forwarded back to other // applications if currentActivity was invoked from another application. currentActivity.onBackPressed(); } else { if (NavUtils.shouldUpRecreateTask(currentActivity, intent)) { // Need to synthesize a backstack since currentActivity was probably invoked by a // different app. The preserves the "Up" functionality within the app according to // the activity hierarchy defined in AndroidManifest.xml via parentActivity // attributes. TaskStackBuilder builder = TaskStackBuilder.create(currentActivity); builder.addNextIntentWithParentStack(intent); builder.startActivities(); } else { // Navigate normally to the manifest defined "Up" activity. NavUtils.navigateUpTo(currentActivity, intent); } } } }