org.kegbot.app.PourInProgressActivity.java Source code

Java tutorial

Introduction

Here is the source code for org.kegbot.app.PourInProgressActivity.java

Source

/*
 * Copyright 2014 Bevbot LLC <info@bevbot.com>
 *
 * This file is part of the Kegtab package from the Kegbot project. For
 * more information on Kegtab or Kegbot, see <http://kegbot.org/>.
 *
 * Kegtab is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free
 * Software Foundation, version 2.
 *
 * Kegtab 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 General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with Kegtab. If not, see <http://www.gnu.org/licenses/>.
 */
package org.kegbot.app;

import android.app.ActionBar;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.v13.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.util.Pair;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.ViewFlipper;

import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.squareup.otto.Subscribe;

import org.kegbot.app.camera.CameraFragment;
import org.kegbot.app.config.AppConfiguration;
import org.kegbot.app.event.FlowUpdateEvent;
import org.kegbot.app.event.PictureDiscardedEvent;
import org.kegbot.app.event.PictureTakenEvent;
import org.kegbot.app.util.ImageDownloader;
import org.kegbot.app.util.Units;
import org.kegbot.app.util.Utils;
import org.kegbot.app.view.BadgeView;
import org.kegbot.backend.Backend;
import org.kegbot.backend.LocalBackend;
import org.kegbot.core.AuthenticationManager;
import org.kegbot.core.Flow;
import org.kegbot.core.FlowManager;
import org.kegbot.core.KegbotCore;
import org.kegbot.proto.Models;
import org.kegbot.proto.Models.KegTap;
import org.kegbot.proto.Models.User;
import org.kegbot.proto.Models.ThermoSensor;

import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import javax.annotation.concurrent.GuardedBy;

import butterknife.ButterKnife;

/**
 * Activity shown while a pour is in progress.
 *
 * @author mike wakerly (opensource@hoho.com)
 */
public class PourInProgressActivity extends CoreActivity {

    private static final String TAG = PourInProgressActivity.class.getSimpleName();

    private static final boolean DEBUG = false;

    /** Delay after a flow has ended, during which a dialog is show. */
    private static final long FLOW_FINISH_DELAY_MILLIS = TimeUnit.SECONDS.toMillis(1);

    /** Minimum idle seconds before a flow is considered idle for display purposes. */
    private static final long IDLE_SCROLL_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(3);

    /** Request code. */
    private static final int REQUEST_AUTH_DRINKER = 100;
    private static final String EXTRA_FLOW_ID = "flow_id";

    private KegbotCore mCore;
    private FlowManager mFlowManager;
    private AppConfiguration mConfig;
    private ImageDownloader mImageDownloader;

    private CameraFragment mCameraFragment;
    private boolean mShowCamera;

    private final Handler mHandler = new Handler(Looper.getMainLooper());

    private PouringTapAdapter mPouringTapAdapter;

    private ViewFlipper mControlsFlipper;
    private Button mClaimPourButton;
    private TextView mDrinkerName;
    private ImageView mDrinkerImage;
    private TextView mShoutText;
    private Button mDoneButton;
    private ViewPager mTapPager;
    private Button mAddButton;
    private Button mSubtractButton;

    //for backgrounds
    private ImageView mImageView0;
    private LinearLayout mImageView1;

    private final DialogFragment mIdleDialogFragment = new DialogFragment() {
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            AlertDialog.Builder builder = new AlertDialog.Builder(PourInProgressActivity.this);
            builder.setMessage("Hey, are you still pouring?").setCancelable(false)
                    .setPositiveButton("Continue Pouring", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            for (final Flow flow : mFlowManager.getAllActiveFlows()) {
                                flow.pokeActivity();
                            }
                            dialog.cancel();
                        }
                    }).setNegativeButton("Done Pouring", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            for (final Flow flow : mFlowManager.getAllActiveFlows()) {
                                mFlowManager.endFlow(flow);
                            }
                            cancelIdleWarning();
                        }
                    });
            return builder.create();
        }
    };
    private boolean mIdleDialogShowing = false;

    private final Object mTapsLock = new Object();

    @GuardedBy("mTapsLock")
    private final List<KegTap> mTapList = Lists.newArrayList();

    private Set<Flow> mActiveFlows = Sets.newLinkedHashSet();

    @Subscribe
    public void onPictureTakenEvent(PictureTakenEvent event) {
        final String filename = event.getFilename();
        Log.d(TAG, "Got photo: " + filename);
        for (final Flow flow : mFlowManager.getAllActiveFlows()) {
            Log.d(TAG, "  - attached to flow: " + flow);
            flow.setImage(filename);
        }
    }

    @Subscribe
    public void onPictureDiscardedEvent(PictureDiscardedEvent event) {
        final String filename = event.getFilename();
        Log.d(TAG, "Discarded photo: " + filename);
        for (final Flow flow : mFlowManager.getAllActiveFlows()) {
            if (filename.equals(flow.getImagePath())) {
                flow.removeImage();
            }
        }
    }

    @Subscribe
    public void onFlowUpdateEvent(FlowUpdateEvent event) {
        refreshFlows();
    }

    private final Runnable REFRESH_FLOWS_RUNNABLE = new Runnable() {
        @Override
        public void run() {
            refreshFlows();
            mHandler.postDelayed(this, 1000);
        }
    };

    private final ViewPager.OnPageChangeListener mPageChangeListener = new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageSelected(int position) {
            updateControlsForFlow(getCurrentlyFocusedFlow());
        }

        @Override
        public void onPageScrolled(int arg0, float arg1, int arg2) {
        }

        @Override
        public void onPageScrollStateChanged(int arg0) {
        }
    };

    public class PouringTapAdapter extends FragmentStatePagerAdapter {

        public PouringTapAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            final KegTap tap;

            synchronized (mTapsLock) {
                tap = mTapList.get(position);
            }

            final PourStatusFragment frag = PourStatusFragment.forTap(tap);

            final Flow flow = mFlowManager.getFlowForTap(tap);
            if (flow != null) {
                frag.updateWithFlow(flow);
            }
            return frag;
        }

        @Override
        public int getItemPosition(Object object) {
            int index;
            synchronized (mTapsLock) {
                index = mTapList.indexOf(object);
            }
            if (index >= 0) {
                return index;
            }
            return POSITION_NONE;
        }

        @Override
        public int getCount() {
            synchronized (mTapsLock) {
                return mTapList.size();
            }
        }

    }

    public static class PourFinishProgressDialog extends DialogFragment {
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            ProgressDialog dialog = new ProgressDialog(getActivity());
            dialog.setIndeterminate(true);
            dialog.setCancelable(false);
            dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
            dialog.setMessage("Please wait..");
            dialog.setTitle("Saving drink");
            return dialog;
        }
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "onCreate()");

        mCore = KegbotCore.getInstance(this);
        mFlowManager = mCore.getFlowManager();
        mConfig = mCore.getConfiguration();
        mImageDownloader = mCore.getImageDownloader();

        overridePendingTransition(R.anim.image_fade_in, R.anim.image_fade_in);
        final ActionBar actionBar = getActionBar();
        //    if (actionBar != null) {
        //      actionBar.hide();
        //    }
        if (mConfig.getShowActionBar()) {
            actionBar.show();
        } else {
            actionBar.hide();
        }
        setContentView(R.layout.pour_in_progress_activity);

        mTapPager = (ViewPager) findViewById(R.id.tapPager);
        mPouringTapAdapter = new PouringTapAdapter(getFragmentManager());
        mTapPager.setAdapter(mPouringTapAdapter);
        mTapPager.setOnPageChangeListener(mPageChangeListener);

        mControlsFlipper = (ViewFlipper) findViewById(R.id.pour_controls_flipper);
        mClaimPourButton = (Button) findViewById(R.id.claimPourButton);
        mDrinkerName = (TextView) findViewById(R.id.pourDrinkerName);
        mDoneButton = (Button) findViewById(R.id.pourEndButton);
        mDrinkerImage = (ImageView) findViewById(R.id.pourDrinkerImage);
        mShoutText = (TextView) findViewById(R.id.shoutText);
        mAddButton = (Button) findViewById(R.id.pourAddButton);
        mSubtractButton = (Button) findViewById(R.id.pourSubtractButton);

        //for backgrounds
        mImageView0 = (ImageView) findViewById(R.id.imageView0);
        mImageView1 = (LinearLayout) findViewById(R.id.imageView1);

        mClaimPourButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final Flow flow = getCurrentlyFocusedFlow();
                if (flow == null || flow.isAuthenticated() || flow.isFinished()) {
                    return;
                }

                Log.d(TAG, "Attempting to claim flow id=" + flow.getFlowId());
                final Intent intent = KegtabCommon.getAuthDrinkerActivityIntent(PourInProgressActivity.this);
                intent.putExtra(EXTRA_FLOW_ID, flow.getFlowId());
                startActivityForResult(intent, REQUEST_AUTH_DRINKER);
                overridePendingTransition(R.anim.image_fade_in, R.anim.image_fade_in);
            }
        });

        mDoneButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                final FlowManager flowManager = mCore.getFlowManager();
                final Flow flow = getCurrentlyFocusedFlow();
                if (flow == null) {
                    return;
                }
                Log.d(TAG, "Done button pressed, ending flow " + flow.getFlowId());
                overridePendingTransition(R.anim.image_fade_in, R.anim.image_fade_in);
                flowManager.endFlow(flow);

                // If we're finishing a non-dormant flow, and other dormant flows
                // exist, assume those were started optimistically and finish them
                // now.
                if (flow.getVolumeMl() > 0) {
                    final AppConfiguration config = mCore.getConfiguration();
                    final long minVolume = config.getMinimumVolumeMl();
                    for (final Flow suspectFlow : flowManager.getAllActiveFlows()) {
                        if (suspectFlow.getVolumeMl() < minVolume) {
                            Log.d(TAG, "Also ending dormant flow: " + suspectFlow.getFlowId());
                            flowManager.endFlow(suspectFlow);
                        }
                    }
                }
            }
        });

        mAddButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                final FlowManager flowManager = mCore.getFlowManager();
                final Flow flow = getCurrentlyFocusedFlow();
                if (flow == null) {
                    return;
                }
                flow.addTicks(135);
                refreshFlows();
            }
        });

        mSubtractButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                final FlowManager flowManager = mCore.getFlowManager();
                final Flow flow = getCurrentlyFocusedFlow();
                if (flow == null) {
                    return;
                }
                flow.addTicks(-135);
                refreshFlows();
            }
        });

        mShoutText = (EditText) findViewById(R.id.shoutText);
        mShoutText.addTextChangedListener(new TextWatcher() {
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
            }

            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }

            @Override
            public void afterTextChanged(Editable s) {
                final Flow flow = getCurrentlyFocusedFlow();
                if (flow == null) {
                    Log.w(TAG, "Flow went away, dropping shout.");
                    return;
                }
                flow.setShout(s.toString());
                flow.pokeActivity();
            }
        });

        mShowCamera = true;
        mCameraFragment = (CameraFragment) getFragmentManager().findFragmentById(R.id.camera);
        mAddButton.setVisibility(View.GONE);
        mSubtractButton.setVisibility(View.GONE);
        if (!mConfig.getUseCamera() || !mConfig.getTakePhotosDuringPour()) {
            mShowCamera = false;
            getFragmentManager().beginTransaction().hide(mCameraFragment).commit();
            mAddButton.setVisibility(View.VISIBLE);
            mSubtractButton.setVisibility(View.VISIBLE);
        }

        refreshFlows();
    }

    /** Returns the currently-focused {@link Flow}, or {@code null}. */
    private Flow getCurrentlyFocusedFlow() {
        synchronized (mTapsLock) {
            final KegTap tap = getCurrentlyFocusedTap();
            if (tap == null) {
                Log.w(TAG, "No current tap, ughuh?!");
                return null;
            }
            return mFlowManager.getFlowForTap(tap);
        }
    }

    /** Returns the currently-focused {@link KegTap}, or {@code null}. */
    private KegTap getCurrentlyFocusedTap() {
        synchronized (mTapsLock) {
            final int currentIndex = mTapPager.getCurrentItem();
            if (currentIndex >= mTapList.size()) {
                return null;
            }
            return mTapList.get(currentIndex);
        }
    }

    private void updateControlsForFlow(Flow flow) {
        if (flow == null) {
            // Tap is inactive.
            mControlsFlipper.setDisplayedChild(0);
            return;
        }

        mControlsFlipper.setDisplayedChild(1);

        if (!mConfig.useAccounts()) {
            mClaimPourButton.setVisibility(View.GONE);
            mShoutText.setVisibility(View.GONE);
            mDrinkerName.setVisibility(View.GONE);
            mDrinkerImage.setVisibility(View.GONE);
        } else {
            boolean imageWasReplaced = false;
            mClaimPourButton.setEnabled(true);

            if (flow.isAnonymous()) {
                mClaimPourButton.setVisibility(View.VISIBLE);
                mDrinkerName.setVisibility(View.GONE);
            } else {
                final String username = flow.getUsername();
                mClaimPourButton.setVisibility(View.GONE);
                mDrinkerName.setVisibility(View.VISIBLE);
                mDrinkerName.setText("Pouring: " + username);

                final AuthenticationManager authManager = mCore.getAuthenticationManager();
                final User user = authManager.getUserDetail(username);
                if (user != null && user.hasImage()) {
                    // NOTE(mikey): Use the full-sized image rather than the thumbnail;
                    // in many cases the former will already be in the cache from
                    // DrinkerSelectActivity.
                    final String thumbnailUrl = user.getImage().getThumbnailUrl();
                    if (!Strings.isNullOrEmpty(thumbnailUrl)) {
                        mImageDownloader.download(thumbnailUrl, mDrinkerImage);
                        imageWasReplaced = true;
                    }
                } else {
                    Log.d(TAG, "No user info.");
                }
            }

            if (!imageWasReplaced) {
                mDrinkerImage.setImageBitmap(null);
                Utils.setBackground(mDrinkerImage, getResources().getDrawable(R.drawable.unknown_drinker));
            }
        }

        //dummy tap status
        {
            final TextView title = (TextView) findViewById(R.id.tapTitle);
            final TextView subtitle = (TextView) findViewById(R.id.tapSubtitle);

            KegTap tap = getCurrentlyFocusedTap();
            final String tapName = tap.getName();
            if (!Strings.isNullOrEmpty(tapName)) {
                subtitle.setText(tapName);
            }

            if (!tap.hasCurrentKeg()) {
                return;
            }

            final Models.Keg keg = tap.getCurrentKeg();
            title.setText(keg.getBeverage().getName());

            // Badge 1: Pints Poured
            final BadgeView badge1 = (BadgeView) findViewById(R.id.tapStatsBadge1);
            double mlPoured = keg.getServedVolumeMl();
            Pair<String, String> qtyPoured = Units.localize(mCore.getConfiguration(), mlPoured);

            badge1.setBadgeValue(qtyPoured.first);
            badge1.setBadgeCaption("Total " + Units.capitalizeUnits(qtyPoured.second) + " Poured");

            // Badge 2: Pints Remain
            final BadgeView badge2 = (BadgeView) findViewById(R.id.tapStatsBadge2);
            Pair<String, String> qtyRemain = Units.localize(mCore.getConfiguration(), keg.getRemainingVolumeMl());

            badge2.setBadgeValue(qtyRemain.first);
            badge2.setBadgeCaption(Units.capitalizeUnits(qtyRemain.second) + " Left");

            // Badge 3: Temperature
            final BadgeView badge3 = (BadgeView) findViewById(R.id.tapStatsBadge3);
            double lastTemperature = Double.NaN;
            if (mConfig.isLocalBackend()) {
                final LocalBackend backend = (LocalBackend) mCore.getBackend();
                lastTemperature = backend.mTemperature;
                badge3.setVisibility(View.VISIBLE);
            } else if (tap.hasLastTemperature()) {
                lastTemperature = tap.getLastTemperature().getTemperatureC();
                badge3.setVisibility(View.VISIBLE);
            } else {
                badge3.setVisibility(View.GONE);
            }
            if (!Double.isNaN(lastTemperature)) {
                String units = "C";
                if (!mCore.getConfiguration().getTemperaturesCelsius()) {
                    lastTemperature = Units.temperatureCToF(lastTemperature);
                    units = "F";
                }
                final String tempValue = String.format("%.1f\u00B0", Double.valueOf(lastTemperature));
                badge3.setBadgeValue(tempValue);
                badge3.setBadgeCaption(String.format("Temperature (%s)", units));
            } else {
                badge3.setVisibility(View.GONE);
            }
        }

        //for backgrounds
        switch (mTapPager.getCurrentItem()) {
        case 0:
            mImageView0.setImageResource(R.drawable.e1);
            mImageView1.setBackgroundResource(R.drawable.e2);
            break;
        case 1:
            mImageView0.setImageResource(R.drawable.a1);
            mImageView1.setBackgroundResource(R.drawable.a2);
            break;
        case 2:
            mImageView0.setImageResource(R.drawable.b1);
            mImageView1.setBackgroundResource(R.drawable.b2);
            break;
        case 3:
            mImageView0.setImageResource(R.drawable.c1);
            mImageView1.setBackgroundResource(R.drawable.c2);
            break;
        case 4:
            mImageView0.setImageResource(R.drawable.d1);
            mImageView1.setBackgroundResource(R.drawable.d2);
            break;
        default:
            mImageView0.setImageResource(R.drawable.e1);
            mImageView1.setBackgroundResource(R.drawable.e2);
            break;
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        synchronized (mTapsLock) {
            for (final KegTap tap : mCore.getTapManager().getVisibleTaps()) {
                mTapList.add(tap);
            }
            mPouringTapAdapter.notifyDataSetChanged();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume");
        mCore.getBus().register(this);
        mHandler.post(REFRESH_FLOWS_RUNNABLE);
    }

    @Override
    protected void onPostResume() {
        super.onPostResume();
        final Flow flow = getCurrentlyFocusedFlow();
        Log.d(TAG, "onPostResume: focusedFlow: " + flow);
        if (flow != null) {
            updateControlsForFlow(flow);
            if (Strings.isNullOrEmpty(flow.getImagePath())) {
                if (mShowCamera && mConfig.getEnableAutoTakePhoto()) {
                    mCameraFragment.schedulePicture();
                }
            }
        }
    }

    @Override
    protected void onPause() {
        Log.d(TAG, "onPause");
        mHandler.removeCallbacks(REFRESH_FLOWS_RUNNABLE);
        mCore.getBus().unregister(this);
        super.onPause();

        if (mConfig.wakeDuringPour()) {
            getWindow().addFlags(
                    WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
                            | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                            | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
        }
    }

    @Override
    protected void onStop() {
        if (isFinishing()) {
            endAllFlows();
        }
        mTapList.clear();
        super.onStop();
    }

    @Override
    public void onBackPressed() {
        if (!mFlowManager.getAllActiveFlows().isEmpty()) {
            endAllFlows();
        } else {
            super.onBackPressed();
        }
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
        refreshFlows();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
        case REQUEST_AUTH_DRINKER:
            if (data == null) {
                Log.i(TAG, "No user selected.");
                return;
            }
            String username = data.getStringExtra(KegtabCommon.ACTIVITY_AUTH_DRINKER_RESULT_EXTRA_USERNAME);
            if (Strings.isNullOrEmpty(username)) {
                Log.i(TAG, "No user selected.");
                return;
            }
            int flowId = data.getIntExtra(EXTRA_FLOW_ID, 0);
            final Flow flow = mFlowManager.getFlowForFlowId(flowId);
            if (flow == null) {
                Log.w(TAG, "No flow for id " + flowId);
                return;
            }
            Log.i(TAG, "Flow " + flowId + " claimed by: " + username);
            flow.setUsername(username);
            return;
        default:
            super.onActivityResult(requestCode, resultCode, data);
        }
    }

    private KegTap getMostActiveTap() {
        KegTap tap = getCurrentlyFocusedTap();
        long leastIdle = Long.MAX_VALUE;

        for (Flow flow : mFlowManager.getAllActiveFlows()) {
            final KegTap flowTap = flow.getTap();
            if (flowTap != null && flow.getIdleTimeMs() < leastIdle) {
                tap = flowTap;
                leastIdle = flow.getIdleTimeMs();
            }
        }
        return tap;
    }

    private void scrollToMostActiveTap() {
        final Flow currentFlow = getCurrentlyFocusedFlow();

        // Current flow is active; don't scroll.
        if (currentFlow != null && currentFlow.getIdleTimeMs() < IDLE_SCROLL_TIMEOUT_MILLIS) {
            return;
        }

        // Current flow is most active; still don't scroll.
        final KegTap mostActive = getMostActiveTap();
        if (currentFlow != null && mostActive == currentFlow.getTap()) {
            return;
        }

        // No active taps.
        if (mostActive == null) {
            Log.d(TAG, "Could not find an active tap.");
            return;
        }

        // We have a candidate.
        final Flow candidateFlow = mFlowManager.getFlowForTap(mostActive);
        if (candidateFlow != null) {
            final KegTap tap = candidateFlow.getTap();
            synchronized (mTapsLock) {
                final int position = mTapList.indexOf(tap);
                if (position >= 0) {
                    scrollToPosition(position);
                }
            }
        }
    }

    private void scrollToPosition(int position) {
        if (position != mTapPager.getCurrentItem()) {
            Log.d(TAG, "scrollToPosition: " + position);
            mTapPager.setCurrentItem(position, true);
        }
    }

    private void refreshFlows() {
        final Set<Flow> activeFlows = Sets.newLinkedHashSet(mFlowManager.getAllActiveFlows());
        mActiveFlows.addAll(activeFlows);
        final Set<Flow> oldFlows = Sets.newLinkedHashSet(Sets.difference(mActiveFlows, activeFlows));
        for (final Flow oldFlow : oldFlows) {
            Log.d(TAG, "Removing inactive flow: " + oldFlow);
            mActiveFlows.remove(oldFlow);
        }

        if (mShowCamera) {
            mCameraFragment.setEnabled(!mActiveFlows.isEmpty());
        }

        if (mActiveFlows.isEmpty()) {
            cancelIdleWarning();
            finish();
            return;
        }

        long largestIdleTime = Long.MIN_VALUE;
        for (final Flow flow : mActiveFlows) {
            if (flow.getTap() == null) {
                continue;
            }

            if (DEBUG) {
                Log.d(TAG, "Refreshing with flow: " + flow);
            }

            final long idleTimeMs = flow.getIdleTimeMs();
            if (idleTimeMs > largestIdleTime) {
                largestIdleTime = idleTimeMs;
            }
        }

        if (largestIdleTime >= mConfig.getIdleWarningMs()) {
            sendIdleWarning();
        } else {
            cancelIdleWarning();
        }

        scrollToMostActiveTap();
    }

    private void endAllFlows() {
        for (final Flow flow : mFlowManager.getAllActiveFlows()) {
            mFlowManager.endFlow(flow);
        }
        cancelIdleWarning();
        if (mShowCamera) {
            mCameraFragment.cancelPendingPicture();
        }
    }

    private void sendIdleWarning() {
        if (mIdleDialogShowing) {
            return;
        }
        mIdleDialogFragment.show(getFragmentManager(), "idle");
        mIdleDialogShowing = true;
    }

    private void cancelIdleWarning() {
        if (mIdleDialogShowing) {
            mIdleDialogFragment.dismiss();
            getFragmentManager().executePendingTransactions();
            mIdleDialogShowing = false;
        }
    }

    public static Intent getStartIntent(Context context) {
        final Intent intent = new Intent(context, PourInProgressActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        return intent;
    }

}