[
  {
    "path": ".gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.DS_Store\n/build\n/captures\n"
  },
  {
    "path": "README.md",
    "content": "# ImageTransition\nThis is a pet project that shows how to implement shared element image transition on pre-Lollipop devices on Android.\n\n# Pre-Lolipop Demo\nIf you look closer there is almost no difference between the native Android Lollipop shared element aniamation\n\nAnimation and canceling animation in the middle of it.\n\n![support_animation](https://cloud.githubusercontent.com/assets/2686355/13902301/783b604c-ee4c-11e5-8428-bab7a67f6fff.gif)     ![cancel support animation in the middle](https://cloud.githubusercontent.com/assets/2686355/13902305/80beb502-ee4c-11e5-8dde-fcfade1eb93c.gif)\n\n# Lollipop native animation\n![lollipop animation](https://cloud.githubusercontent.com/assets/2686355/13902304/7d066c52-ee4c-11e5-9329-a455c0baa440.gif)\n\n# Details of implementation\n\n[![Medium](https://img.shields.io/badge/Meduim-Implementing%20ImageView%20transition%20between%20activities%20for%20pre--Lollipop%20devices.-blue.svg)](https://medium.com/@v.danylo/implementing-imageview-transition-between-activities-for-pre-lollipop-devices-8b24bc387a2a)\n\n\n\n# License\n\nCopyright 2016 Danylo Volokh\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "app/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 23\n    buildToolsVersion \"23.0.2\"\n\n    defaultConfig {\n        applicationId \"com.volokh.danylo.imagetransition\"\n        minSdkVersion 15\n        targetSdkVersion 23\n        versionCode 1\n        versionName \"1.0\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\ndependencies {\n    compile fileTree(dir: 'libs', include: ['*.jar'])\n    testCompile 'junit:junit:4.12'\n    compile 'com.android.support:appcompat-v7:23.1.1'\n    compile 'com.android.support:design:23.1.1'\n    compile 'com.squareup.picasso:picasso:2.5.2'\n    compile 'com.squareup:otto:1.3.8'\n\n}\n"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in D:\\Projects\\android_sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "app/src/androidTest/java/com/volokh/danylo/imagetransition/ApplicationTest.java",
    "content": "package com.volokh.danylo.imagetransition;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * <a href=\"http://d.android.com/tools/testing/testing_android.html\">Testing Fundamentals</a>\n */\npublic class ApplicationTest extends ApplicationTestCase<Application> {\n    public ApplicationTest() {\n        super(Application.class);\n    }\n}"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.volokh.danylo.imagetransition\">\n\n    <application\n\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:theme=\"@style/AppTheme\">\n        <activity\n            android:name=\".activities_v21.ImagesListActivity_v21\"\n            android:theme=\"@style/ImagesListActivityStyle_v21\">\n\n        </activity>\n\n        <activity\n            android:name=\".activities_v21.ImageDetailsActivity_v21\"\n            android:theme=\"@style/ImagesDetailsActivityStyle_v21\">\n        </activity>\n\n        <activity\n            android:name=\".activities.ImagesListActivity\"\n            android:theme=\"@style/ImagesListActivityStyle\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n\n        <activity\n            android:name=\".activities.ImageDetailsActivity\"\n            android:theme=\"@style/ImagesDetailsActivityStyle\">\n        </activity>\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "app/src/main/java/com/volokh/danylo/imagetransition/ImageFilesCreateLoader.java",
    "content": "package com.volokh.danylo.imagetransition;\n\nimport android.app.LoaderManager;\nimport android.content.AsyncTaskLoader;\nimport android.content.Context;\nimport android.content.Loader;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.util.SparseArray;\n\nimport com.volokh.danylo.imagetransition.adapter.Image;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\n/**\n * Created by danylo.volokh on 3/12/16.\n */\npublic class ImageFilesCreateLoader implements LoaderManager.LoaderCallbacks<List<File>> {\n\n    private static final String TAG = ImageFilesCreateLoader.class.getSimpleName();\n\n    private static final String AVATAR = \"avatar.png\";\n    private static final String BEST_APP_OF_THE_YEAR = \"best_app_of_the_year.png\";\n    private static final String HB_DANYLO = \"hb_danylo.png\";\n    private static final String LENA_DANYLO_VIKTOR = \"lena_danylo_victor.png\";\n    private static final String VENECIA_LENA_DANYLO_OLYA = \"venecia_lena_danylo_olya.png\";\n    private static final String DANYLO = \"danylo.jng\";\n\n    private static final SparseArray<String> mImagesResourcesList = new SparseArray<>();\n\n    static {\n        mImagesResourcesList.put(R.raw.avatar, AVATAR);\n        mImagesResourcesList.put(R.raw.best_app_of_the_year, BEST_APP_OF_THE_YEAR);\n        mImagesResourcesList.put(R.raw.hb_danylo, HB_DANYLO);\n        mImagesResourcesList.put(R.raw.lena_danylo_victor, LENA_DANYLO_VIKTOR);\n        mImagesResourcesList.put(R.raw.venecia_lena_danylo_olya, VENECIA_LENA_DANYLO_OLYA);\n        mImagesResourcesList.put(R.raw.danylo, DANYLO);\n    }\n\n    private final Context mContext;\n    private LoadFinishedCallback mLoadFinishedCallback;\n\n    public interface LoadFinishedCallback{\n        void onLoadFinished(List<Image> imagesList);\n    }\n\n    public ImageFilesCreateLoader(Context context, LoadFinishedCallback loadFinishedCallback) {\n        mContext = context;\n        mLoadFinishedCallback = loadFinishedCallback;\n    }\n\n    @Override\n    public Loader<List<File>> onCreateLoader(int id, Bundle args) {\n        return new AsyncTaskLoader<List<File>>(mContext) {\n            @Override\n            public List<File> loadInBackground() {\n                Log.v(TAG, \"loadInBackground\");\n                List<File> resultList = new ArrayList<>();\n\n                for (int resourceIndex = 0; resourceIndex < mImagesResourcesList.size(); resourceIndex++) {\n\n                    writeResourceToFile(resultList, resourceIndex);\n                }\n                Log.v(TAG, \"loadInBackground, resultList \" + resultList);\n\n                return resultList;\n            }\n        };\n    }\n\n    private void writeResourceToFile(List<File> resultList, int resourceIndex) {\n        File fileDir = mContext.getCacheDir();\n\n        List<String> existingFiles = Arrays.asList(fileDir.list());\n\n        String fileName = mImagesResourcesList.valueAt(resourceIndex);\n        File file = new File(fileDir + File.separator + fileName);\n        if (existingFiles.contains(fileName)) {\n            resultList.add(file);\n        } else {\n            saveIntoFile(file, mImagesResourcesList.keyAt(resourceIndex));\n            resultList.add(file);\n        }\n    }\n\n    private void saveIntoFile(File file, Integer resource) {\n        try {\n            InputStream inputStream = mContext.getResources().openRawResource(resource);\n            FileOutputStream fileOutputStream = new FileOutputStream(file);\n\n            byte buf[] = new byte[1024];\n            int len;\n            while ((len = inputStream.read(buf)) > 0) {\n                fileOutputStream.write(buf, 0, len);\n            }\n\n            fileOutputStream.close();\n            inputStream.close();\n        } catch (IOException e1) {\n        }\n    }\n\n    @Override\n    public void onLoadFinished(Loader<List<File>> loader, List<File> data) {\n        Log.v(TAG, \"onLoadFinished, data \" + data);\n\n        fillImageList(data);\n    }\n\n    private void fillImageList(List<File> data) {\n        List<Image> imagesList = new ArrayList<>();\n\n        int imageId = 0;\n        int times = 7;\n        while(--times > 0){\n            for (int i = 0; i < data.size(); i++, imageId++) {\n                File file = data.get(i);\n\n                Log.v(TAG, \"fillImageList, imageId \" + imageId);\n\n                imagesList.add(new Image(imageId, file));\n\n            }\n        }\n        mLoadFinishedCallback.onLoadFinished(imagesList);\n\n    }\n\n    @Override\n    public void onLoaderReset(Loader<List<File>> loader) {\n\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/volokh/danylo/imagetransition/activities/ImageDetailsActivity.java",
    "content": "package com.volokh.danylo.imagetransition.activities;\n\nimport android.animation.AnimatorSet;\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.ViewTreeObserver;\nimport android.widget.FrameLayout;\nimport android.widget.ImageView;\n\nimport com.squareup.otto.Bus;\nimport com.squareup.picasso.Callback;\nimport com.squareup.picasso.Picasso;\nimport com.volokh.danylo.imagetransition.animations.EnterScreenAnimations;\nimport com.volokh.danylo.imagetransition.animations.ExitScreenAnimations;\nimport com.volokh.danylo.imagetransition.event_bus.ChangeImageThumbnailVisibility;\nimport com.volokh.danylo.imagetransition.event_bus.EventBusCreator;\nimport com.volokh.danylo.imagetransition.R;\n\nimport java.io.File;\n\n/**\n * Created by danylo.volokh on 2/21/2016.\n */\npublic class ImageDetailsActivity extends Activity {\n\n    private static final String IMAGE_FILE_KEY = \"IMAGE_FILE_KEY\";\n    private static final String KEY_THUMBNAIL_INIT_TOP_POSITION = \"KEY_THUMBNAIL_INIT_TOP_POSITION\";\n    private static final String KEY_THUMBNAIL_INIT_LEFT_POSITION = \"KEY_THUMBNAIL_INIT_LEFT_POSITION\";\n    private static final String KEY_THUMBNAIL_INIT_WIDTH = \"KEY_THUMBNAIL_INIT_WIDTH\";\n    private static final String KEY_THUMBNAIL_INIT_HEIGHT = \"KEY_THUMBNAIL_INIT_HEIGHT\";\n    private static final String KEY_SCALE_TYPE = \"KEY_SCALE_TYPE\";\n\n    private static final String TAG = ImageDetailsActivity.class.getSimpleName();\n\n    private static final long IMAGE_TRANSLATION_DURATION = 3000;\n\n    private ImageView mEnlargedImage;\n    private ImageView mTransitionImage;\n\n    private Picasso mImageDownloader;\n\n    private final Bus mBus = EventBusCreator.defaultEventBus();\n\n    private AnimatorSet mExitingAnimation;\n\n    private EnterScreenAnimations mEnterScreenAnimations;\n    private ExitScreenAnimations mExitScreenAnimations;\n\n    @Override\n    protected void onCreate(final Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        overridePendingTransition(0, 0);\n\n        setContentView(R.layout.image_details_activity_layout);\n        mEnlargedImage = (ImageView) findViewById(R.id.enlarged_image);\n\n        mImageDownloader = Picasso.with(this);\n\n        File imageFile = (File) getIntent().getSerializableExtra(IMAGE_FILE_KEY);\n\n        final View mainContainer = findViewById(R.id.main_container);\n\n        if(savedInstanceState == null){\n            // We entered activity for the first time.\n            // Initialize Image view that will be transitioned\n            initializeTransitionView();\n        } else {\n            // Activity is retrieved. Main container is invisible. Make it visible\n            mainContainer.setAlpha(1.0f);\n        }\n\n        mEnterScreenAnimations = new EnterScreenAnimations(mTransitionImage, mEnlargedImage, mainContainer);\n        mExitScreenAnimations = new ExitScreenAnimations(mTransitionImage, mEnlargedImage, mainContainer);\n\n        initializeEnlargedImageAndRunAnimation(savedInstanceState, imageFile);\n    }\n\n    /**\n     * This method waits for the main \"big\" image is loaded.\n     * And then if activity is started for the first time - it runs \"entering animation\"\n     *\n     * Activity is entered fro the first time if saveInstanceState is null\n     *\n     */\n\n    private void initializeEnlargedImageAndRunAnimation(final Bundle savedInstanceState, File imageFile) {\n        Log.v(TAG, \"initializeEnlargedImageAndRunAnimation\");\n\n        mImageDownloader.load(imageFile).into(mEnlargedImage, new Callback() {\n\n            /**\n             * Image is loaded when this method is called\n             */\n            @Override\n            public void onSuccess() {\n                Log.v(TAG, \"onSuccess, mEnlargedImage\");\n\n                // In this callback we already have image set into ImageView and we can use it's Matrix for animation\n                // But we have to wait for final measurements. We use OnPreDrawListener to be sure everything is measured\n\n                if (savedInstanceState == null) {\n                    // if savedInstanceState is null activity is started for the first time.\n                    // run the animation\n                    runEnteringAnimation();\n                } else {\n                    // activity was retrieved from recent apps. No animation needed, just load the image\n                }\n            }\n\n            @Override\n            public void onError() {\n                // CAUTION: on error is not handled. If OutOfMemory emerged during image loading we have to handle it here\n                Log.v(TAG, \"onError, mEnlargedImage\");\n            }\n        });\n    }\n\n    /**\n     * This method does very tricky part:\n     *\n     * It sets up {@link android.view.ViewTreeObserver.OnPreDrawListener}\n     * When onPreDraw() method is called the layout is already measured.\n     * It means that we can use locations of images on the screen at tis point.\n     *\n     * 1. When first frame is rendered we start animation.\n     * 2. We just let second frame to render\n     * 3. Make a view on the previous screen invisible and remove onPreDrawListener\n     *\n     * Why do we do that:\n     * The Android rendering system is double-buffered.\n     * Similar technique is used in the SDK. See here : {@link android.app.EnterTransitionCoordinator#startSharedElementTransition}\n     *\n     * You can read more about it here : https://source.android.com/devices/graphics/architecture.html\n     *\n     */\n    private void runEnteringAnimation() {\n        Log.v(TAG, \"runEnteringAnimation, addOnPreDrawListener\");\n\n        mEnlargedImage.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {\n\n            int mFrames = 0;\n\n            @Override\n            public boolean onPreDraw() {\n                // When this method is called we already have everything laid out and measured so we can start our animation\n                Log.v(TAG, \"onPreDraw, mFrames \" + mFrames);\n\n                switch (mFrames++) {\n                    case 0:\n                        /**\n                         * 1. start animation on first frame\n                         */\n                        final int[] finalLocationOnTheScreen = new int[2];\n                        mEnlargedImage.getLocationOnScreen(finalLocationOnTheScreen);\n\n                        mEnterScreenAnimations.playEnteringAnimation(\n                                finalLocationOnTheScreen[0], // left\n                                finalLocationOnTheScreen[1], // top\n                                mEnlargedImage.getWidth(),\n                                mEnlargedImage.getHeight());\n\n                        return true;\n                    case 1:\n                        /**\n                         * 2. Do nothing. We just draw this frame\n                         */\n\n                        return true;\n                }\n                /**\n                 * 3.\n                 * Make view on previous screen invisible on after this drawing frame\n                 * Here we ensure that animated view will be visible when we make the viw behind invisible\n                 */\n                Log.v(TAG, \"run, onAnimationStart\");\n                mBus.post(new ChangeImageThumbnailVisibility(false));\n\n                mEnlargedImage.getViewTreeObserver().removeOnPreDrawListener(this);\n\n                Log.v(TAG, \"onPreDraw, << mFrames \" + mFrames);\n\n                return true;\n            }\n        });\n    }\n\n\n    private void initializeTransitionView() {\n        Log.v(TAG, \"initializeTransitionView\");\n\n        FrameLayout androidContent = (FrameLayout) getWindow().getDecorView().findViewById(android.R.id.content);\n        mTransitionImage = new ImageView(this);\n        androidContent.addView(mTransitionImage);\n\n        Bundle bundle = getIntent().getExtras();\n\n        int thumbnailTop = bundle.getInt(KEY_THUMBNAIL_INIT_TOP_POSITION)\n                - getStatusBarHeight();\n        int thumbnailLeft = bundle.getInt(KEY_THUMBNAIL_INIT_LEFT_POSITION);\n        int thumbnailWidth = bundle.getInt(KEY_THUMBNAIL_INIT_WIDTH);\n\n        int thumbnailHeight = bundle.getInt(KEY_THUMBNAIL_INIT_HEIGHT);\n\n        ImageView.ScaleType scaleType = (ImageView.ScaleType) bundle.getSerializable(KEY_SCALE_TYPE);\n\n        Log.v(TAG, \"initInitialThumbnail, thumbnailTop [\" + thumbnailTop + \"]\");\n        Log.v(TAG, \"initInitialThumbnail, thumbnailLeft [\" + thumbnailLeft + \"]\");\n        Log.v(TAG, \"initInitialThumbnail, thumbnailWidth [\" + thumbnailWidth + \"]\");\n        Log.v(TAG, \"initInitialThumbnail, thumbnailHeight [\" + thumbnailHeight + \"]\");\n        Log.v(TAG, \"initInitialThumbnail, scaleType \" + scaleType);\n\n        // We set initial margins to the view so that it was situated at exact same spot that view from the previous screen were.\n        FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mTransitionImage.getLayoutParams();\n        layoutParams.height = thumbnailHeight;\n        layoutParams.width = thumbnailWidth;\n        layoutParams.setMargins(thumbnailLeft, thumbnailTop, 0, 0);\n\n        File imageFile = (File) getIntent().getSerializableExtra(IMAGE_FILE_KEY);\n        mTransitionImage.setScaleType(scaleType);\n\n        mImageDownloader.load(imageFile).noFade().into(mTransitionImage);\n    }\n\n    private int getStatusBarHeight() {\n        int result = 0;\n        int resourceId = getResources().getIdentifier(\"status_bar_height\", \"dimen\", \"android\");\n        if (resourceId > 0) {\n            result = getResources().getDimensionPixelSize(resourceId);\n        }\n        return result;\n    }\n\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n        // prevent leaking activity if image was not loaded yet\n        Picasso.with(this).cancelRequest(mEnlargedImage);\n    }\n\n    @Override\n    public void onBackPressed() {\n//        We don't call super to leave this activity on the screen when back is pressed\n//        super.onBackPressed();\n\n        Log.v(TAG, \"onBackPressed\");\n\n        mEnterScreenAnimations.cancelRunningAnimations();\n\n        Log.v(TAG, \"onBackPressed, mExitingAnimation \" + mExitingAnimation);\n\n        Bundle initialBundle = getIntent().getExtras();\n        int toTop = initialBundle.getInt(KEY_THUMBNAIL_INIT_TOP_POSITION);\n        int toLeft = initialBundle.getInt(KEY_THUMBNAIL_INIT_LEFT_POSITION);\n        int toWidth = initialBundle.getInt(KEY_THUMBNAIL_INIT_WIDTH);\n        int toHeight = initialBundle.getInt(KEY_THUMBNAIL_INIT_HEIGHT);\n\n        mExitScreenAnimations.playExitAnimations(\n                toTop,\n                toLeft,\n                toWidth,\n                toHeight,\n                mEnterScreenAnimations.getInitialThumbnailMatrixValues());\n    }\n\n    public static Intent getStartIntent(Activity activity, File imageFile, int left, int top, int width, int height, ImageView.ScaleType scaleType) {\n        Log.v(TAG, \"getStartIntent, imageFile \" + imageFile);\n\n        Intent startIntent = new Intent(activity, ImageDetailsActivity.class);\n        startIntent.putExtra(IMAGE_FILE_KEY, imageFile);\n\n        startIntent.putExtra(KEY_THUMBNAIL_INIT_TOP_POSITION, top);\n        startIntent.putExtra(KEY_THUMBNAIL_INIT_LEFT_POSITION, left);\n        startIntent.putExtra(KEY_THUMBNAIL_INIT_WIDTH, width);\n        startIntent.putExtra(KEY_THUMBNAIL_INIT_HEIGHT, height);\n        startIntent.putExtra(KEY_SCALE_TYPE, scaleType);\n\n        return startIntent;\n    }\n\n    @Override\n    protected void onResume() {\n        super.onResume();\n        Log.v(TAG, \"onResume\");\n\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/volokh/danylo/imagetransition/activities/ImagesListActivity.java",
    "content": "package com.volokh.danylo.imagetransition.activities;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.graphics.Rect;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.support.v7.widget.GridLayoutManager;\nimport android.support.v7.widget.RecyclerView;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.Button;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport com.squareup.otto.Bus;\nimport com.squareup.otto.Subscribe;\nimport com.squareup.picasso.Picasso;\nimport com.volokh.danylo.imagetransition.event_bus.EventBusCreator;\nimport com.volokh.danylo.imagetransition.ImageFilesCreateLoader;\nimport com.volokh.danylo.imagetransition.adapter.ImagesAdapter;\nimport com.volokh.danylo.imagetransition.activities_v21.ImagesListActivity_v21;\nimport com.volokh.danylo.imagetransition.R;\nimport com.volokh.danylo.imagetransition.event_bus.ChangeImageThumbnailVisibility;\nimport com.volokh.danylo.imagetransition.adapter.Image;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class ImagesListActivity extends Activity implements ImagesAdapter.ImagesAdapterCallback {\n\n    private static final String TAG = ImagesListActivity.class.getSimpleName();\n\n    private static final String IMAGE_DETAILS_IMAGE_MODEL = \"IMAGE_DETAILS_IMAGE_MODEL\";\n\n    private final List<Image> mImagesList = new ArrayList<>();\n\n    private static final int SPAN_COUNT = 2;\n\n    private Picasso mImageDownloader;\n\n    private RecyclerView mRecyclerView;\n\n    private GridLayoutManager mLayoutManager;\n\n    private ImagesAdapter mAdapter;\n\n    private Image mImageDetailsImageModel;\n\n    private final Bus mBus = EventBusCreator.defaultEventBus();\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n        setContentView(R.layout.images_list);\n\n        initializeImagesDownloader();\n\n        initializeAdapter();\n\n        initializeImages();\n\n        initializeRecyclerView();\n\n        initializeTitle();\n\n        initializeSavedImageModel(savedInstanceState);\n\n        initializeSwitchButton();\n    }\n\n    private void initializeImagesDownloader() {\n        mImageDownloader = Picasso.with(this);\n    }\n\n    private void initializeAdapter() {\n        mAdapter = new ImagesAdapter(this, mImagesList, mImageDownloader, SPAN_COUNT);\n    }\n\n    private void initializeImages() {\n        // load images\n        getLoaderManager().initLoader(0, null, new ImageFilesCreateLoader(this, new ImageFilesCreateLoader.LoadFinishedCallback() {\n            @Override\n            public void onLoadFinished(List<Image> imagesList) {\n                mImagesList.addAll(imagesList);\n                mAdapter.notifyDataSetChanged();\n            }\n        })).forceLoad();\n    }\n\n    private void initializeSwitchButton() {\n        Button switchButton = (Button) findViewById(R.id.switch_to);\n\n        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){\n            switchButton.setVisibility(View.VISIBLE);\n            switchButton.setText(\"Switch to Lollipop List Activity\");\n            switchButton.setOnClickListener(new View.OnClickListener() {\n                @Override\n                public void onClick(View view) {\n\n                    ImagesListActivity.this.finish();\n\n                    Intent startActivity_v21_intent = new Intent(ImagesListActivity.this, ImagesListActivity_v21.class);\n                    startActivity(startActivity_v21_intent);\n\n                }\n            });\n        }\n    }\n\n    private void initializeSavedImageModel(Bundle savedInstanceState) {\n        Log.v(TAG, \"initializeSavedImageModel, savedInstanceState \" + savedInstanceState);\n\n        if(savedInstanceState != null){\n            mImageDetailsImageModel = savedInstanceState.getParcelable(IMAGE_DETAILS_IMAGE_MODEL);\n        }\n\n        if(mImageDetailsImageModel != null) {\n            updateModel(mImageDetailsImageModel);\n        }\n    }\n\n    private void initializeTitle() {\n        TextView title = (TextView) findViewById(R.id.title);\n        title.setText(\"List Activity Ice Cream Sandwich\");\n    }\n\n    private void initializeRecyclerView() {\n        mRecyclerView = (RecyclerView) findViewById(R.id.accounts_recycler_view);\n        mLayoutManager = new GridLayoutManager(this, SPAN_COUNT);\n        mRecyclerView.setLayoutManager(mLayoutManager);\n        mRecyclerView.setAdapter(mAdapter);\n        // we don't need animation when items are hidden or shown\n        mRecyclerView.setItemAnimator(null);\n    }\n\n    @Override\n    protected void onStart() {\n        super.onStart();\n        Log.v(TAG, \"onStart\");\n        mBus.register(this);\n\n    }\n\n    @Override\n    protected void onStop() {\n        super.onStop();\n        Log.v(TAG, \"onStop\");\n        mBus.unregister(this);\n    }\n\n    @Override\n    protected void onResume() {\n        super.onResume();\n        Log.v(TAG, \"onResume\");\n\n        if(mRecyclerView.getChildCount() > 1){\n            Log.v(TAG, \"onResume, \" + mRecyclerView.getChildAt(4).getVisibility());\n        }\n    }\n\n    /**\n     * This method is called when event is sent from {@link ImageDetailsActivity}\n     * via event bus.\n     *\n     * When it's called it means that transition animation started and we have to hide the image on this activity in order to look\n     * like image from here is transitioned to the new screen\n     *\n     */\n    @Subscribe\n    public void hideImageThumbnail(ChangeImageThumbnailVisibility message){\n        Log.v(TAG, \">> hideImageThumbnail\");\n\n        mImageDetailsImageModel.setVisibility(message.isVisible());\n\n        updateModel(mImageDetailsImageModel);\n\n        Log.v(TAG, \"<< hideImageThumbnail\");\n    }\n\n    /**\n     * This method basically changes visibility of concrete item\n     */\n    private void updateModel(Image imageToUpdate) {\n        Log.v(TAG, \"updateModel, imageToUpdate \" + imageToUpdate);\n        for (Image image : mImagesList) {\n\n            if(image.equals(imageToUpdate)){\n                Log.v(TAG, \"updateModel, found imageToUpdate \" + imageToUpdate);\n                image.setVisibility(imageToUpdate.isVisible());\n                break;\n            }\n        }\n        int index = mImagesList.indexOf(imageToUpdate);\n        Log.v(TAG, \"updateModel, index \" + index);\n\n        mAdapter.notifyItemChanged(index);\n\n        /**\n         * For some reason recycler view is not always redrawn when adapter updated.\n         * onBindViewHolder is called but image doesn't disappear from screen\n         * That's why we have to do this invalidation\n         */\n        Rect dirty = new Rect();\n        View viewAtPosition = mLayoutManager.findViewByPosition(index);\n        viewAtPosition.getDrawingRect(dirty);\n        mRecyclerView.invalidate(dirty);\n    }\n\n    @Override\n    protected void onSaveInstanceState(Bundle outState) {\n        super.onSaveInstanceState(outState);\n        outState.putParcelable(IMAGE_DETAILS_IMAGE_MODEL, mImageDetailsImageModel);\n    }\n\n    @Override\n    public void enterImageDetails(String sharedImageTransitionName, File imageFile, final ImageView image, Image imageModel) {\n        Log.v(TAG, \"enterImageDetails, imageFile \" + imageFile);\n        Log.v(TAG, \"enterImageDetails, image.getScaleType() \" + image.getScaleType());\n\n        /**\n         * We store this model for two purposes:\n         * 1. When image on the next screen will be transitioned we have to hide the view\n         * representation of this model in order it to look like this exact view is transitioned.\n         * Transitioned view will leave empty space behind it\n         *\n         * 2. Activity might be destroyed at some point and we have to store this information to restore view\n         * visibility when activity is recreated.\n         */\n        mImageDetailsImageModel = imageModel;\n\n        int[] screenLocation = new int[2];\n        image.getLocationInWindow(screenLocation);\n\n        Intent startIntent = ImageDetailsActivity.getStartIntent(this, imageFile,\n                screenLocation[0],\n                screenLocation[1],\n                image.getWidth(),\n                image.getHeight(),\n                image.getScaleType());\n\n        startActivity(startIntent);\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/volokh/danylo/imagetransition/activities_v21/ImageDetailsActivity_v21.java",
    "content": "package com.volokh.danylo.imagetransition.activities_v21;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.support.v4.view.ViewCompat;\nimport android.view.View;\nimport android.widget.ImageView;\n\nimport com.squareup.picasso.Picasso;\nimport com.volokh.danylo.imagetransition.R;\n\nimport java.io.File;\n\n/**\n * Created by danylo.volokh on 2/21/2016.\n */\npublic class ImageDetailsActivity_v21 extends Activity{\n\n    public static final String SHARED_ELEMENT_IMAGE_KEY = \"SHARED_ELEMENT_IMAGE_KEY\";\n    public static final String IMAGE_FILE_KEY = \"IMAGE_FILE_KEY\";\n\n    private ImageView mImage;\n\n    private Picasso mImageDownloader;\n\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.image_details_activity_layout);\n\n        mImage = (ImageView) findViewById(R.id.enlarged_image);\n        mImage.setVisibility(View.VISIBLE);\n\n        String imageTransitionName = getIntent().getStringExtra(SHARED_ELEMENT_IMAGE_KEY);\n        ViewCompat.setTransitionName(mImage, imageTransitionName);\n\n        View mainContainer = findViewById(R.id.main_container);\n        mainContainer.setAlpha(1.f);\n        mImageDownloader = Picasso.with(this);\n\n        File imageFile = (File) getIntent().getSerializableExtra(IMAGE_FILE_KEY);\n\n        mImageDownloader.load(imageFile).into(mImage);\n\n    }\n\n    public static Intent getStartIntent(Activity activity, String sharedImageTransitionName, File imageFile) {\n        Intent startIntent = new Intent(activity, ImageDetailsActivity_v21.class);\n        startIntent.putExtra(SHARED_ELEMENT_IMAGE_KEY, sharedImageTransitionName);\n        startIntent.putExtra(IMAGE_FILE_KEY, imageFile);\n        return startIntent;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/volokh/danylo/imagetransition/activities_v21/ImagesListActivity_v21.java",
    "content": "package com.volokh.danylo.imagetransition.activities_v21;\n\nimport android.app.Activity;\nimport android.app.ActivityOptions;\nimport android.content.Intent;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.support.v7.widget.GridLayoutManager;\nimport android.support.v7.widget.RecyclerView;\nimport android.transition.ChangeImageTransform;\nimport android.view.View;\nimport android.widget.Button;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport com.squareup.picasso.Picasso;\nimport com.volokh.danylo.imagetransition.ImageFilesCreateLoader;\nimport com.volokh.danylo.imagetransition.adapter.ImagesAdapter;\nimport com.volokh.danylo.imagetransition.R;\nimport com.volokh.danylo.imagetransition.activities.ImagesListActivity;\nimport com.volokh.danylo.imagetransition.adapter.Image;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class ImagesListActivity_v21 extends Activity implements ImagesAdapter.ImagesAdapterCallback {\n\n    private static final String TAG = ImagesListActivity_v21.class.getSimpleName();\n\n    private final List<Image> mImagesList = new ArrayList<>();\n\n    private static final int SPAN_COUNT = 2;\n\n    private Picasso mImageDownloader;\n    private RecyclerView mRecyclerView;\n    private ImagesAdapter mAdapter;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.images_list);\n\n        mImageDownloader = Picasso.with(this);\n\n        mAdapter = new ImagesAdapter(this, mImagesList, mImageDownloader, SPAN_COUNT);\n\n        getLoaderManager().initLoader(0, null, new ImageFilesCreateLoader(this, new ImageFilesCreateLoader.LoadFinishedCallback() {\n            @Override\n            public void onLoadFinished(List<Image> imagesList) {\n                mImagesList.addAll(imagesList);\n                mAdapter.notifyDataSetChanged();\n            }\n        })).forceLoad();\n\n        mRecyclerView = (RecyclerView) findViewById(R.id.accounts_recycler_view);\n\n        mRecyclerView.setLayoutManager(new GridLayoutManager(this, SPAN_COUNT));\n        mRecyclerView.setAdapter(mAdapter);\n\n        TextView title = (TextView) findViewById(R.id.title);\n        title.setText(\"List Activity Lollipop\");\n\n        Button switchButton = (Button) findViewById(R.id.switch_to);\n\n        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){\n            switchButton.setVisibility(View.VISIBLE);\n            switchButton.setText(\"Switch to Ice Cream Sandwich List Activity\");\n            switchButton.setOnClickListener(new View.OnClickListener() {\n                @Override\n                public void onClick(View view) {\n\n                    ImagesListActivity_v21.this.finish();\n\n                    Intent startActivityIntent = new Intent(ImagesListActivity_v21.this, ImagesListActivity.class);\n                    startActivity(startActivityIntent);\n\n                }\n            });\n        }\n    }\n\n\n\n\n    @Override\n    public void enterImageDetails(String sharedImageTransitionName, File imageFile, ImageView image, Image imageModel) {\n        ActivityOptions activityOptions =\n                ActivityOptions.makeSceneTransitionAnimation(this, image, sharedImageTransitionName);\n\n        getWindow().setSharedElementEnterTransition(new ChangeImageTransform(this, null));\n\n        Intent startIntent = ImageDetailsActivity_v21.getStartIntent(this, sharedImageTransitionName, imageFile);\n        startActivity(startIntent, activityOptions.toBundle());\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/volokh/danylo/imagetransition/adapter/Image.java",
    "content": "package com.volokh.danylo.imagetransition.adapter;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\nimport java.io.File;\n\n/**\n * Created by danylo.volokh on 2/21/2016.\n *\n * We use Parcelable in order it to store this model in bundle.\n *\n * NOTE: it is better to use some cache for models.(Maybe ORM)\n * Parcelable is more verbose then Serializable, but we use it because #PerfMatters\n */\npublic class Image implements Parcelable{\n\n    public final int imageId;\n    public final File imageFile;\n\n    /**\n     * By default the image is visible but for the purpose of the animation it will be changed to invisible at some time.\n     */\n    private boolean mImageIsVisible = true;\n\n    public Image(int imageId, File imageFile) {\n        this.imageId = imageId;\n        this.imageFile = imageFile;\n    }\n\n    protected Image(Parcel in) {\n        imageId = in.readInt();\n        imageFile = (File) in.readSerializable();\n        mImageIsVisible = in.readByte() != 0;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n\n        Image image = (Image) o;\n\n        return imageId == image.imageId;\n    }\n\n    @Override\n    public int hashCode() {\n        return imageId;\n    }\n\n    /**\n     * This is generated by studio\n     */\n    @Override\n    public String toString() {\n        return \"Image{\" +\n                \"imageId=\" + imageId +\n                \", imageFile=\" + imageFile +\n                \", mImageIsVisible=\" + mImageIsVisible +\n                '}';\n    }\n\n    public static final Creator<Image> CREATOR = new Creator<Image>() {\n        @Override\n        public Image createFromParcel(Parcel in) {\n            return new Image(in);\n        }\n\n        @Override\n        public Image[] newArray(int size) {\n            return new Image[size];\n        }\n    };\n\n    @Override\n    public int describeContents() {\n        return 0;\n    }\n\n    @Override\n    public void writeToParcel(Parcel dest, int flags) {\n        dest.writeInt(imageId);\n        dest.writeSerializable(imageFile);\n        dest.writeByte((byte) (mImageIsVisible ? 1 : 0));\n    }\n\n    public boolean isVisible() {\n        return mImageIsVisible;\n    }\n\n    public void setVisibility(boolean isVisible){\n        mImageIsVisible = isVisible;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/volokh/danylo/imagetransition/adapter/ImagesAdapter.java",
    "content": "package com.volokh.danylo.imagetransition.adapter;\n\nimport android.support.v4.view.ViewCompat;\nimport android.support.v7.widget.RecyclerView;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.ImageView;\n\nimport com.squareup.picasso.Picasso;\nimport com.volokh.danylo.imagetransition.R;\n\nimport java.io.File;\nimport java.util.List;\n\n/**\n * Created by danylo.volokh on 2/19/2016.\n *\n * This adapter class is for the list of images. There is 6 types of item types.\n * Each on for different {@link android.widget.ImageView.ScaleType}\n */\npublic class ImagesAdapter extends RecyclerView.Adapter<ImagesViewHolder> {\n\n    private static final String TAG = ImagesAdapter.class.getSimpleName();\n\n    private final ImagesAdapterCallback mImagesAdapterCallback;\n    private final List<Image> mImagesList;\n    private final Picasso mImageDownloader;\n    private final int mSpanCount;\n\n    public interface ImagesAdapterCallback{\n        void enterImageDetails(String sharedImageTransitionName, File imageFile, ImageView image, Image imageModel);\n    }\n\n    public ImagesAdapter(ImagesAdapterCallback imagesAdapterCallback, List<Image> imagesList, Picasso imageDownloader, int spanCount) {\n\n        mImagesAdapterCallback = imagesAdapterCallback;\n        mImagesList = imagesList;\n        mImageDownloader = imageDownloader;\n        mSpanCount = spanCount;\n    }\n\n    public static String createSharedImageTransitionName(int position){\n        return \"SharedImage\" + position;\n    }\n\n    @Override\n    public ImagesViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {\n        View item = LayoutInflater.from(parent.getContext()).inflate(R.layout.image_item, parent, false);\n        RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) item.getLayoutParams();\n        layoutParams.height = item.getResources().getDisplayMetrics().widthPixels / mSpanCount;\n\n        ImagesViewHolder viewHolder = new ImagesViewHolder(item);\n\n        /**\n         * Depends on View type we set the according {@link android.widget.ImageView.ScaleType\n         */\n\n        if(viewType == 0) {\n            viewHolder.image.setScaleType(ImageView.ScaleType.CENTER);\n        }\n\n        if(viewType == 1) {\n            viewHolder.image.setScaleType(ImageView.ScaleType.CENTER_CROP);\n        }\n\n        if(viewType == 2) {\n            viewHolder.image.setScaleType(ImageView.ScaleType.CENTER_INSIDE);\n        }\n\n        if(viewType == 3) {\n            viewHolder.image.setScaleType(ImageView.ScaleType.FIT_CENTER);\n        }\n\n        if(viewType == 4) {\n            viewHolder.image.setScaleType(ImageView.ScaleType.FIT_XY);\n        }\n\n        if(viewType == 5) {\n            viewHolder.image.setScaleType(ImageView.ScaleType.MATRIX);\n        }\n\n        return viewHolder;\n    }\n\n    @Override\n    public void onBindViewHolder(final ImagesViewHolder holder, final int position) {\n        final Image image = mImagesList.get(position);\n        Log.v(TAG, \"onBindViewHolder holder.imageFile \" + image.imageFile);\n\n        Log.v(TAG, \"onBindViewHolder holder.image \" + holder.image);\n        Log.v(TAG, \"onBindViewHolder holder.matrix \" + holder.image.getMatrix());\n        Log.v(TAG, \"onBindViewHolder holder.scaleType \" + holder.image.getScaleType());\n\n        // set transition name for sdk 21 shared element transition\n        final String sharedImageTransitionName = createSharedImageTransitionName(position);\n        ViewCompat.setTransitionName(holder.image, sharedImageTransitionName);\n\n        Log.v(TAG, \"onBindViewHolder isVisible \" + image.isVisible());\n        Log.v(TAG, \"onBindViewHolder isVisible \" + holder.image.getVisibility());\n\n        if(image.isVisible()){\n            mImageDownloader.load(image.imageFile).into(holder.image);\n            holder.image.setVisibility(View.VISIBLE);\n        } else {\n            /** in purpose of animation image might become invisible */\n            holder.image.setVisibility(View.INVISIBLE);\n        }\n        Log.v(TAG, \"onBindViewHolder isVisible \" + holder.image.getVisibility());\n\n\n        holder.image.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                Log.v(TAG, \"onClick image \" + holder.image );\n                Log.v(TAG, \"onClick matrix \" + holder.image.getMatrix() );\n\n                mImagesAdapterCallback.enterImageDetails(sharedImageTransitionName, image.imageFile, holder.image, image);\n\n            }\n        });\n        Log.v(TAG, \"onBindViewHolder position \" + position);\n    }\n\n    @Override\n    public int getItemCount() {\n        return mImagesList.size();\n    }\n\n    /**\n     * We create 6 item types for each {@link android.widget.ImageView.ScaleType} there is,\n     * besides:\n     *      {@link android.widget.ImageView.ScaleType#FIT_START}\n     *      {@link android.widget.ImageView.ScaleType#FIT_END}\n     */\n    @Override\n    public int getItemViewType(int position) {\n\n        if(position % 6 == 0) {\n            return 0;\n        }\n\n        if(position % 5 == 0) {\n            return 1;\n        }\n\n        if(position % 4 == 0) {\n            return 2;\n        }\n\n        if(position % 3 == 0) {\n            return 3;\n        }\n\n        if(position % 2 == 0) {\n            return 4;\n        }\n\n        return 5;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/volokh/danylo/imagetransition/adapter/ImagesViewHolder.java",
    "content": "package com.volokh.danylo.imagetransition.adapter;\n\nimport android.support.v7.widget.RecyclerView;\nimport android.view.View;\nimport android.widget.ImageView;\n\nimport com.volokh.danylo.imagetransition.R;\n\npublic class ImagesViewHolder extends RecyclerView.ViewHolder{\n\n    public final ImageView image;\n\n    public ImagesViewHolder(View itemView) {\n        super(itemView);\n        image = (ImageView) itemView.findViewById(R.id.image);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/volokh/danylo/imagetransition/animations/EnterScreenAnimations.java",
    "content": "package com.volokh.danylo.imagetransition.animations;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorSet;\nimport android.animation.ObjectAnimator;\nimport android.animation.PropertyValuesHolder;\nimport android.graphics.Matrix;\nimport android.support.annotation.NonNull;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.animation.AccelerateInterpolator;\nimport android.widget.FrameLayout;\nimport android.widget.ImageView;\n\nimport java.util.Arrays;\n\n/**\n * Created by danylo.volokh on 3/16/16.\n */\npublic class EnterScreenAnimations extends ScreenAnimation{\n\n    private static final String TAG = EnterScreenAnimations.class.getSimpleName();\n\n    /**\n     * Image with is initially situated on the place from where transition starts\n     */\n    private final ImageView mImageTo;\n\n    /**\n     * This is image that represents of how should transitioned image look like at the end of transition\n     */\n    private final ImageView mAnimatedImage;\n\n    private View mMainContainer;\n\n    private AnimatorSet mEnteringAnimation;\n\n    /**\n     * This array will contain the data about image matrix of original image.\n     * We will use that matrix to animate image back to original state\n     */\n    private float[] mInitThumbnailMatrixValues = new float[9];\n\n    /**\n     * These values represent the final position of a Image that is translated\n     */\n    private int mToTop;\n    private int mToLeft;\n    private int mToWidth;\n    private int mToHeight;\n\n    public EnterScreenAnimations(ImageView animatedImage, ImageView imageTo, View mainContainer) {\n        super(animatedImage.getContext());\n        mAnimatedImage = animatedImage;\n        mImageTo = imageTo;\n        mMainContainer = mainContainer;\n    }\n\n    /**\n     * This method combines several animations when screen is opened.\n     * 1. Animation of ImageView position and image matrix.\n     * 2. Animation of main container elements - they are fading in after image is animated to position\n     */\n    public void playEnteringAnimation(int left, int top, int width, int height) {\n        Log.v(TAG, \">> playEnteringAnimation\");\n\n        mToLeft = left;\n        mToTop = top;\n        mToWidth = width;\n        mToHeight =  height;\n\n        AnimatorSet imageAnimatorSet = createEnteringImageAnimation();\n\n        Animator mainContainerFadeAnimator = createEnteringFadeAnimator();\n\n        mEnteringAnimation = new AnimatorSet();\n        mEnteringAnimation.setDuration(IMAGE_TRANSLATION_DURATION);\n        mEnteringAnimation.setInterpolator(new AccelerateInterpolator());\n        mEnteringAnimation.addListener(new SimpleAnimationListener() {\n\n            @Override\n            public void onAnimationCancel(Animator animation) {\n                Log.v(TAG, \"onAnimationCancel, mEnteringAnimation \" + mEnteringAnimation);\n                mEnteringAnimation = null;\n            }\n\n            @Override\n            public void onAnimationEnd(Animator animation) {\n\n                Log.v(TAG, \"onAnimationEnd, mEnteringAnimation \" + mEnteringAnimation);\n                if (mEnteringAnimation != null) {\n                    mEnteringAnimation = null;\n\n                    mImageTo.setVisibility(View.VISIBLE);\n                    mAnimatedImage.setVisibility(View.INVISIBLE);\n                } else {\n                    // Animation was cancelled. Do nothing\n                }\n\n            }\n        });\n\n        mEnteringAnimation.playTogether(\n                imageAnimatorSet,\n                mainContainerFadeAnimator\n        );\n\n        mEnteringAnimation.start();\n        Log.v(TAG, \"<< playEnteringAnimation\");\n    }\n\n    /**\n     * Animator returned form this method animates fade in of all other elements on the screen besides ImageView\n     */\n    private ObjectAnimator createEnteringFadeAnimator() {\n        ObjectAnimator fadeInAnimator = ObjectAnimator.ofFloat(mMainContainer, \"alpha\", 0.0f, 1.0f);\n        return fadeInAnimator;\n    }\n    /**\n     * This method creates an animator set of 2 animations:\n     * 1. ImageView position animation when screen is opened\n     * 2. ImageView image matrix animation when screen is opened\n     */\n    @NonNull\n    private AnimatorSet createEnteringImageAnimation() {\n        Log.v(TAG, \">> createEnteringImageAnimation\");\n\n        ObjectAnimator positionAnimator = createEnteringImagePositionAnimator();\n        ObjectAnimator matrixAnimator = createEnteringImageMatrixAnimator();\n\n        AnimatorSet enteringImageAnimation = new AnimatorSet();\n        enteringImageAnimation.playTogether(positionAnimator, matrixAnimator);\n\n        Log.v(TAG, \"<< createEnteringImageAnimation\");\n        return enteringImageAnimation;\n    }\n\n    /**\n     * This method creates an animator that changes ImageView position on the screen.\n     * It will look like view is translated from its position on previous screen to its new position on this screen\n     */\n    @NonNull\n    private ObjectAnimator createEnteringImagePositionAnimator() {\n\n        Log.v(TAG, \"createEnteringImagePositionAnimator\");\n\n        PropertyValuesHolder propertyLeft = PropertyValuesHolder.ofInt(\"left\", mAnimatedImage.getLeft(), mToLeft);\n        PropertyValuesHolder propertyTop = PropertyValuesHolder.ofInt(\"top\", mAnimatedImage.getTop(),\n                mToTop - getStatusBarHeight());\n\n        PropertyValuesHolder propertyRight = PropertyValuesHolder.ofInt(\"right\", mAnimatedImage.getRight(), mToLeft + mToWidth);\n        PropertyValuesHolder propertyBottom = PropertyValuesHolder.ofInt(\"bottom\", mAnimatedImage.getBottom(), mToTop + mToHeight - getStatusBarHeight());\n\n        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(mAnimatedImage, propertyLeft, propertyTop, propertyRight, propertyBottom);\n        animator.addListener(new SimpleAnimationListener() {\n            @Override\n            public void onAnimationEnd(Animator animation) {\n                // set new parameters of animated ImageView. This will prevent blinking of view when set visibility to visible in Exit animation\n                FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mAnimatedImage.getLayoutParams();\n                layoutParams.height = mImageTo.getHeight();\n                layoutParams.width = mImageTo.getWidth();\n                layoutParams.setMargins(mToLeft, mToTop - getStatusBarHeight(), 0, 0);\n            }\n        });\n        return animator;\n    }\n\n    /**\n     * This method creates Animator that will animate matrix of ImageView.\n     * It is needed in order to show the effect when scaling of one view is smoothly changed to the scale of the second view.\n     * <p>\n     * For example: first view can have scaleType: centerCrop, and the other one fitCenter.\n     * The image inside ImageView will smoothly change from one to another\n     */\n    private ObjectAnimator createEnteringImageMatrixAnimator() {\n\n        Matrix initMatrix = MatrixUtils.getImageMatrix(mAnimatedImage);\n        // store the data about original matrix into array.\n        // this array will be used later for exit animation\n        initMatrix.getValues(mInitThumbnailMatrixValues);\n\n        final Matrix endMatrix = MatrixUtils.getImageMatrix(mImageTo);\n        Log.v(TAG, \"createEnteringImageMatrixAnimator, mInitThumbnailMatrixValues \" + Arrays.toString(mInitThumbnailMatrixValues));\n\n        Log.v(TAG, \"createEnteringImageMatrixAnimator, initMatrix \" + initMatrix);\n        Log.v(TAG, \"createEnteringImageMatrixAnimator,  endMatrix \" + endMatrix);\n\n        mAnimatedImage.setScaleType(ImageView.ScaleType.MATRIX);\n\n        return ObjectAnimator.ofObject(mAnimatedImage, MatrixEvaluator.ANIMATED_TRANSFORM_PROPERTY,\n                new MatrixEvaluator(), initMatrix, endMatrix);\n    }\n\n    public float[] getInitialThumbnailMatrixValues() {\n        return mInitThumbnailMatrixValues;\n    }\n\n    public void cancelRunningAnimations() {\n        Log.v(TAG, \"cancelRunningAnimations, mEnteringAnimation \" + mEnteringAnimation);\n\n        if (mEnteringAnimation != null) {\n            // cancel existing animation\n            mEnteringAnimation.cancel();\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/volokh/danylo/imagetransition/animations/ExitScreenAnimations.java",
    "content": "package com.volokh.danylo.imagetransition.animations;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorSet;\nimport android.animation.ObjectAnimator;\nimport android.animation.PropertyValuesHolder;\nimport android.app.Activity;\nimport android.graphics.Matrix;\nimport android.support.annotation.NonNull;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.animation.AccelerateInterpolator;\nimport android.widget.ImageView;\n\nimport com.squareup.otto.Bus;\nimport com.volokh.danylo.imagetransition.event_bus.ChangeImageThumbnailVisibility;\nimport com.volokh.danylo.imagetransition.event_bus.EventBusCreator;\n\n/**\n * Created by danylo.volokh on 3/16/16.\n */\npublic class ExitScreenAnimations extends ScreenAnimation{\n\n    private static final String TAG = ExitScreenAnimations.class.getSimpleName();\n\n    private final Bus mBus = EventBusCreator.defaultEventBus();\n\n    private final ImageView mAnimatedImage;\n    private final ImageView mImageTo;\n    private final View mMainContainer;\n\n    /**\n     * These values represent the final position of a Image that is translated\n     */\n    private int mToTop;\n    private int mToLeft;\n    private int mToWidth;\n    private int mToHeight;\n\n    private AnimatorSet mExitingAnimation;\n\n    private float[] mToThumbnailMatrixValues;\n\n    public ExitScreenAnimations(ImageView animatedImage, ImageView imageTo, View mainContainer) {\n        super(animatedImage.getContext());\n        mAnimatedImage = animatedImage;\n        mImageTo = imageTo;\n        mMainContainer = mainContainer;\n    }\n\n    public void playExitAnimations(int toTop, int toLeft, int toWidth, int toHeight, float[] toThumbnailMatrixValues) {\n        mToTop = toTop;\n        mToLeft = toLeft;\n        mToWidth = toWidth;\n        mToHeight = toHeight;\n\n        mToThumbnailMatrixValues = toThumbnailMatrixValues;\n\n        Log.v(TAG, \"playExitAnimations, mExitingAnimation \" + mExitingAnimation);\n        if (mExitingAnimation == null) {\n            playExitingAnimation();\n        }\n    }\n\n    private void playExitingAnimation() {\n        Log.v(TAG, \"playExitingAnimation\");\n\n        mAnimatedImage.setVisibility(View.VISIBLE);\n        mImageTo.setVisibility(View.INVISIBLE);\n\n        AnimatorSet imageAnimatorSet = createExitingImageAnimation();\n\n        Animator mainContainerFadeAnimator = createExitingFadeAnimator();\n\n        mExitingAnimation = new AnimatorSet();\n        mExitingAnimation.setDuration(IMAGE_TRANSLATION_DURATION);\n        mExitingAnimation.setInterpolator(new AccelerateInterpolator());\n        mExitingAnimation.addListener(new SimpleAnimationListener() {\n\n            @Override\n            public void onAnimationEnd(Animator animation) {\n\n                mBus.post(new ChangeImageThumbnailVisibility(true));\n\n                Log.v(TAG, \"onAnimationEnd, mExitingAnimation \" + mExitingAnimation);\n                mExitingAnimation = null;\n\n                // finish the activity when animation is finished\n                Activity activity = (Activity) mAnimatedImage.getContext();\n                activity.finish();\n                activity.overridePendingTransition(0, 0);\n            }\n        });\n\n        mExitingAnimation.playTogether(\n                imageAnimatorSet,\n                mainContainerFadeAnimator\n        );\n\n        mExitingAnimation.start();\n    }\n\n    /**\n     * This method creates an animator set of 2 animations:\n     * 1. ImageView position animation when screen is closed\n     * 2. ImageView image matrix animation when screen is closed\n     */\n    private AnimatorSet createExitingImageAnimation() {\n        Log.v(TAG, \">> createExitingImageAnimation\");\n\n        ObjectAnimator positionAnimator = createExitingImagePositionAnimator();\n        ObjectAnimator matrixAnimator = createExitingImageMatrixAnimator();\n\n        AnimatorSet exitingImageAnimation = new AnimatorSet();\n        exitingImageAnimation.playTogether(positionAnimator, matrixAnimator);\n\n        Log.v(TAG, \"<< createExitingImageAnimation\");\n        return exitingImageAnimation;\n    }\n\n    /**\n     * This method creates an animator that changes ImageView position on the screen.\n     * It will look like view is translated from its position on this screen to its position on previous screen\n     */\n    @NonNull\n    private ObjectAnimator createExitingImagePositionAnimator() {\n\n        // get initial location on the screen and start animation from there\n        int[] locationOnScreen = new int[2];\n        mAnimatedImage.getLocationOnScreen(locationOnScreen);\n\n        PropertyValuesHolder propertyLeft = PropertyValuesHolder.ofInt(\"left\",\n                locationOnScreen[0],\n                mToLeft);\n\n        PropertyValuesHolder propertyTop = PropertyValuesHolder.ofInt(\"top\",\n                locationOnScreen[1] - getStatusBarHeight(),\n                mToTop - getStatusBarHeight());\n\n        PropertyValuesHolder propertyRight = PropertyValuesHolder.ofInt(\"right\",\n                locationOnScreen[0] + mAnimatedImage.getWidth(),\n                mToLeft + mToWidth);\n\n\n        PropertyValuesHolder propertyBottom = PropertyValuesHolder.ofInt(\"bottom\",\n                mAnimatedImage.getBottom(),\n                mToTop + mToHeight - getStatusBarHeight());\n\n        return ObjectAnimator.ofPropertyValuesHolder(mAnimatedImage, propertyLeft, propertyTop, propertyRight, propertyBottom);\n    }\n\n\n    /**\n     * This method creates animator that animates Matrix of ImageView.\n     * It is needed in order to show the effect when scaling of one view is smoothly changed to the scale of the second view.\n     * <p>\n     * For example: first view can have scaleType: centerCrop, and the other one fitCenter.\n     * The image inside ImageView will smoothly change from one to another\n     */\n    private ObjectAnimator createExitingImageMatrixAnimator() {\n\n        Matrix initialMatrix = MatrixUtils.getImageMatrix(mAnimatedImage);\n\n        Matrix endMatrix = new Matrix();\n        endMatrix.setValues(mToThumbnailMatrixValues);\n\n        Log.v(TAG, \"createExitingImageMatrixAnimator, initialMatrix \" + initialMatrix);\n        Log.v(TAG, \"createExitingImageMatrixAnimator,     endMatrix \" + endMatrix);\n\n        mAnimatedImage.setScaleType(ImageView.ScaleType.MATRIX);\n\n        return ObjectAnimator.ofObject(mAnimatedImage, MatrixEvaluator.ANIMATED_TRANSFORM_PROPERTY,\n                new MatrixEvaluator(), initialMatrix, endMatrix);\n    }\n\n    private ObjectAnimator createExitingFadeAnimator() {\n        ObjectAnimator fadeInAnimator = ObjectAnimator.ofFloat(mMainContainer, \"alpha\", 1.0f, 0.0f);\n        return fadeInAnimator;\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/volokh/danylo/imagetransition/animations/MatrixEvaluator.java",
    "content": "package com.volokh.danylo.imagetransition.animations;\n\nimport android.animation.TypeEvaluator;\nimport android.graphics.Matrix;\nimport android.graphics.drawable.Drawable;\nimport android.util.Property;\nimport android.widget.ImageView;\n\n/**\n * This class is passed to ObjectAnimator in order to animate changes in ImageView image matrix\n */\npublic class MatrixEvaluator implements TypeEvaluator<Matrix> {\n\n    private static final String TAG = MatrixEvaluator.class.getSimpleName();\n\n    public static TypeEvaluator<Matrix> NULL_MATRIX_EVALUATOR = new TypeEvaluator<Matrix>() {\n        @Override\n        public Matrix evaluate(float fraction, Matrix startValue, Matrix endValue) {\n            return null;\n        }\n    };\n\n    /**\n     * This property is passed to ObjectAnimator when we are animating image matrix of ImageView\n     */\n    public static final Property<ImageView, Matrix> ANIMATED_TRANSFORM_PROPERTY = new Property<ImageView, Matrix>(Matrix.class,\n            \"animatedTransform\") {\n\n        /**\n         * This is copy-paste form ImageView#animateTransform - method is invisible in sdk\n         */\n        @Override\n        public void set(ImageView imageView, Matrix matrix) {\n            Drawable drawable = imageView.getDrawable();\n            if (drawable == null) {\n                return;\n            }\n            if (matrix == null) {\n                drawable.setBounds(0, 0, imageView.getWidth(), imageView.getHeight());\n            } else {\n\n                drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());\n                Matrix drawMatrix = imageView.getImageMatrix();\n                if (drawMatrix == null) {\n                    drawMatrix = new Matrix();\n                    imageView.setImageMatrix(drawMatrix);\n                }\n                imageView.setImageMatrix(matrix);\n            }\n            imageView.invalidate();\n        }\n\n        @Override\n        public Matrix get(ImageView object) {\n            return null;\n        }\n    };\n\n    float[] mTempStartValues = new float[9];\n\n    float[] mTempEndValues = new float[9];\n\n    Matrix mTempMatrix = new Matrix();\n\n    @Override\n    public Matrix evaluate(float fraction, Matrix startValue, Matrix endValue) {\n        startValue.getValues(mTempStartValues);\n        endValue.getValues(mTempEndValues);\n        for (int i = 0; i < 9; i++) {\n            float diff = mTempEndValues[i] - mTempStartValues[i];\n            mTempEndValues[i] = mTempStartValues[i] + (fraction * diff);\n        }\n        mTempMatrix.setValues(mTempEndValues);\n\n        return mTempMatrix;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/volokh/danylo/imagetransition/animations/MatrixUtils.java",
    "content": "package com.volokh.danylo.imagetransition.animations;\n\nimport android.graphics.Matrix;\nimport android.graphics.Rect;\nimport android.graphics.drawable.Drawable;\nimport android.util.Log;\nimport android.widget.ImageView;\n\n/**\n * Created by danylo.volokh on 3/14/16.\n */\npublic class MatrixUtils {\n\n    private static final String TAG = MatrixUtils.class.getSimpleName();\n\n    public static Matrix getImageMatrix(ImageView imageView) {\n        Log.v(TAG, \"getImageMatrix, imageView \" + imageView);\n\n        int left = imageView.getLeft();\n        int top = imageView.getTop();\n        int right = imageView.getRight();\n        int bottom = imageView.getBottom();\n\n        Rect bounds = new Rect(left, top, right, bottom);\n\n        Drawable drawable = imageView.getDrawable();\n\n        Matrix matrix;\n        ImageView.ScaleType scaleType = imageView.getScaleType();\n        Log.v(TAG, \"getImageMatrix, scaleType \" + scaleType);\n\n        if (scaleType == ImageView.ScaleType.FIT_XY) {\n            matrix = imageView.getImageMatrix();\n            if (!matrix.isIdentity()) {\n                matrix = new Matrix(matrix);\n            } else {\n                int drawableWidth = drawable.getIntrinsicWidth();\n                int drawableHeight = drawable.getIntrinsicHeight();\n                if (drawableWidth > 0 && drawableHeight > 0) {\n                    float scaleX = ((float) bounds.width()) / drawableWidth;\n                    float scaleY = ((float) bounds.height()) / drawableHeight;\n                    matrix = new Matrix();\n                    matrix.setScale(scaleX, scaleY);\n                } else {\n                    matrix = null;\n                }\n            }\n        } else {\n            matrix = new Matrix(imageView.getImageMatrix());\n        }\n\n        return matrix;\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/volokh/danylo/imagetransition/animations/ScreenAnimation.java",
    "content": "package com.volokh.danylo.imagetransition.animations;\n\nimport android.content.Context;\n\n/**\n * Created by danylo.volokh on 3/19/16.\n */\npublic abstract class ScreenAnimation {\n\n    protected static final long IMAGE_TRANSLATION_DURATION = 1000;\n\n    private final Context mContext;\n\n    protected ScreenAnimation(Context context) {\n        mContext = context;\n    }\n\n    protected int getStatusBarHeight() {\n        int result = 0;\n        int resourceId = mContext.getResources().getIdentifier(\"status_bar_height\", \"dimen\", \"android\");\n        if (resourceId > 0) {\n            result = mContext.getResources().getDimensionPixelSize(resourceId);\n        }\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/volokh/danylo/imagetransition/animations/SimpleAnimationListener.java",
    "content": "package com.volokh.danylo.imagetransition.animations;\n\nimport android.animation.Animator;\n\n/**\n * Created by danylo.volokh on 3/9/16.\n */\npublic class SimpleAnimationListener implements Animator.AnimatorListener {\n\n    /**\n     * For overriding\n     */\n    @Override\n    public void onAnimationStart(Animator animation) {\n    }\n\n    /**\n     * For overriding\n     */\n    @Override\n    public void onAnimationEnd(Animator animation) {\n    }\n\n    /**\n     * For overriding\n     */\n    @Override\n    public void onAnimationCancel(Animator animation) {\n    }\n\n    /**\n     * For overriding\n     */\n    @Override\n    public void onAnimationRepeat(Animator animation) {\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/volokh/danylo/imagetransition/event_bus/ChangeImageThumbnailVisibility.java",
    "content": "package com.volokh.danylo.imagetransition.event_bus;\n\n/**\n * Created by danylo.volokh on 3/15/16.\n *\n * This message is sent from {@link com.volokh.danylo.imagetransition.activities.ImageDetailsActivity}\n * to {@link com.volokh.danylo.imagetransition.activities.ImagesListActivity}\n *\n * When image transition is about to start. This message should invoke hiding of original image\n * Which transition we are imitating.\n *\n */\npublic class ChangeImageThumbnailVisibility {\n\n    private final boolean visible;\n\n    public ChangeImageThumbnailVisibility(boolean visible) {\n        this.visible = visible;\n    }\n\n    public boolean isVisible() {\n        return visible;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/volokh/danylo/imagetransition/event_bus/EventBusCreator.java",
    "content": "package com.volokh.danylo.imagetransition.event_bus;\n\nimport com.squareup.otto.Bus;\n\n/**\n * Created by danylo.volokh on 3/14/16.\n * Double checked singleton (basically we need it only in UI thread) for Otto event bus.\n */\npublic class EventBusCreator {\n\n    private static Bus bus;\n\n    public static Bus defaultEventBus() {\n\n        if (bus == null) {\n            synchronized (EventBusCreator.class) {\n                if (bus == null) {\n                    bus = new Bus();\n                }\n            }\n        }\n        return bus;\n    }\n}\n"
  },
  {
    "path": "app/src/main/res/layout/image_details_activity_layout.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/main_container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:alpha=\"0\"\n    android:orientation=\"vertical\"\n    android:background=\"@color/green\">\n\n    <TextView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"48dp\"\n        android:background=\"@color/white2\"\n        android:gravity=\"center\"\n        android:text=\"Image Details Activity\" />\n\n    <ImageView\n        android:id=\"@+id/enlarged_image\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\"\n        android:scaleType=\"centerCrop\"\n        android:visibility=\"invisible\"/>\n\n    <TextView\n        android:id=\"@+id/image_description\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\"\n        android:background=\"@color/orange\"\n        android:gravity=\"top|left\"\n        android:padding=\"20dp\"\n        android:text=\"I see a beautiful city and a brilliant people rising from this abyss. I see the lives for which I lay down my life, peaceful, useful, prosperous and happy. I see that I hold a sanctuary in their hearts, and in the hearts of their descendants, generations hence. It is a far, far better thing that I do, than I have ever done; it is a far, far better rest that I go to than I have ever known.\" />\n\n</LinearLayout>"
  },
  {
    "path": "app/src/main/res/layout/image_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ImageView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/image\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" />"
  },
  {
    "path": "app/src/main/res/layout/images_list.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:fitsSystemWindows=\"true\"\n    tools:context=\".activities.ImagesListActivity\"\n    android:id=\"@+id/root_layout\"\n    android:orientation=\"vertical\">\n\n    <TextView\n        android:id=\"@+id/title\"\n        android:textSize=\"18sp\"\n        android:background=\"?attr/android:windowBackground\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"48dp\"\n        android:gravity=\"center\"/>\n\n    <android.support.v7.widget.RecyclerView\n        android:id=\"@+id/accounts_recycler_view\"\n        android:layout_width=\"match_parent\"\n        android:layout_weight=\"1\"\n        android:layout_height=\"0dp\">\n    </android.support.v7.widget.RecyclerView>\n\n    <Button\n        android:id=\"@+id/switch_to\"\n        android:visibility=\"gone\"\n        android:layout_width=\"match_parent\"\n        android:layout_gravity=\"bottom\"\n        android:layout_height=\"48dp\" />\n\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/transition/change_image_transition.xml",
    "content": "<transitionSet xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\n    android:duration=\"1000\">\n\n    <changeClipBounds/>\n    <changeBounds/>\n    <changeImageTransform/>\n</transitionSet>"
  },
  {
    "path": "app/src/main/res/transition/enter_exit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<transitionSet xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <fade\n        android:duration=\"1000\"/>\n\n</transitionSet>"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#3F51B5</color>\n    <color name=\"colorPrimaryDark\">#303F9F</color>\n    <color name=\"colorAccent\">#FF4081</color>\n\n    <color name=\"green\">@color/material_deep_teal_500</color>\n    <color name=\"dark_blue\">@color/material_deep_teal_200</color>\n    <color name=\"orange\">#F7D896</color>\n    <color name=\"white2\">#DDDDDD</color>\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n    </style>\n\n    <style name=\"AppTheme.AppBarOverlay\" parent=\"ThemeOverlay.AppCompat.Dark.ActionBar\" />\n\n    <style name=\"AppTheme.PopupOverlay\" parent=\"ThemeOverlay.AppCompat.Light\" />\n\n\n    <style name=\"ImagesDetailsActivityStyle\" parent=\"AppTheme\">\n\n        <item name=\"colorPrimaryDark\">@color/white2</item>\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <item name=\"android:windowIsTranslucent\">true</item>\n\n    </style>\n\n    <style name=\"ImagesListActivityStyle\" parent=\"AppTheme\">\n\n        <item name=\"colorPrimaryDark\">@color/green</item>\n        <item name=\"android:windowBackground\">@color/green</item>\n\n    </style>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-v21/styles.xml",
    "content": "<resources>>\n\n    <style name=\"AppTheme.NoActionBar_v21\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <item name=\"windowActionBar\">false</item>\n        <item name=\"windowNoTitle\">false</item>\n        <item name=\"android:windowDrawsSystemBarBackgrounds\">true</item>\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n\n    </style>\n\n    <style name=\"ImagesDetailsActivityStyle_v21\" parent=\"AppTheme.NoActionBar_v21\">\n\n        <item name=\"android:windowBackground\">@color/green</item>\n\n        <item name=\"android:windowActivityTransitions\">true</item>\n        <item name=\"android:windowContentTransitions\">true</item>\n\n        <item name=\"android:windowAllowEnterTransitionOverlap\">true</item>\n        <item name=\"android:windowAllowReturnTransitionOverlap\">true</item>\n\n        <item name=\"android:windowExitTransition\">@transition/enter_exit</item>\n        <item name=\"android:windowEnterTransition\">@transition/enter_exit</item>\n\n        <item name=\"android:windowSharedElementEnterTransition\">@transition/change_image_transition</item>\n        <item name=\"android:windowSharedElementExitTransition\">@transition/change_image_transition</item>\n\n    </style>\n\n    <style name=\"ImagesListActivityStyle_v21\" parent=\"AppTheme.NoActionBar_v21\">\n\n        <item name=\"android:windowBackground\">@color/dark_blue</item>\n\n        <item name=\"android:windowActivityTransitions\">true</item>\n        <item name=\"android:windowContentTransitions\">true</item>\n\n        <item name=\"android:windowExitTransition\">@transition/enter_exit</item>\n        <item name=\"android:windowEnterTransition\">@transition/enter_exit</item>\n\n        <item name=\"android:windowSharedElementEnterTransition\">@transition/change_image_transition</item>\n        <item name=\"android:windowSharedElementExitTransition\">@transition/change_image_transition</item>\n\n    </style>\n</resources>\n"
  },
  {
    "path": "app/src/test/java/com/volokh/danylo/imagetransition/ExampleUnitTest.java",
    "content": "package com.volokh.danylo.imagetransition;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * To work on unit tests, switch the Test Artifact in the Build Variants view.\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    repositories {\n        jcenter()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:1.5.0'\n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        jcenter()\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Wed Oct 21 11:34:03 PDT 2015\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-2.8-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\n# Default value: -Xmx10248m -XX:MaxPermSize=256m\n# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8\n\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windowz variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\ngoto execute\n\n:4NT_args\n@rem Get arguments from the 4NT Shell from JP Software\nset CMD_LINE_ARGS=%$\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "settings.gradle",
    "content": "include ':app'\n"
  }
]