package com.battlelancer.seriesguide.ui; import android.Manifest; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.Handler; import android.support.v4.app.NavUtils; import android.support.v4.app.TaskStackBuilder; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.text.format.DateUtils; import android.view.MenuItem; import com.battlelancer.seriesguide.R; import com.battlelancer.seriesguide.settings.AdvancedSettings; import com.battlelancer.seriesguide.settings.BackupSettings; import com.battlelancer.seriesguide.sync.SgSyncAdapter; import com.battlelancer.seriesguide.util.AddShowTask; import com.battlelancer.seriesguide.util.DBUtils; import com.battlelancer.seriesguide.util.TaskManager; import com.battlelancer.seriesguide.util.TraktTask; import com.battlelancer.seriesguide.widgets.FirstRunView; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; /** * Provides some common functionality across all activities like setting the theme, navigation * shortcuts and triggering AutoUpdates and AutoBackups. <p> Also registers with {@link * EventBus#getDefault()} by default to handle various common events, see {@link * #registerEventBus()} and {@link #unregisterEventBus()} to prevent that. */ public abstract class BaseActivity extends AppCompatActivity { private Handler mHandler; private Runnable mUpdateShowRunnable; @Override protected void onCreate(Bundle arg0) { setCustomTheme(); super.onCreate(arg0); } protected void setCustomTheme() { // set a theme based on user preference setTheme(SeriesGuidePreferences.THEME); } /** * Implementers must call this in {@link #onCreate} after {@link #setContentView} if they want * to use the action bar. <p>If setting a title, might also want to supply a title to the * activity ({@link #setTitle(CharSequence)}) for better accessibility. */ protected void setupActionBar() { Toolbar toolbar = (Toolbar) findViewById(R.id.sgToolbar); setSupportActionBar(toolbar); } @Override protected void onStart() { super.onStart(); // make sync interfering with backup task less likely if (!onAutoBackup()) { SgSyncAdapter.requestSyncIfTime(this); } registerEventBus(); } /** * Override this to avoid registering with {@link EventBus#getDefault()} in {@link #onStart()}. * * <p> See {@link #unregisterEventBus()} as well. */ public void registerEventBus() { EventBus.getDefault().register(this); } @Override protected void onStop() { super.onStop(); if (mHandler != null && mUpdateShowRunnable != null) { mHandler.removeCallbacks(mUpdateShowRunnable); } unregisterEventBus(); } /** * Override this to avoid unregistering from {@link EventBus#getDefault()} in {@link * #onStop()}. * * <p> See {@link #registerEventBus()} as well. */ public void unregisterEventBus() { EventBus.getDefault().unregister(this); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: Intent upIntent = NavUtils.getParentActivityIntent(this); if (NavUtils.shouldUpRecreateTask(this, upIntent)) { // This activity is NOT part of this app's task, so create a new task // when navigating up, with a synthesized back stack. TaskStackBuilder.create(this) // Add all of this activity's parents to the back stack .addNextIntentWithParentStack(upIntent) // Navigate up to the closest parent .startActivities(); } else { // This activity is part of this app's task, so simply // navigate up to the logical parent activity. NavUtils.navigateUpTo(this, upIntent); } return true; } return super.onOptionsItemSelected(item); } @Subscribe public void onEvent(AddShowTask.OnShowAddedEvent event) { // display status toast about adding shows event.handle(this); } @Subscribe public void onEvent(TraktTask.TraktActionCompleteEvent event) { // display status toast about trakt action event.handle(this); } @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(DBUtils.DatabaseErrorEvent event) { event.handle(this); } /** * Periodically do an automatic backup of the show database. */ private boolean onAutoBackup() { if (!AdvancedSettings.isAutoBackupEnabled(this)) { return false; } // only continue if we are allowed to write to external storage if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { // only show warning if the user is done with first run if (FirstRunView.hasSeenFirstRunFragment(this)) { onShowAutoBackupPermissionWarning(); } return false; } long now = System.currentTimeMillis(); long previousBackupTime = AdvancedSettings.getLastAutoBackupTime(this); final boolean isTime = (now - previousBackupTime) > 7 * DateUtils.DAY_IN_MILLIS; if (isTime) { // if custom files are enabled, make sure they are configured // note: backup task clears backup file setting if there was an issue with the file if (!BackupSettings.isUseAutoBackupDefaultFiles(this) && BackupSettings.isMissingAutoBackupFile(this)) { onShowAutoBackupMissingFilesWarning(); return false; } TaskManager.getInstance(this).tryBackupTask(); return true; } else { return false; } } /** * Implementers may choose to show a warning that auto backup can not complete because not all * custom backup files are configured. */ protected void onShowAutoBackupMissingFilesWarning() { // do nothing } /** * Implementers may choose to show a warning that auto backup can not complete because of * missing permissions. */ protected void onShowAutoBackupPermissionWarning() { // do nothing } /** * Schedule an update for the given show. Might not run if this show was just updated. Execution * is also delayed so it won't reduce UI setup performance (= you can run this in {@link * #onCreate(android.os.Bundle)}). * * <p> See {@link com.battlelancer.seriesguide.sync.SgSyncAdapter#requestSyncIfTime(android.content.Context, * int)}. */ protected void updateShowDelayed(final int showTvdbId) { if (mHandler == null) { mHandler = new Handler(); } // delay sync request to avoid slowing down UI final Context context = getApplicationContext(); mUpdateShowRunnable = new Runnable() { @Override public void run() { SgSyncAdapter.requestSyncIfTime(context, showTvdbId); } }; mHandler.postDelayed(mUpdateShowRunnable, DateUtils.SECOND_IN_MILLIS); } }