public enum WorldRetriever {
    INSTANCE;

    private World world;

    public void inject(World world) {
        this.world = world;
    }

    public World getWorld() {
        return world;
    }

} public enum EditorAction {
    CANCEL,
    CLEAR,
    DONE
} public interface WorldEditorMenu {
    void attachActionSelectedListener(EditorMenuView.OnActionSelectedListener listener);

    void detachActionSelectedListener();

    WorldEditorMenuFragment getFragment();

    void abortEdition();
} public interface ImageLoader {
    void attachLoadImageListener(OnLoadImageSelectedListener listener);

    void detachActionSelectedListener();

    ImageLoaderFragment getFragment();

    void loadWorldFrom(Uri imageUri);

    void abortPictureLoading();

    interface OnLoadImageSelectedListener {
        void onLoadImage(ImageLoaderAction action);
    }
} public enum ImageLoaderAction {
    CAMERA,
    GALLERY
} public enum ImageLoaderResult {
    GALLERY(23214),
    CAMERA(12321),
    UNKNOWN(-1);

    private int code;

    ImageLoaderResult(int code) {
        this.code = code;
    }

    public int getCode() {
        return code;
    }

    public static ImageLoaderResult from(int code) {
        for (ImageLoaderResult imageLoaderResult : values()) {
            if (imageLoaderResult.code == code) {
                return imageLoaderResult;
            }
        }
        return UNKNOWN;
    }
} public class InfoActivity extends AppIntro2 {
    private static final int OFF_SCREEN_PAGE_LIMIT = 3;
    private final InfoPreferences infoPreferences;

    public InfoActivity() {
        super();
        infoPreferences = InfoPreferences.newInstance();
    }

    @Override
    public void init(Bundle savedInstanceState) {
        addSlide(new WellcomeSlide(), getApplicationContext());
        addSlide(new PresentationSlide(), getApplicationContext());
        addSlide(new RulesSlide(), getApplicationContext());
        addSlide(new LifeCreationSlide(), getApplicationContext());
        setOffScreenPageLimit(OFF_SCREEN_PAGE_LIMIT);
    }

    @Override
    public void onDonePressed() {
        done();
    }

    private void done() {
        infoPreferences.markInfoAsSeen();
        finish();
    }
} public class InfoPreferences {
    private static final String INFO_PREFERENCES = "InfoPreferences";
    private static final String ALREADY_SEEN = INFO_PREFERENCES + ".AlreadySeen";
    public static final String FROM_MENU = INFO_PREFERENCES + ".FromMenu";
    private final SharedPreferences sharedPreferences;

    protected InfoPreferences(SharedPreferences sharedPreferences) {
        this.sharedPreferences = sharedPreferences;
    }

    public static InfoPreferences newInstance() {
        Context context = ContextRetriever.INSTANCE.getApplicationContext();
        SharedPreferences sharedPreferences = context.getSharedPreferences(INFO_PREFERENCES,
                Context.MODE_PRIVATE);

        return new InfoPreferences(sharedPreferences);
    }

    public boolean shouldShowInfo() {
        return !sharedPreferences.getBoolean(ALREADY_SEEN, false);
    }

    public void markInfoAsSeen() {
        sharedPreferences.edit().putBoolean(ALREADY_SEEN, true).apply();
    }
} public interface MainMenu {
    void attachItemSelectedListener(MenuOptionsView.OnItemSelectedListener listener);

    void detachItemSelectedListener();

    boolean isOpened();

    void closeMenu();

    void openMenu();

    MainMenuFragment getFragment();
} public enum MenuItem {
    EDIT_WORLD("Edit World"),
    LOAD_WORLD("Load World"),
    INFO("Info");

    private final String value;

    MenuItem(String value) {
        this.value = value;
    }

    public String value() {
        return value;
    }
} public class MenuSwitcher {

    private final FragmentManager fragmentManager;

    public static MenuSwitcher newInstance() {
        return new MenuSwitcher(ContextRetriever.INSTANCE.getActivity().getSupportFragmentManager());
    }

    @VisibleForTesting
    protected MenuSwitcher(FragmentManager fragmentManager) {
        this.fragmentManager = fragmentManager;
    }

    public MainMenu addMainMenu() {
        if (mainMenuIsPresent()) {
            return Classes.from(fragmentManager.findFragmentByTag(MainMenuFragment.TAG));
        } else {
            MainMenu mainMenu = new MainMenuFragment();
            fragmentManager.beginTransaction().replace(R.id.menu_container, mainMenu.getFragment(), MainMenuFragment.TAG).commit();

            return mainMenu;
        }
    }

    public WorldEditorMenu addEditorMenu() {
        if (editorMenuIsPresent()) {
            return Classes.from(fragmentManager.findFragmentByTag(WorldEditorMenuFragment.TAG));
        } else {
            WorldEditorMenu worldEditorMenu = new WorldEditorMenuFragment();
            fragmentManager.beginTransaction().replace(R.id.menu_container, worldEditorMenu.getFragment(), WorldEditorMenuFragment.TAG).commit();

            return worldEditorMenu;
        }
    }

    public ImageLoader addImageLoader() {
        if (imageLoaderIsPresent()) {
            return Classes.from(fragmentManager.findFragmentByTag(ImageLoaderFragment.TAG));
        } else {
            ImageLoader imageLoader = new ImageLoaderFragment();
            fragmentManager.beginTransaction().replace(R.id.menu_container, imageLoader.getFragment(), ImageLoaderFragment.TAG).commit();

            return imageLoader;
        }
    }

    private boolean mainMenuIsPresent() {
        return fragmentManager.findFragmentByTag(MainMenuFragment.TAG) != null;
    }

    private boolean editorMenuIsPresent() {
        return fragmentManager.findFragmentByTag(WorldEditorMenuFragment.TAG) != null;
    }

    private boolean imageLoaderIsPresent() {
        return fragmentManager.findFragmentByTag(ImageLoaderFragment.TAG) != null;
    }
} public class PictureRetriever {
    private static final String MATERIAL_LIFE = "MATERIAL_LIFE";
    private static final String EXTENSION = ".jpg";
    private static final String FILE_SCHEME = "file:";

    private String lastLoadedImagePath;

    public void openCameraForResult() {
        Intent intent = createIntentForCamera();
        if (intent != null) {
            ContextRetriever.INSTANCE.getActivity().startActivityForResult(intent, ImageLoaderResult.CAMERA.getCode());
        }
    }

    public Uri getLastLoadedImagePath() {
        return Uri.parse(lastLoadedImagePath);
    }

    public void openGalleryForResult() {
        ContextRetriever.INSTANCE.getActivity().startActivityForResult(createIntentForGallery(), ImageLoaderResult.GALLERY.getCode());
    }

    private Intent createIntentForCamera() {
        try {
            return new Intent(MediaStore.ACTION_IMAGE_CAPTURE)
                    .putExtra(MediaStore.EXTRA_OUTPUT, createImageFile());
        } catch (IOException e) {
            Log.e("Problem creating picture:" + e);
        }
        return null;
    }

    private Uri createImageFile() throws IOException {
        File storageDir = ContextRetriever.INSTANCE.getApplicationContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES);
        File image = File.createTempFile(MATERIAL_LIFE, EXTENSION, storageDir);
        lastLoadedImagePath = FILE_SCHEME + image.getAbsolutePath();
        return FileProvider.getUriForFile(
                ContextRetriever.INSTANCE.getApplicationContext(),
                ContextRetriever.INSTANCE.getResources().getString(R.string.file_provider),
                image
        );
    }

    private Intent createIntentForGallery() {
        return new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    }
} public class EditorMenuView extends RelativeLayout {
    private ClearView clearView;
    private PixelButton clearButton;

    public EditorMenuView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public EditorMenuView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        clearView = Views.findById(this, R.id.clear_view);
        clearButton = Views.findById(this, R.id.edit_fab_clear);
    }

    public void animateClear(final ClearAnimationListener clearAnimationListener) {
        clearView.animateClearFrom(ViewMeasurer.getViewCenterOnScreen(clearButton), clearAnimationListener);
    }

    public interface ClearAnimationListener {
        void onClearDone();
    }

    public interface OnActionSelectedListener {
        void onActionPerformed(EditorAction action);
    }
} public class MenuOptionsView extends ListView {

    private OnItemSelectedListener onMenuOnItemSelectedListener;
    private ViewRevealer viewRevealer;

    public MenuOptionsView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MenuOptionsView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        viewRevealer = new ViewRevealer();
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        setOnItemClickListener(onItemSelectedListener);
    }

    public void setOnMenuItemSelectedListener(OnItemSelectedListener onMenuItemSelectedListener) {
        this.onMenuOnItemSelectedListener = onMenuItemSelectedListener;
    }

    private boolean hasOnMenuItemSelectedListener() {
        return onMenuOnItemSelectedListener != null;
    }

    private final AdapterView.OnItemClickListener onItemSelectedListener = new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            setSelection(position);
            if (hasOnMenuItemSelectedListener()) {
                MenuItemAdapter adapter = Classes.from(parent.getAdapter());
                onMenuOnItemSelectedListener.onItemSelected(adapter.getItem(position));
            }
        }
    };

    public void revealFrom(Point point) {
        viewRevealer.revealFrom(point, this, revealAnimationListener);
    }

    public void concealFrom(Point point) {
        viewRevealer.concealFrom(point, this, concealAnimationListener);
    }

    private final AnimatorListenerAdapter revealAnimationListener = new AnimatorListenerAdapter() {
        @Override
        public void onAnimationStart(Animator animation) {
            show();
        }
    };

    private final AnimatorListenerAdapter concealAnimationListener = new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
            hide();
        }
    };

    private void show() {
        setVisibility(View.VISIBLE);
    }

    private void hide() {
        setVisibility(View.INVISIBLE);
    }

    @Override
    protected void onDetachedFromWindow() {
        setOnItemSelectedListener(null);
        super.onDetachedFromWindow();
    }

    public interface OnItemSelectedListener {
        void onItemSelected(MenuItem item);
    }
} public class World extends Rainbow implements RainbowImage.LoadPictureListener {
    private static final RainbowScheduler SCHEDULER = RainbowSchedulers.single("LoadWorld", RainbowSchedulers.Priority.MIN);

    @VisibleForTesting
    //CHECKSTYLE IGNORE VisibilityModifier
            GameOfLife gameOfLife;
    //CHECKSTYLE END IGNORE

    public static World newInstance() {
        RainbowDrawer rainbowDrawer = new RainbowDrawer();
        RainbowInputController rainbowInputController = RainbowInputController.newInstance();
        return new World(rainbowDrawer, rainbowInputController);
    }

    @VisibleForTesting
    protected World(RainbowDrawer rainbowDrawer, RainbowInputController rainbowInputController) {
        super(rainbowDrawer, rainbowInputController);
    }

    @Override
    public void onSketchSetup() {
        super.onSketchSetup();
        gameOfLife = GameOfLife.newInstance(getRainbowDrawer(), getRainbowInputController());
        getRainbowInputController().attach(gameOfLife);
    }

    public void startEdition() {
        gameOfLife.startEdition();
    }

    public void endEdition() {
        gameOfLife.endEdition();
    }

    public void clear() {
        gameOfLife.clear();
    }

    @Override
    public void onStep() {
        gameOfLife.doStep();
    }

    @Override
    public void onSketchDestroy() {
        getRainbowInputController().detach();
    }

    public void restoreLastWorld() {
        gameOfLife.restoreLastWorld();
    }

    public void loadWorldFrom(final Uri image) {
        gameOfLife.clear();
        SCHEDULER.scheduleNow(new Runnable() {
            @Override
            public void run() {
                getRainbowDrawer().loadImage(image, Modes.LoadMode.LOAD_CENTER_CROP, World.this);
            }
        });
    }

    @Override
    public void onLoadSucceed(RainbowImage image) {
        gameOfLife.loadWorldFrom(image);
    }

    @Override
    public void onLoadFail() {
        restoreLastWorld();
    }
}
import android.content.Intent;
import android.os.Bundle;
import android.view.ViewGroup;
import com.juankysoriano.materiallife.R;
import com.juankysoriano.materiallife.WorldRetriever;
import com.juankysoriano.materiallife.editor.EditorAction;
import com.juankysoriano.materiallife.editor.WorldEditorMenu;
import com.juankysoriano.materiallife.imageloader.ImageLoader;
import com.juankysoriano.materiallife.imageloader.ImageLoaderAction;
import com.juankysoriano.materiallife.imageloader.ImageLoaderResult;
import com.juankysoriano.materiallife.info.InfoActivity;
import com.juankysoriano.materiallife.info.preferences.InfoPreferences;
import com.juankysoriano.materiallife.menu.MainMenu;
import com.juankysoriano.materiallife.menu.MenuItem;
import com.juankysoriano.materiallife.navigation.MenuSwitcher;
import com.juankysoriano.materiallife.navigation.PictureRetriever;
import com.juankysoriano.materiallife.ui.sketch.editor.EditorMenuView;
import com.juankysoriano.materiallife.ui.sketch.menu.MenuOptionsView;
import com.juankysoriano.materiallife.world.World;
package com.juankysoriano.materiallife.activities;



public class GameOfLifeActivity extends MaterialLifeActivity {

    private final World world;
    private final InfoPreferences infoPreferences;
    private MenuSwitcher menuSwitcher;
    private PictureRetriever pictureRetriever;
    private MainMenu mainMenu;
    private WorldEditorMenu editorMenu;
    private ImageLoader imageLoader;

    public GameOfLifeActivity() {
        this(World.newInstance(), InfoPreferences.newInstance());
    }

    private GameOfLifeActivity(World world, InfoPreferences infoPreferences) {
        WorldRetriever.INSTANCE.inject(world);
        this.world = world;
        this.infoPreferences = infoPreferences;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        menuSwitcher = MenuSwitcher.newInstance();
        pictureRetriever = new PictureRetriever();

        setContentView(R.layout.world);
        world.injectInto((ViewGroup) findViewById(R.id.world));

        addMainMenu();
    }

    @Override
    protected void onStart() {
        super.onStart();
        world.start();
    }

    @Override
    protected void onResume() {
        super.onResume();
        world.resume();
        if (infoPreferences.shouldShowInfo()) {
            Intent intent = new Intent(getApplicationContext(), InfoActivity.class);
            startActivity(intent);
        }
    }

    private final MenuOptionsView.OnItemSelectedListener onMenuItemSelectedListener = new MenuOptionsView.OnItemSelectedListener() {
        @Override
        public void onItemSelected(MenuItem item) {