public class App extends Application {
    public static Context CONTEXT;
    public static Resources RESOURCES;
    public static Handler MAIN_THREAD;
    public static TinyBus BUS;
    public static AmbientLight SENSOR_AMBIENT_LIGHT;
    public static List<Gaia> GAIAS;
    public static Realm REALM;

    public static void LOG(Object msg) {
        do {
            Log.d(CONTEXT.getPackageName(), String.valueOf(msg));
        } while (false);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        if (LeakCanary.isInAnalyzerProcess(this)) {
            return;
        }
        LeakCanary.install(this);
        Realm.init(this);

        CONTEXT = this;
        RESOURCES = getResources();
        MAIN_THREAD = new Handler(getMainLooper());
        BUS = TinyBus.from(this);
        SENSOR_AMBIENT_LIGHT = new AmbientLight();
        GAIAS = new ArrayList<>();
        REALM = Realm.getDefaultInstance();
    }
} public class Future {
} public class Past {
} public class Present {
} public class FutureFragment extends SuperSheetsFragment {
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setResId(R.layout.fragment_future);
        setTabTitle(MainActivity.FUTURE);
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        RealmResults<Note> results = App.REALM.where(Note.class)
                .equalTo("archive", false)
                .equalTo("reminder", true)
                .findAllSorted("date");

        for (int i = 0; i < results.size(); i++) {
            addNote(results.get(i));
        }

        setEmptyView();
    }
} public class PastFragment extends SuperSheetsFragment {
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setResId(R.layout.fragment_past);
        setTabTitle(MainActivity.PAST);
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        RealmResults<Note> results = App.REALM.where(Note.class)
                .equalTo("archive", true)
                .equalTo("reminder", false)
                .findAllSorted("date");

        for (int i = 0; i < results.size(); i++) {
            addNote(results.get(i));
        }

        setEmptyView();
    }
} public class PresentFragment extends SuperSheetsFragment {
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setResId(R.layout.fragment_present);
        setTabTitle(MainActivity.PRESENT);
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        RealmResults<Note> results = App.REALM.where(Note.class)
                .equalTo("archive", false)
                .equalTo("reminder", false)
                .findAllSorted("date");

        for (int i = 0; i < results.size(); i++) {
            addNote(results.get(i));
        }

        setEmptyView();
    }
} public class Gaia {
    private static final int COLOR_TRANSFORM_DURATION = 250;

    // this field is important because it'll identify the object in the Gaia objects Array inside Application class
    private String name;
    // well the object that'll be mutated... duh!
    private WeakReference<Object> objectToMutate;
    // original view color
    @ColorInt
    private int originalColor;
    // stores the last time, in ms, the object mutated
    private long lastUpdate;

    private Gaia(Builder builder) {
        this.name = builder.getName();
        this.objectToMutate = builder.getMutativeView();
        this.originalColor = builder.getColorToScale();
        // tango down!
        GaiaUtils.seekAndDestroy(getName());
        // good morning!!
        App.GAIAS.add(this);
        // One Bus to rule them all, One Bus to find them,
        // One Bus to bring them all and in the darkness bind them
        App.BUS.register(this);
    }

    public String getName() {
        return name;
    }

    public static class Builder {
        private String name;
        private WeakReference<Object> mutativeView;
        @ColorInt
        private int defaultBgColor;

        public Builder setName(String name) {
            this.name = name;
            return this;
        }

        public Builder setMutativeView(WeakReference<Object> mutativeView) {
            this.mutativeView = mutativeView;
            return this;
        }

        public Builder setColorToScale(int colorToScale) {
            this.defaultBgColor = colorToScale;
            return this;
        }

        WeakReference<Object> getMutativeView() {
            return mutativeView;
        }

        int getColorToScale() {
            return defaultBgColor;
        }

        public Gaia build() {
            return new Gaia(this);
        }

        public String getName() {
            return name;
        }
    }

    @Subscribe(mode = Subscribe.Mode.Background)
    public void onUpdatedAmbientLight(LightSensor sensor) {
        // calculates the new color based on the sensor values and the original color
        // the algorithm is magical
        final int newColor = Color.HSVToColor(GaiaUtils.computeHSV(sensor, objectToMutate, originalColor));
        int currentColor = 0;

        // mutate all the things
        Object view = objectToMutate.get();
        if (view != null) {
            if (view instanceof LinearLayout) {
                currentColor = ((ColorDrawable) ((LinearLayout) view).getBackground()).getColor();
            } else if (view instanceof TextView) {
                currentColor = ((TextView) view).getCurrentTextColor();
            } else if (view instanceof FloatingActionButton) {
                currentColor = ((FloatingActionButton) view).getBackgroundTintList().getDefaultColor();
            } else if (view instanceof FrameLayout) {
                currentColor = ((ColorDrawable) ((FrameLayout) view).getBackground()).getColor();
            }
        }

        boolean hasNewColor = currentColor != newColor;
        boolean isReady = GaiaUtils.isReady(lastUpdate);

        // we don't want to update the colors every time the sensor outputs new values. Yes, the sensor is very nervous
        if (hasNewColor && isReady) {
            final int fromColor = currentColor;
            lastUpdate = System.currentTimeMillis();

            // run this on the Main Thread, this @Subscribe method runs in a background thread
            App.MAIN_THREAD.post(new Runnable() {
                @Override
                public void run() {
                    // much animation. wow material. such beauty. so rad
                    // pass it the object (in this case it's a view) that's going to be mutated,
                    // the starting color value, and the color we want to animate to
                    // lastly the duration of the animation, in this case the default should be 250ms
                    ColorAnimationUtils.animateColor(objectToMutate, fromColor, newColor, COLOR_TRANSFORM_DURATION);
                }
            });
        }
    }
} public class GaiaUtils {
    private final static int MIN_UPDATE_INTERVAL_MS = 300;
    public final static String FAB = "fab";
    public final static String NOTE_PARENT_LAYOUT = "note_parent_layout";
    public final static String NOTE_TITLE = "note_title";
    public final static String NOTE_CONTENT = "note_content";

    // makes sure it doesn't go too bright
    public final static float HIGH_THRESHOLD = 0.8f;
    // makes sure it doesn't go too dark
    public final static float TOLERABLE_THRESHOLD = 0.2f;
    public final static float LOW_THRESHOLD = 0.05f;
    public final static float MIN = 0.0f;

    public static boolean isReady(long lastUpdate) {
        return System.currentTimeMillis() > (lastUpdate + MIN_UPDATE_INTERVAL_MS);
    }

    public static void seekAndDestroy(String name) {
        for (int i = 0; i < App.GAIAS.size(); i++) {
            Gaia gaia = App.GAIAS.get(i);

            if (gaia.getName().equals(name)) {
                if (App.BUS.hasRegistered(gaia)) {
                    App.BUS.unregister(gaia);
                    App.GAIAS.remove(i);
                    break;
                }
            }
        }
    }

    /**
     *
     * @param sensor object coming from our bus containing the Ambient Light sensor value
     * @param objectToMutate the object that'll be mutated
     * @param originalColor the original color of the object that'll be mutated
     *
     * @return a new HSV value to be applied into the object
     */
    public static float[] computeHSV(LightSensor sensor, Object objectToMutate, int originalColor) {
        // we divide the color into red green and blue
        int red = Color.red(originalColor);
        int green = Color.green(originalColor);
        int blue = Color.blue(originalColor);

        final float hsv[] = new float[3];

        Color.RGBToHSV(red, green, blue, hsv);

        // 'magic' algorithm
        float div = Float.valueOf(String.format(Locale.US, "%.2f",
                sensor.getLight() / ((int) SensorManager.LIGHT_OVERCAST >> 1)));

        if (div > HIGH_THRESHOLD) {
            div = HIGH_THRESHOLD;
        } else if (div < LOW_THRESHOLD) {
            div = MIN;
        }

        // Text is, by rule, in a contrasted color to the background, so we have to apply the formula backwards to the
        // rest of the views
        if (objectToMutate instanceof TextView) {
            hsv[2] += div;
        } else {
            hsv[2] -= div;
        }

        // making sure we don't have a weird negative value
        hsv[2] = Math.max(hsv[2], TOLERABLE_THRESHOLD);

        return hsv;
    }
} public class NoteUtils {
    // it's ok to call this on the Main Thread, Realm is ridiculously fast it doesn't block Main Thread
    public static boolean hasNotes() {
        return App.REALM.where(Note.class).findAll().size() > 0;
    }
} public class ViewUtils {
    public static int[] viewCoordinates(View view) {
        int[] viewLocation = new int[2];

        view.getLocationOnScreen(viewLocation);

        return viewLocation;
    }

    public static final ViewOutlineProvider CIRCULAR_OUTLINE = new ViewOutlineProvider() {
        @Override
        public void getOutline(View view, Outline outline) {
            outline.setOval(view.getPaddingLeft(),
                    view.getPaddingTop(),
                    view.getWidth() - view.getPaddingRight(),
                    view.getHeight() - view.getPaddingBottom());
        }
    };
}
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.ActivityOptions;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.ViewPager;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.mikepenz.materialize.util.UIUtils;
import java.lang.ref.WeakReference;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import de.halfbit.tinybus.Subscribe;
import liam.franco.selene.R;
import liam.franco.selene.application.App;
import liam.franco.selene.bus.Future;
import liam.franco.selene.bus.Past;
import liam.franco.selene.bus.Present;
import liam.franco.selene.fragments.FutureFragment;
import liam.franco.selene.fragments.PastFragment;
import liam.franco.selene.fragments.PresentFragment;
import liam.franco.selene.modules.Gaia;
import liam.franco.selene.utils.GaiaUtils;
import liam.franco.selene.utils.NoteUtils;
import liam.franco.selene.utils.ViewUtils;
/*
 * Copyright 2016 Francisco Franco & Liam Spradlin
 *
 * 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 liam.franco.selene.activities;





public class MainActivity extends SuperAppCompatActivity {
    public static final String PAST = App.CONTEXT.getString(R.string.past);
    public static final String PRESENT = App.CONTEXT.getString(R.string.present);
    public static final String FUTURE = App.CONTEXT.getString(R.string.future);

    @BindView(R.id.parent_layout)
    protected FrameLayout parentLayout;
    @BindView(R.id.toolbar)
    protected Toolbar toolbar;
    @BindView(R.id.tablayout)
    protected TabLayout tabLayout;
    @BindView(R.id.viewpager)
    protected ViewPager viewPager;
    @BindView(R.id.try_writing_a_note)
    protected TextView tryWritingAnote;
    @BindView(R.id.fab)
    protected FloatingActionButton fab;
    @BindView(R.id.bottom_fab_bar)
    protected FrameLayout bottomFabBar;

    private static String[] tabs;

    static {
        tabs = new String[]{
                PAST,
                PRESENT,
                FUTURE
        };
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        App.BUS.register(this);

        setSupportActionBar(toolbar);

        viewPager.setOffscreenPageLimit(3);
        viewPager.setAdapter(fragmentPagerAdapter);
        tabLayout.setupWithViewPager(viewPager);

        // we want custom fonts on the tabs textViews
        for (int i = 0; i < tabLayout.getTabCount(); i++) {
            tabLayout.getTabAt(i).setCustomView(R.layout.tab_layout_item);
        }

        viewPager.setCurrentItem(1);

        // mutate the FAB's colour!
        new Gaia.Builder()
                .setName(GaiaUtils.FAB)
                .setMutativeView(new WeakReference<Object>(fab))
                .setColorToScale(fab.getBackgroundTintList().getDefaultColor())
                .build();
    }

    private void animateOpenBottomFabSheet() {
        if (bottomFabBar != null && bottomFabBar.getVisibility() == View.INVISIBLE) {
            final int[] xy = ViewUtils.viewCoordinates(fab);

            Animator animator = ViewAnimationUtils.createCircularReveal(
                    bottomFabBar,
                    (xy[0] + (fab.getMeasuredWidth() >> 1)),
                    -bottomFabBar.getHeight(),
                    UIUtils.convertDpToPixel(56f, App.CONTEXT),
                    Math.max(bottomFabBar.getWidth(), bottomFabBar.getHeight()));
            animator.setDuration(500);
            animator.setStartDelay(7000);
            animator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationStart(Animator animation) {
                    if (fab != null) {
                        fab.setVisibility(View.GONE);
                    }

                    if (bottomFabBar != null) {
                        bottomFabBar.setVisibility(View.VISIBLE);
                    }
                }
            });
            animator.start();
        }
    }

    private void animateCloseBottomFabSheet() {
        if (bottomFabBar != null && bottomFabBar.getVisibility() == View.VISIBLE) {
            final int[] xy = ViewUtils.viewCoordinates(fab);

            Animator animator = ViewAnimationUtils.createCircularReveal(
                    bottomFabBar,
                    (xy[0] + (fab.getMeasuredWidth() >> 1)),
                    -bottomFabBar.getHeight(),
                    Math.max(bottomFabBar.getWidth(), bottomFabBar.getHeight()),
                    0);
            animator.setDuration(500);
            animator.setStartDelay(500);
            animator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    if (fab != null) {
                        fab.setVisibility(View.VISIBLE);
                    }
                    if (bottomFabBar != null) {
                        bottomFabBar.setVisibility(View.INVISIBLE);
                    }
                }
            });
            animator.start();
        }
    }

    private FragmentPagerAdapter fragmentPagerAdapter = new FragmentPagerAdapter(getSupportFragmentManager()) {
        @Override
        public Fragment getItem(int position) {
            switch (position) {
                case 0:
                    return new PastFragment();
                case 1:
                    return new PresentFragment();