[
  {
    "path": ".gitignore",
    "content": ".gradle\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.DS_Store\n/build\n"
  },
  {
    "path": ".idea/.name",
    "content": "MissView"
  },
  {
    "path": ".idea/compiler.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"CompilerConfiguration\">\n    <option name=\"DEFAULT_COMPILER\" value=\"Javac\" />\n    <resourceExtensions />\n    <wildcardResourcePatterns>\n      <entry name=\"!?*.java\" />\n      <entry name=\"!?*.form\" />\n      <entry name=\"!?*.class\" />\n      <entry name=\"!?*.groovy\" />\n      <entry name=\"!?*.scala\" />\n      <entry name=\"!?*.flex\" />\n      <entry name=\"!?*.kt\" />\n      <entry name=\"!?*.clj\" />\n    </wildcardResourcePatterns>\n    <annotationProcessing>\n      <profile default=\"true\" name=\"Default\" enabled=\"false\">\n        <processorPath useClasspath=\"true\" />\n      </profile>\n    </annotationProcessing>\n  </component>\n</project>"
  },
  {
    "path": ".idea/copyright/profiles_settings.xml",
    "content": "<component name=\"CopyrightManager\">\n  <settings default=\"\" />\n</component>"
  },
  {
    "path": ".idea/encodings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"Encoding\">\n    <file url=\"PROJECT\" charset=\"UTF-8\" />\n  </component>\n</project>"
  },
  {
    "path": ".idea/gradle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"GradleSettings\">\n    <option name=\"linkedExternalProjectsSettings\">\n      <GradleProjectSettings>\n        <option name=\"distributionType\" value=\"DEFAULT_WRAPPED\" />\n        <option name=\"externalProjectPath\" value=\"$PROJECT_DIR$\" />\n        <option name=\"modules\">\n          <set>\n            <option value=\"$PROJECT_DIR$\" />\n            <option value=\"$PROJECT_DIR$/demo\" />\n            <option value=\"$PROJECT_DIR$/library\" />\n          </set>\n        </option>\n        <option name=\"myModules\">\n          <set>\n            <option value=\"$PROJECT_DIR$\" />\n            <option value=\"$PROJECT_DIR$/demo\" />\n            <option value=\"$PROJECT_DIR$/library\" />\n          </set>\n        </option>\n      </GradleProjectSettings>\n    </option>\n  </component>\n</project>"
  },
  {
    "path": ".idea/misc.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"EntryPointsManager\">\n    <entry_points version=\"2.0\" />\n  </component>\n  <component name=\"NullableNotNullManager\">\n    <option name=\"myDefaultNullable\" value=\"android.support.annotation.Nullable\" />\n    <option name=\"myDefaultNotNull\" value=\"android.support.annotation.NonNull\" />\n    <option name=\"myNullables\">\n      <value>\n        <list size=\"4\">\n          <item index=\"0\" class=\"java.lang.String\" itemvalue=\"org.jetbrains.annotations.Nullable\" />\n          <item index=\"1\" class=\"java.lang.String\" itemvalue=\"javax.annotation.Nullable\" />\n          <item index=\"2\" class=\"java.lang.String\" itemvalue=\"edu.umd.cs.findbugs.annotations.Nullable\" />\n          <item index=\"3\" class=\"java.lang.String\" itemvalue=\"android.support.annotation.Nullable\" />\n        </list>\n      </value>\n    </option>\n    <option name=\"myNotNulls\">\n      <value>\n        <list size=\"4\">\n          <item index=\"0\" class=\"java.lang.String\" itemvalue=\"org.jetbrains.annotations.NotNull\" />\n          <item index=\"1\" class=\"java.lang.String\" itemvalue=\"javax.annotation.Nonnull\" />\n          <item index=\"2\" class=\"java.lang.String\" itemvalue=\"edu.umd.cs.findbugs.annotations.NonNull\" />\n          <item index=\"3\" class=\"java.lang.String\" itemvalue=\"android.support.annotation.NonNull\" />\n        </list>\n      </value>\n    </option>\n  </component>\n  <component name=\"ProjectLevelVcsManager\" settingsEditedManually=\"false\">\n    <OptionsSetting value=\"true\" id=\"Add\" />\n    <OptionsSetting value=\"true\" id=\"Remove\" />\n    <OptionsSetting value=\"true\" id=\"Checkout\" />\n    <OptionsSetting value=\"true\" id=\"Update\" />\n    <OptionsSetting value=\"true\" id=\"Status\" />\n    <OptionsSetting value=\"true\" id=\"Edit\" />\n    <ConfirmationsSetting value=\"0\" id=\"Add\" />\n    <ConfirmationsSetting value=\"0\" id=\"Remove\" />\n  </component>\n  <component name=\"ProjectRootManager\" version=\"2\" languageLevel=\"JDK_1_7\" assert-keyword=\"true\" jdk-15=\"true\" project-jdk-name=\"1.8\" project-jdk-type=\"JavaSDK\">\n    <output url=\"file://$PROJECT_DIR$/build/classes\" />\n  </component>\n  <component name=\"ProjectType\">\n    <option name=\"id\" value=\"Android\" />\n  </component>\n  <component name=\"masterDetails\">\n    <states>\n      <state key=\"ProjectJDKs.UI\">\n        <settings>\n          <last-edited>1.8</last-edited>\n          <splitter-proportions>\n            <option name=\"proportions\">\n              <list>\n                <option value=\"0.2\" />\n              </list>\n            </option>\n          </splitter-proportions>\n        </settings>\n      </state>\n    </states>\n  </component>\n</project>"
  },
  {
    "path": ".idea/modules.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ProjectModuleManager\">\n    <modules>\n      <module fileurl=\"file://$PROJECT_DIR$/MissView.iml\" filepath=\"$PROJECT_DIR$/MissView.iml\" />\n      <module fileurl=\"file://$PROJECT_DIR$/demo/demo.iml\" filepath=\"$PROJECT_DIR$/demo/demo.iml\" />\n      <module fileurl=\"file://$PROJECT_DIR$/library/library.iml\" filepath=\"$PROJECT_DIR$/library/library.iml\" />\n    </modules>\n  </component>\n</project>"
  },
  {
    "path": ".idea/runConfigurations.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"RunConfigurationProducerService\">\n    <option name=\"ignoredProducers\">\n      <set>\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer\" />\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer\" />\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer\" />\n      </set>\n    </option>\n  </component>\n</project>"
  },
  {
    "path": ".idea/vcs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"VcsDirectoryMappings\">\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n  </component>\n</project>"
  },
  {
    "path": "MissView.iml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module external.linked.project.id=\"MissView\" external.linked.project.path=\"$MODULE_DIR$\" external.root.project.path=\"$MODULE_DIR$\" external.system.id=\"GRADLE\" external.system.module.group=\"\" external.system.module.version=\"unspecified\" type=\"JAVA_MODULE\" version=\"4\">\n  <component name=\"FacetManager\">\n    <facet type=\"java-gradle\" name=\"Java-Gradle\">\n      <configuration>\n        <option name=\"BUILD_FOLDER_PATH\" value=\"$MODULE_DIR$/build\" />\n        <option name=\"BUILDABLE\" value=\"false\" />\n      </configuration>\n    </facet>\n  </component>\n  <component name=\"NewModuleRootManager\" LANGUAGE_LEVEL=\"JDK_1_7\" inherit-compiler-output=\"true\">\n    <exclude-output />\n    <content url=\"file://$MODULE_DIR$\">\n      <excludeFolder url=\"file://$MODULE_DIR$/.gradle\" />\n    </content>\n    <orderEntry type=\"inheritedJdk\" />\n    <orderEntry type=\"sourceFolder\" forTests=\"false\" />\n  </component>\n</module>"
  },
  {
    "path": "README.md",
    "content": "# MissView\nThis project is a part of Muzei,(https://github.com/romannurik/muzei) .<br>\nThanks for Roman Nurik..<br>\n\nI like the animation in that project...so i studied his codes and make this..<br>\n\n![](https://github.com/Rogero0o/MissView/raw/master/missview.gif)\n\n..as you see right now,wish you like it.\n\n### Step\n\nImport this project into android studio..it's built with it.\n\n###  Usage\n\n#### Jcenter\n\ngradle\n```\ncompile 'com.roger.missview.library:library:1.0.0'\n```\n\n##### Config in xml\n\n```xml\n    <com.roger.missview.library.MissView\n        android:id=\"@+id/missview\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n```\n\n##### and init in java code\n\n```java\n mMissview = (MissView) findViewById(R.id.missview);\n mMissview.initPicture(path);//path is the String of background picture path\n```\n\n\n## About me\n\nA small guy  in mainland FUJIAN China.\n\nIf you have any new idea about this project, feel free to tell me ,Tks. :smiley:\n\n## License\n\n\tThe MIT License (MIT)\n\n\tCopyright © 2015 Roger\n\n\tPermission is hereby granted, free of charge, to any person obtaining a copy\n\tof this software and associated documentation files (the \"Software\"), to deal\n\tin the Software without restriction, including without limitation the rights\n\tto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n\tcopies of the Software, and to permit persons to whom the Software is\n\tfurnished to do so, subject to the following conditions:\n\n\tThe above copyright notice and this permission notice shall be included in\n\tall copies or substantial portions of the Software.\n\n\tTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n\tIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n\tFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n\tAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n\tLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n\tOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n\tTHE SOFTWARE.\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.1.+'\n        classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0'\n        classpath 'com.github.dcendents:android-maven-plugin:1.2'\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"
  },
  {
    "path": "demo/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "demo/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 21\n    buildToolsVersion \"21.1.2\"\n\n    defaultConfig {\n        applicationId \"com.roger.missview\"\n        minSdkVersion 19\n        targetSdkVersion 21\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    compile 'com.android.support:appcompat-v7:21.0.3'\n    compile project(':library')\n}\n"
  },
  {
    "path": "demo/demo.iml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module external.linked.project.id=\":demo\" external.linked.project.path=\"$MODULE_DIR$\" external.root.project.path=\"$MODULE_DIR$/..\" external.system.id=\"GRADLE\" external.system.module.group=\"MissView\" external.system.module.version=\"unspecified\" type=\"JAVA_MODULE\" version=\"4\">\n  <component name=\"FacetManager\">\n    <facet type=\"android-gradle\" name=\"Android-Gradle\">\n      <configuration>\n        <option name=\"GRADLE_PROJECT_PATH\" value=\":demo\" />\n      </configuration>\n    </facet>\n    <facet type=\"android\" name=\"Android\">\n      <configuration>\n        <option name=\"SELECTED_BUILD_VARIANT\" value=\"debug\" />\n        <option name=\"SELECTED_TEST_ARTIFACT\" value=\"_android_test_\" />\n        <option name=\"ASSEMBLE_TASK_NAME\" value=\"assembleDebug\" />\n        <option name=\"COMPILE_JAVA_TASK_NAME\" value=\"compileDebugSources\" />\n        <afterSyncTasks>\n          <task>generateDebugSources</task>\n        </afterSyncTasks>\n        <option name=\"ALLOW_USER_CONFIGURATION\" value=\"false\" />\n        <option name=\"MANIFEST_FILE_RELATIVE_PATH\" value=\"/src/main/AndroidManifest.xml\" />\n        <option name=\"RES_FOLDER_RELATIVE_PATH\" value=\"/src/main/res\" />\n        <option name=\"RES_FOLDERS_RELATIVE_PATH\" value=\"file://$MODULE_DIR$/src/main/res\" />\n        <option name=\"ASSETS_FOLDER_RELATIVE_PATH\" value=\"/src/main/assets\" />\n      </configuration>\n    </facet>\n  </component>\n  <component name=\"NewModuleRootManager\" LANGUAGE_LEVEL=\"JDK_1_7\" inherit-compiler-output=\"false\">\n    <output url=\"file://$MODULE_DIR$/build/intermediates/classes/debug\" />\n    <output-test url=\"file://$MODULE_DIR$/build/intermediates/classes/test/debug\" />\n    <exclude-output />\n    <content url=\"file://$MODULE_DIR$\">\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/source/r/debug\" isTestSource=\"false\" generated=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/source/aidl/debug\" isTestSource=\"false\" generated=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/source/buildConfig/debug\" isTestSource=\"false\" generated=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/source/rs/debug\" isTestSource=\"false\" generated=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/res/rs/debug\" type=\"java-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/res/generated/debug\" type=\"java-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/source/r/androidTest/debug\" isTestSource=\"true\" generated=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug\" isTestSource=\"true\" generated=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug\" isTestSource=\"true\" generated=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug\" isTestSource=\"true\" generated=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/res/generated/androidTest/debug\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/debug/res\" type=\"java-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/debug/resources\" type=\"java-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/debug/assets\" type=\"java-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/debug/aidl\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/debug/java\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/debug/jni\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/debug/rs\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/testDebug/res\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/testDebug/resources\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/testDebug/assets\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/testDebug/aidl\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/testDebug/java\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/testDebug/jni\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/testDebug/rs\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/main/res\" type=\"java-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/main/resources\" type=\"java-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/main/assets\" type=\"java-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/main/aidl\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/main/java\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/main/jni\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/main/rs\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/androidTest/res\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/androidTest/resources\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/androidTest/assets\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/androidTest/aidl\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/androidTest/java\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/androidTest/jni\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/androidTest/rs\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/test/res\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/test/resources\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/test/assets\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/test/aidl\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/test/java\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/test/jni\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/test/rs\" isTestSource=\"true\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/assets\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/classes\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/dependency-cache\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/dex\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/incremental\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/manifests\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/mockable-android-21.jar\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/pre-dexed\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/res\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/resources\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/rs\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/symbols\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/tmp\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/outputs\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/tmp\" />\n    </content>\n    <orderEntry type=\"jdk\" jdkName=\"Android API 21 Platform\" jdkType=\"Android SDK\" />\n    <orderEntry type=\"sourceFolder\" forTests=\"false\" />\n    <orderEntry type=\"library\" exported=\"\" name=\"appcompat-v7-21.0.3\" level=\"project\" />\n    <orderEntry type=\"library\" exported=\"\" scope=\"TEST\" name=\"mockable-android-21\" level=\"project\" />\n    <orderEntry type=\"library\" exported=\"\" name=\"support-v4-21.0.3\" level=\"project\" />\n    <orderEntry type=\"library\" exported=\"\" name=\"support-annotations-21.0.3\" level=\"project\" />\n    <orderEntry type=\"module\" module-name=\"library\" exported=\"\" />\n  </component>\n</module>"
  },
  {
    "path": "demo/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 F:\\adt-bundle-windows-x86_64_with_Android_5.0\\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": "demo/src/androidTest/java/com/roger/missview/ApplicationTest.java",
    "content": "package com.roger.missview;\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": "demo/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.roger.missview\">\n\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\" />\n\n    <application\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:theme=\"@style/AppTheme\">\n        <activity\n            android:name=\".MainActivity\"\n            android:label=\"@string/app_name\">\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    </application>\n\n</manifest>\n"
  },
  {
    "path": "demo/src/main/java/com/roger/missview/MainActivity.java",
    "content": "package com.roger.missview;\n\nimport android.app.Activity;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.os.Bundle;\nimport android.os.Environment;\n\nimport com.roger.missview.library.MissView;\n\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\n\n\npublic class MainActivity extends Activity {\n    private final String IMAGE_NAME = \"night.jpg\";\n    MissView mMissview;\n    String path;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n        initImage();//将图片从assets考到SD卡\n        mMissview = (MissView) findViewById(R.id.missview);\n        Bitmap bit = BitmapFactory.decodeResource(this.getResources(),R.drawable.night);\n        mMissview.initPicture(bit);\n    }\n\n    private void initImage() {\n        try {\n            path = getSDPath() + \"/\" + IMAGE_NAME;\n            File image = new File(path);\n            if (image.exists()) {\n                return;\n            }\n            assetsDataToSD(path);\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n    }\n\n    private void assetsDataToSD(String fileName) throws IOException {\n        InputStream myInput;\n        OutputStream myOutput = new FileOutputStream(fileName);\n        myInput = this.getAssets().open(IMAGE_NAME);\n        byte[] buffer = new byte[1024];\n        int length = myInput.read(buffer);\n        while (length > 0) {\n            myOutput.write(buffer, 0, length);\n            length = myInput.read(buffer);\n        }\n        myOutput.flush();\n        myInput.close();\n        myOutput.close();\n    }\n\n    public String getSDPath() {\n        File sdDir = null;\n        boolean sdCardExist = Environment.getExternalStorageState()\n                .equals(android.os.Environment.MEDIA_MOUNTED);   //判断sd卡是否存在\n        if (sdCardExist)      //如果SD卡存在，则获取跟目录\n        {\n            sdDir = Environment.getExternalStorageDirectory();//获取跟目录\n        }\n        return sdDir.toString();\n    }\n}\n"
  },
  {
    "path": "demo/src/main/res/layout/activity_main.xml",
    "content": "<RelativeLayout 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    tools:context=\".MainActivity\">\n\n    <com.roger.missview.library.MissView\n        android:id=\"@+id/missview\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n</RelativeLayout>\n"
  },
  {
    "path": "demo/src/main/res/menu/menu_main.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\" tools:context=\".MainActivity\">\n    <item android:id=\"@+id/action_settings\" android:title=\"@string/action_settings\"\n        android:orderInCategory=\"100\" app:showAsAction=\"never\" />\n</menu>\n"
  },
  {
    "path": "demo/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n</resources>\n"
  },
  {
    "path": "demo/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">Missview</string>\n\n    <string name=\"hello_world\">Hello world!</string>\n    <string name=\"action_settings\">Settings</string>\n</resources>\n"
  },
  {
    "path": "demo/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    </style>\n\n</resources>\n"
  },
  {
    "path": "demo/src/main/res/values-w820dp/dimens.xml",
    "content": "<resources>\n    <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n         (such as screen margins) for screens with more than 820dp of available width. This\n         would include 7\" and 10\" devices in landscape (~960dp and ~1280dp respectively). -->\n    <dimen name=\"activity_horizontal_margin\">64dp</dimen>\n</resources>\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Wed Apr 10 15:27:10 PDT 2013\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-2.2.1-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# For Cygwin, ensure paths are in UNIX format before anything is touched.\nif $cygwin ; then\n    [ -n \"$JAVA_HOME\" ] && JAVA_HOME=`cygpath --unix \"$JAVA_HOME\"`\nfi\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\\\"`/\" >&-\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >&-\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\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": "library/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "library/build.gradle",
    "content": "apply plugin: 'com.android.library'\napply plugin: 'com.github.dcendents.android-maven'\napply plugin: 'com.jfrog.bintray'\nversion = \"1.0.0\"\nandroid {\n    compileSdkVersion 21\n    buildToolsVersion \"21.1.2\"\n    resourcePrefix \"missview__\"\n    defaultConfig {\n        minSdkVersion 9\n        targetSdkVersion 21\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}\ndependencies {\n    compile fileTree(dir: 'libs', include: ['*.jar'])\n    compile files('libs/picasso-2.1.1.jar')\n}\ndef siteUrl = 'https://github.com/Rogero0o/MissView'\ndef gitUrl = 'https://github.com/Rogero0o/MissView.git'\ngroup = \"com.roger.missview.library\"\ninstall {\n    repositories.mavenInstaller {\n        // This generates POM.xml with proper parameters\n        pom {\n            project {\n                packaging 'aar'\n                // Add your description here\n                name 'Android MissView' \t//Ŀ\n                url siteUrl\n                // Set your license\n                licenses {\n                    license {\n                        name 'The Apache Software License, Version 2.0'\n                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'\n                    }\n                }\n                developers {\n                    developer {\n                        id 'roger'\t\t//дһЩϢ\n                        name 'Jack'\n                        email 'flyrogermail@gmail.com'\n                    }\n                }\n                scm {\n                    connection gitUrl\n                    developerConnection gitUrl\n                    url siteUrl\n                }\n            }\n        }\n    }\n}\ntask sourcesJar(type: Jar) {\n    from android.sourceSets.main.java.srcDirs\n    classifier = 'sources'\n}\ntask javadoc(type: Javadoc) {\n    source = android.sourceSets.main.java.srcDirs\n    classpath += project.files(android.getBootClasspath().join(File.pathSeparator))\n}\ntask javadocJar(type: Jar, dependsOn: javadoc) {\n    classifier = 'javadoc'\n    from javadoc.destinationDir\n}\nartifacts {\n    archives javadocJar\n    archives sourcesJar\n}\nProperties properties = new Properties()\nproperties.load(project.rootProject.file('local.properties').newDataInputStream())\nbintray {\n    user = properties.getProperty(\"bintray.user\")\n    key = properties.getProperty(\"bintray.apikey\")\n    configurations = ['archives']\n    pkg {\n        repo = \"maven\"\n        name = \"MissView\"\n        websiteUrl = siteUrl\n        vcsUrl = gitUrl\n        licenses = [\"Apache-2.0\"]\n        publish = true\n    }\n}"
  },
  {
    "path": "library/library.iml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module external.linked.project.id=\":library\" external.linked.project.path=\"$MODULE_DIR$\" external.root.project.path=\"$MODULE_DIR$/..\" external.system.id=\"GRADLE\" external.system.module.group=\"com.roger.missview.library\" external.system.module.version=\"1.0.0\" type=\"JAVA_MODULE\" version=\"4\">\n  <component name=\"FacetManager\">\n    <facet type=\"android-gradle\" name=\"Android-Gradle\">\n      <configuration>\n        <option name=\"GRADLE_PROJECT_PATH\" value=\":library\" />\n      </configuration>\n    </facet>\n    <facet type=\"android\" name=\"Android\">\n      <configuration>\n        <option name=\"SELECTED_BUILD_VARIANT\" value=\"debug\" />\n        <option name=\"SELECTED_TEST_ARTIFACT\" value=\"_android_test_\" />\n        <option name=\"ASSEMBLE_TASK_NAME\" value=\"assembleDebug\" />\n        <option name=\"COMPILE_JAVA_TASK_NAME\" value=\"compileDebugSources\" />\n        <afterSyncTasks>\n          <task>generateDebugSources</task>\n        </afterSyncTasks>\n        <option name=\"ALLOW_USER_CONFIGURATION\" value=\"false\" />\n        <option name=\"MANIFEST_FILE_RELATIVE_PATH\" value=\"/src/main/AndroidManifest.xml\" />\n        <option name=\"RES_FOLDER_RELATIVE_PATH\" value=\"/src/main/res\" />\n        <option name=\"RES_FOLDERS_RELATIVE_PATH\" value=\"file://$MODULE_DIR$/src/main/res\" />\n        <option name=\"ASSETS_FOLDER_RELATIVE_PATH\" value=\"/src/main/assets\" />\n        <option name=\"LIBRARY_PROJECT\" value=\"true\" />\n      </configuration>\n    </facet>\n  </component>\n  <component name=\"NewModuleRootManager\" LANGUAGE_LEVEL=\"JDK_1_7\" inherit-compiler-output=\"false\">\n    <output url=\"file://$MODULE_DIR$/build/intermediates/classes/debug\" />\n    <output-test url=\"file://$MODULE_DIR$/build/intermediates/classes/test/debug\" />\n    <exclude-output />\n    <content url=\"file://$MODULE_DIR$\">\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/source/r/debug\" isTestSource=\"false\" generated=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/source/aidl/debug\" isTestSource=\"false\" generated=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/source/buildConfig/debug\" isTestSource=\"false\" generated=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/source/rs/debug\" isTestSource=\"false\" generated=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/res/rs/debug\" type=\"java-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/res/generated/debug\" type=\"java-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/source/r/androidTest/debug\" isTestSource=\"true\" generated=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug\" isTestSource=\"true\" generated=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug\" isTestSource=\"true\" generated=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug\" isTestSource=\"true\" generated=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/res/generated/androidTest/debug\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/debug/res\" type=\"java-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/debug/resources\" type=\"java-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/debug/assets\" type=\"java-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/debug/aidl\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/debug/java\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/debug/jni\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/debug/rs\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/testDebug/res\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/testDebug/resources\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/testDebug/assets\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/testDebug/aidl\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/testDebug/java\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/testDebug/jni\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/testDebug/rs\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/main/res\" type=\"java-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/main/resources\" type=\"java-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/main/assets\" type=\"java-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/main/aidl\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/main/java\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/main/jni\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/main/rs\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/androidTest/res\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/androidTest/resources\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/androidTest/assets\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/androidTest/aidl\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/androidTest/java\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/androidTest/jni\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/androidTest/rs\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/test/res\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/test/resources\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/test/assets\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/test/aidl\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/test/java\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/test/jni\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/test/rs\" isTestSource=\"true\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/docs\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/assets\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/bundles\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/classes\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/dependency-cache\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/incremental\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/lint\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/manifests\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/mockable-android-21.jar\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/res\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/resources\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/rs\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/symbols\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/libs\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/outputs\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/poms\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/tmp\" />\n    </content>\n    <orderEntry type=\"jdk\" jdkName=\"Android API 21 Platform\" jdkType=\"Android SDK\" />\n    <orderEntry type=\"sourceFolder\" forTests=\"false\" />\n    <orderEntry type=\"library\" exported=\"\" scope=\"TEST\" name=\"mockable-android-21\" level=\"project\" />\n    <orderEntry type=\"library\" exported=\"\" name=\"picasso-2.1.1\" level=\"project\" />\n  </component>\n</module>"
  },
  {
    "path": "library/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 F:/adt-bundle-windows-x86_64_with_Android_5.0/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": "library/src/androidTest/java/com/roger/missview/library/ApplicationTest.java",
    "content": "package com.roger.missview.library;\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": "library/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.roger.missview.library\">\n</manifest>\n"
  },
  {
    "path": "library/src/main/java/com/roger/missview/library/BlurRenderer.java",
    "content": "/*\n * Copyright 2014 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.roger.missview.library;\n\nimport android.annotation.TargetApi;\nimport android.app.ActivityManager;\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.graphics.Color;\nimport android.graphics.Rect;\nimport android.graphics.RectF;\nimport android.opengl.GLES20;\nimport android.opengl.GLSurfaceView;\nimport android.opengl.Matrix;\nimport android.os.Build;\nimport android.preference.PreferenceManager;\nimport android.util.DisplayMetrics;\nimport android.view.animation.AccelerateDecelerateInterpolator;\nimport android.view.animation.Interpolator;\n\nimport com.roger.missview.library.util.ArtDetailViewport;\nimport com.roger.missview.library.util.BitmapRegionLoader;\nimport com.roger.missview.library.util.ImageBlurrer;\nimport com.roger.missview.library.util.ImageUtil;\nimport com.roger.missview.library.util.MathUtil;\nimport com.roger.missview.library.util.TickingFloatAnimator;\n\nimport javax.microedition.khronos.egl.EGLConfig;\nimport javax.microedition.khronos.opengles.GL10;\n\npublic class BlurRenderer implements GLSurfaceView.Renderer {\n\n    private static final int CROSSFADE_ANIMATION_DURATION = 750;\n    private static final int BLUR_ANIMATION_DURATION = 750;\n\n    public static final int DEFAULT_BLUR = 250; // max 500\n    public static final int DEFAULT_GREY = 0; // max 500\n    public static final int DEMO_DIM = 64;\n    public static final int DEFAULT_MAX_DIM = 128; // technical max 255\n    public static final float DIM_RANGE = 0.5f; // percent of max dim\n\n    private boolean mDemoMode;\n    private boolean mPreview;\n    private int mMaxPrescaledBlurPixels;\n    private int mBlurKeyframes = 3;\n    private int mBlurredSampleSize;\n    private int mMaxDim;\n    private int mMaxGrey;\n\n    // Model and view matrices. Projection and MVP stored in picture set\n    private final float[] mMMatrix = new float[16];\n    private final float[] mVMatrix = new float[16];\n\n    private Callbacks mCallbacks;\n\n    private float mAspectRatio;\n    private int mHeight;\n\n    private GLPictureSet mCurrentGLPictureSet;\n    private GLPictureSet mNextGLPictureSet;\n    private GLColorOverlay mColorOverlay;\n\n    private BitmapRegionLoader mQueuedNextBitmapRegionLoader;\n\n    private boolean mSurfaceCreated;\n\n    private volatile float mNormalOffsetX;\n    private volatile RectF mCurrentViewport = new RectF(); // [-1, -1] to [1, 1], flipped\n\n    private Context mContext;\n\n    private boolean mIsBlurred = true;\n    private boolean mBlurRelatedToArtDetailMode = false;\n    private Interpolator mBlurInterpolator = new AccelerateDecelerateInterpolator();\n    private TickingFloatAnimator mBlurAnimator;\n    private TickingFloatAnimator mCrossfadeAnimator = TickingFloatAnimator.create().from(0);\n\n    public BlurRenderer(Context context, Callbacks callbacks) {\n        mContext = context;\n        mCallbacks = callbacks;\n\n        mBlurKeyframes = getNumberOfKeyframes();\n        mBlurAnimator = TickingFloatAnimator.create().from(mBlurKeyframes);\n\n        mCurrentGLPictureSet = new GLPictureSet(0);\n        mNextGLPictureSet = new GLPictureSet(1); // for transitioning to next pictures\n        setNormalOffsetX(0);\n        recomputeMaxPrescaledBlurPixels();\n        recomputeMaxDimAmount();\n        recomputeGreyAmount();\n    }\n\n    @TargetApi(Build.VERSION_CODES.KITKAT)\n    private int getNumberOfKeyframes() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n            ActivityManager activityManager = (ActivityManager)\n                    mContext.getSystemService(Context.ACTIVITY_SERVICE);\n            if (activityManager.isLowRamDevice()) {\n                return 1;\n            }\n        }\n\n        return 5;\n    }\n\n    public void recomputeMaxPrescaledBlurPixels() {\n        // Compute blur sizes\n        float maxBlurRadiusOverScreenHeight = PreferenceManager\n                .getDefaultSharedPreferences(mContext).getInt(\"blur_amount\", DEFAULT_BLUR)\n                * 0.0001f;\n        DisplayMetrics dm = mContext.getResources().getDisplayMetrics();\n        int maxBlurPx = (int) (dm.heightPixels * maxBlurRadiusOverScreenHeight);\n        mBlurredSampleSize = 4;\n        while (maxBlurPx / mBlurredSampleSize > ImageBlurrer.MAX_SUPPORTED_BLUR_PIXELS) {\n            mBlurredSampleSize <<= 1;\n        }\n        mMaxPrescaledBlurPixels = maxBlurPx / mBlurredSampleSize;\n    }\n\n    public void recomputeMaxDimAmount() {\n        mMaxDim = PreferenceManager\n                .getDefaultSharedPreferences(mContext).getInt(\"dim_amount\", DEFAULT_MAX_DIM);\n    }\n\n    public void recomputeGreyAmount() {\n        mMaxGrey = PreferenceManager\n                .getDefaultSharedPreferences(mContext).getInt(\"grey_amount\", DEFAULT_GREY);\n    }\n\n    public void onSurfaceCreated(GL10 unused, EGLConfig config) {\n        mSurfaceCreated = false;\n        GLES20.glEnable(GLES20.GL_BLEND);\n//        GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);\n        GLES20.glBlendFuncSeparate(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA,\n                GLES20.GL_ONE, GLES20.GL_ONE);\n        GLES20.glClearColor(0, 0, 0, 0);\n\n        // Set the camera position (View matrix)\n        Matrix.setLookAtM(mVMatrix, 0,\n                0, 0, 1,\n                0, 0, -1,\n                0, 1, 0);\n\n        GLColorOverlay.initGl();\n        GLPicture.initGl();\n\n        mColorOverlay = new GLColorOverlay(0);\n\n        mSurfaceCreated = true;\n        if (mQueuedNextBitmapRegionLoader != null) {\n            BitmapRegionLoader loader = mQueuedNextBitmapRegionLoader;\n            mQueuedNextBitmapRegionLoader = null;\n            setAndConsumeBitmapRegionLoader(loader);\n        }\n    }\n\n    public void onSurfaceChanged(GL10 unused, int width, int height) {\n        GLES20.glViewport(0, 0, width, height);\n        hintViewportSize(width, height);\n        if (!mDemoMode && !mPreview) {\n            // Reset art detail viewports\n            ArtDetailViewport.getInstance().setViewport(0, 0, 0, 0, 0, false);\n            ArtDetailViewport.getInstance().setViewport(1, 0, 0, 0, 0, false);\n        }\n        mCurrentGLPictureSet.recomputeTransformMatrices();\n        mNextGLPictureSet.recomputeTransformMatrices();\n        recomputeMaxPrescaledBlurPixels();\n    }\n\n    public void hintViewportSize(int width, int height) {\n        mHeight = height;\n        mAspectRatio = width * 1f / height;\n    }\n\n    public void onDrawFrame(GL10 unused) {\n        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);\n\n        Matrix.setIdentityM(mMMatrix, 0);\n\n        boolean stillAnimating = mCrossfadeAnimator.tick();\n        stillAnimating |= mBlurAnimator.tick();\n\n        if (mBlurRelatedToArtDetailMode) {\n            mCurrentGLPictureSet.recomputeTransformMatrices();\n            mNextGLPictureSet.recomputeTransformMatrices();\n        }\n\n        float dimAmount = mCurrentGLPictureSet.mDimAmount;\n        mCurrentGLPictureSet.drawFrame(1);\n        if (mCrossfadeAnimator.isRunning()) {\n            dimAmount = MathUtil.interpolate(dimAmount, mNextGLPictureSet.mDimAmount,\n                    mCrossfadeAnimator.currentValue());\n            mNextGLPictureSet.drawFrame(mCrossfadeAnimator.currentValue());\n        }\n\n        mColorOverlay.setColor(Color.argb((int) (dimAmount\n                * mBlurAnimator.currentValue() / mBlurKeyframes), 0, 0, 0));\n        mColorOverlay.draw(mMMatrix); // don't need any perspective or anything for color overlay\n\n        if (stillAnimating) {\n            mCallbacks.requestRender();\n        }\n    }\n\n    public void setNormalOffsetX(float x) {\n        mNormalOffsetX = MathUtil.constrain(0, 1, x);\n        onViewportChanged();\n    }\n\n    private void onViewportChanged() {\n        mCurrentGLPictureSet.recomputeTransformMatrices();\n        mNextGLPictureSet.recomputeTransformMatrices();\n        if (mSurfaceCreated) {\n            mCallbacks.requestRender();\n        }\n    }\n\n    private float blurRadiusAtFrame(float f) {\n        return mMaxPrescaledBlurPixels * mBlurInterpolator.getInterpolation(f / mBlurKeyframes);\n    }\n\n    public void setAndConsumeBitmapRegionLoader(final BitmapRegionLoader bitmapRegionLoader) {\n        if (!mSurfaceCreated) {\n            mQueuedNextBitmapRegionLoader = bitmapRegionLoader;\n            return;\n        }\n\n        if (mCrossfadeAnimator.isRunning()) {\n            if (mQueuedNextBitmapRegionLoader != null) {\n                mQueuedNextBitmapRegionLoader.destroy();\n            }\n            mQueuedNextBitmapRegionLoader = bitmapRegionLoader;\n            return;\n        }\n\n\n        mNextGLPictureSet.load(bitmapRegionLoader);\n\n        mCrossfadeAnimator\n                .from(0).to(1)\n                .withDuration(CROSSFADE_ANIMATION_DURATION)\n                .withEndListener(new Runnable() {\n                    @Override\n                    public void run() {\n                        // swap current and next picturesets\n                        final GLPictureSet oldGLPictureSet = mCurrentGLPictureSet;\n                        mCurrentGLPictureSet = mNextGLPictureSet;\n                        mNextGLPictureSet = new GLPictureSet(oldGLPictureSet.mId);\n                        mCallbacks.requestRender();\n                        oldGLPictureSet.destroyPictures();\n                        System.gc();\n                        if (mQueuedNextBitmapRegionLoader != null) {\n                            BitmapRegionLoader queuedNextBitmapRegionLoader\n                                    = mQueuedNextBitmapRegionLoader;\n                            mQueuedNextBitmapRegionLoader = null;\n                            setAndConsumeBitmapRegionLoader(queuedNextBitmapRegionLoader);\n                        }\n                    }\n                })\n                .start();\n        mCallbacks.requestRender();\n    }\n\n    public void setDemoMode(boolean demoMode) {\n        mDemoMode = demoMode;\n    }\n    public void setIsPreview(boolean preview) {\n        mPreview = preview;\n    }\n\n    private class GLPictureSet {\n        private int mId;\n        private volatile float[] mPMatrix = new float[16];\n        private final float[] mMVPMatrix = new float[16];\n        private GLPicture[] mPictures = new GLPicture[mBlurKeyframes + 1];\n        private boolean mHasBitmap = false;\n        private float mBitmapAspectRatio = 1f;\n        private int mDimAmount = 0;\n\n        public GLPictureSet(int id) {\n            mId = id;\n        }\n\n        public void load(BitmapRegionLoader bitmapRegionLoader) {\n            mHasBitmap = (bitmapRegionLoader != null);\n            mBitmapAspectRatio = mHasBitmap\n                    ? bitmapRegionLoader.getWidth() * 1f / bitmapRegionLoader.getHeight()\n                    : 1f;\n\n            mDimAmount = DEFAULT_MAX_DIM;\n\n            destroyPictures();\n\n            if (bitmapRegionLoader != null) {\n                BitmapFactory.Options options = new BitmapFactory.Options();\n                Rect rect = new Rect();\n                int originalWidth = bitmapRegionLoader.getWidth();\n                int originalHeight = bitmapRegionLoader.getHeight();\n\n                // Calculate image darkness to determine dim amount\n                rect.set(0, 0, originalWidth, originalHeight);\n                options.inSampleSize = ImageUtil.calculateSampleSize(originalHeight, 64);\n                Bitmap tempBitmap = bitmapRegionLoader.decodeRegion(rect, options);\n                float darkness = ImageUtil.calculateDarkness(tempBitmap);\n                mDimAmount = mDemoMode\n                        ? DEMO_DIM\n                        : (int) (mMaxDim * ((1 - DIM_RANGE) + DIM_RANGE * Math.sqrt(darkness)));\n                tempBitmap.recycle();\n\n                // Create the GLPicture objects\n                mPictures[0] = new GLPicture(bitmapRegionLoader, mHeight);\n                if (mMaxPrescaledBlurPixels == 0) {\n                    for (int f = 1; f <= mBlurKeyframes; f++) {\n                        mPictures[f] = mPictures[0];\n                    }\n                } else {\n                    ImageBlurrer blurrer = new ImageBlurrer(mContext);\n                    // To blur, first load the entire bitmap region, but at a very large\n                    // sample size that's appropriate for the final blurred image\n                    options.inSampleSize = ImageUtil.calculateSampleSize(\n                            originalHeight, mHeight / mBlurredSampleSize);\n                    rect.set(0, 0, originalWidth, originalHeight);\n                    tempBitmap = bitmapRegionLoader.decodeRegion(rect, options);\n\n                    // Next, create a scaled down version of the bitmap so that the blur radius\n                    // looks appropriate (tempBitmap will likely be bigger than the final blurred\n                    // bitmap, and thus the blur may look smaller if we just used tempBitmap as\n                    // the final blurred bitmap).\n\n                    // Note that image width should be a multiple of 4 to avoid\n                    // issues with RenderScript allocations.\n                    int scaledHeight = Math.max(2, MathUtil.floorEven(\n                            mHeight / mBlurredSampleSize));\n                    int scaledWidth = Math.max(4, MathUtil.roundMult4(\n                            (int) (scaledHeight * mBitmapAspectRatio)));\n                    Bitmap scaledBitmap = Bitmap.createScaledBitmap(\n                            tempBitmap, scaledWidth, scaledHeight, true);\n\n                    tempBitmap.recycle();\n\n                    // And finally, create a blurred copy for each keyframe.\n                    for (int f = 1; f <= mBlurKeyframes; f++) {\n                        float desaturateAmount = mMaxGrey / 500f * f / mBlurKeyframes;\n                        Bitmap blurredBitmap = blurrer.blurBitmap(\n                                scaledBitmap, blurRadiusAtFrame(f), desaturateAmount);\n                        mPictures[f] = new GLPicture(blurredBitmap);\n                        blurredBitmap.recycle();\n                    }\n\n                    scaledBitmap.recycle();\n                    blurrer.destroy();\n                }\n            }\n\n            recomputeTransformMatrices();\n            mCallbacks.requestRender();\n        }\n\n        private void recomputeTransformMatrices() {\n            float screenToBitmapAspectRatio = mAspectRatio / mBitmapAspectRatio;\n\n            // Ensure the bitmap is wider than the screen relatively by applying zoom\n            // if necessary. Vary width but keep height the same.\n            float zoom = Math.max(1f, 1.15f * screenToBitmapAspectRatio);\n\n            // Total scale factors in both zoom and scale due to aspect ratio.\n            float totalScale = zoom / screenToBitmapAspectRatio;\n\n            mCurrentViewport.left = MathUtil.interpolate(\n                    -1f * Math.min(1f, screenToBitmapAspectRatio), // remove screenToBitmapAspectRatio to unconstrain panning amount\n                    1f * Math.min(1f, screenToBitmapAspectRatio),\n                    mNormalOffsetX * (totalScale - 1) / totalScale);\n            mCurrentViewport.right = mCurrentViewport.left + 2f / totalScale;\n            mCurrentViewport.bottom = -1f / zoom;\n            mCurrentViewport.top = 1f / zoom;\n\n            float focusAmount = (mBlurKeyframes - mBlurAnimator.currentValue()) / mBlurKeyframes;\n            if (mBlurRelatedToArtDetailMode && focusAmount > 0) {\n                RectF artDetailViewport = ArtDetailViewport.getInstance().getViewport(mId);\n                if (artDetailViewport.width() == 0 || artDetailViewport.height() == 0) {\n                    if (!mDemoMode && !mPreview) {\n                        // reset art detail viewport\n                        ArtDetailViewport.getInstance().setViewport(mId,\n                                MathUtil.uninterpolate(-1, 1, mCurrentViewport.left),\n                                MathUtil.uninterpolate(1, -1, mCurrentViewport.top),\n                                MathUtil.uninterpolate(-1, 1, mCurrentViewport.right),\n                                MathUtil.uninterpolate(1, -1, mCurrentViewport.bottom),\n                                false);\n                    }\n                } else {\n                    // interpolate\n                    mCurrentViewport.left = MathUtil.interpolate(\n                            mCurrentViewport.left,\n                            MathUtil.interpolate(-1, 1, artDetailViewport.left),\n                            focusAmount);\n                    mCurrentViewport.top = MathUtil.interpolate(\n                            mCurrentViewport.top,\n                            MathUtil.interpolate(1, -1, artDetailViewport.top),\n                            focusAmount);\n                    mCurrentViewport.right = MathUtil.interpolate(\n                            mCurrentViewport.right,\n                            MathUtil.interpolate(-1, 1, artDetailViewport.right),\n                            focusAmount);\n                    mCurrentViewport.bottom = MathUtil.interpolate(\n                            mCurrentViewport.bottom,\n                            MathUtil.interpolate(1, -1, artDetailViewport.bottom),\n                            focusAmount);\n                }\n            }\n\n            Matrix.orthoM(mPMatrix, 0,\n                    mCurrentViewport.left, mCurrentViewport.right,\n                    mCurrentViewport.bottom, mCurrentViewport.top,\n                    1, 10);\n        }\n\n        public void drawFrame(float globalAlpha) {\n            if (!mHasBitmap) {\n                return;\n            }\n\n            Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, mMMatrix, 0);\n            Matrix.multiplyMM(mMVPMatrix, 0, mPMatrix, 0, mMVPMatrix, 0);\n\n            float blurFrame = mBlurAnimator.currentValue();\n            int lo = (int) Math.floor(blurFrame);\n            int hi = (int) Math.ceil(blurFrame);\n\n            float localHiAlpha = (blurFrame - lo);\n            if (globalAlpha <= 0) {\n                // Nothing to draw\n            } else if (lo == hi) {\n                // Just draw one\n                mPictures[lo].draw(mMVPMatrix, globalAlpha);\n            } else if (globalAlpha == 1) {\n                // Simple drawing\n                mPictures[lo].draw(mMVPMatrix, 1);\n                mPictures[hi].draw(mMVPMatrix, localHiAlpha);\n            } else {\n                // If there's both a global and local alpha, re-compose alphas, to\n                // effectively compose hi and lo before composing the result\n                // with the background.\n                //\n                // The math, where a1,a2 are previous alphas and b1,b2 are new alphas:\n                //   b1 = a1 * (a2 - 1) / (a1 * a2 - 1)\n                //   b2 = a1 * a2\n                float newLocalLoAlpha = globalAlpha * (localHiAlpha - 1)\n                        / (globalAlpha * localHiAlpha - 1);\n                float newLocalHiAlpha = globalAlpha * localHiAlpha;\n                mPictures[lo].draw(mMVPMatrix, newLocalLoAlpha);\n                mPictures[hi].draw(mMVPMatrix, newLocalHiAlpha);\n            }\n        }\n\n        public void destroyPictures() {\n            for (int i = 0; i < mPictures.length; i++) {\n                if (mPictures[i] == null) {\n                    continue;\n                }\n\n                mPictures[i].destroy();\n                mPictures[i] = null;\n            }\n        }\n    }\n\n    public void destroy() {\n        mCurrentGLPictureSet.destroyPictures();\n        mNextGLPictureSet.destroyPictures();\n    }\n\n    public boolean isBlurred() {\n        return mIsBlurred;\n    }\n\n    public void setIsBlurred(final boolean isBlurred, final boolean artDetailMode) {\n        if (artDetailMode && !isBlurred && !mDemoMode && !mPreview) {\n            // Reset art detail viewport\n            ArtDetailViewport.getInstance().setViewport(0, 0, 0, 0, 0, false);\n            ArtDetailViewport.getInstance().setViewport(1, 0, 0, 0, 0, false);\n        }\n\n        mBlurRelatedToArtDetailMode = artDetailMode;\n        mIsBlurred = isBlurred;\n        mBlurAnimator.cancel();\n        mBlurAnimator\n                .to(isBlurred ? mBlurKeyframes : 0)\n                .withDuration(BLUR_ANIMATION_DURATION * (mDemoMode ? 5 : 1))\n                .withEndListener(new Runnable() {\n                    @Override\n                    public void run() {\n                        if (isBlurred && artDetailMode) {\n                            System.gc();\n                        }\n                    }\n                })\n                .start();\n        mCallbacks.requestRender();\n    }\n\n    public static interface Callbacks {\n        void requestRender();\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/com/roger/missview/library/DemoRenderController.java",
    "content": "/*\n * Copyright 2014 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.roger.missview.library;\n\nimport android.animation.Animator;\nimport android.animation.AnimatorListenerAdapter;\nimport android.animation.ObjectAnimator;\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.os.Handler;\nimport com.roger.missview.library.util.BitmapRegionLoader;\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\n\npublic class DemoRenderController extends RenderController {\n\n  private final Handler mHandler = new Handler();\n\n  private static final long ANIMATION_CYCLE_TIME_MILLIS = 35000;\n  private static final long FOCUS_DELAY_TIME_MILLIS = 2000;\n  private static final long FOCUS_TIME_MILLIS = 6000;\n\n  private Animator mCurrentScrollAnimator;\n  private boolean mReverseDirection = false;\n  private boolean mAllowFocus = true;\n  private String mPictureName;\n  private Bitmap mBitmap;\n\n  public DemoRenderController(Context context, BlurRenderer renderer, Callbacks callbacks,\n      boolean allowFocus, String mPictureName) {\n    super(context, renderer, callbacks);\n    mAllowFocus = allowFocus;\n    this.mPictureName = mPictureName;\n    runAnimation();\n  }\n\n  public DemoRenderController(Context context, BlurRenderer renderer, Callbacks callbacks,\n      boolean allowFocus, Bitmap mBitmap) {\n    super(context, renderer, callbacks);\n    mAllowFocus = allowFocus;\n    this.mBitmap = mBitmap;\n    runAnimation();\n  }\n\n  private void runAnimation() {\n    if (mCurrentScrollAnimator != null) {\n      mCurrentScrollAnimator.cancel();\n    }\n\n    mCurrentScrollAnimator =\n        ObjectAnimator.ofFloat(mRenderer, \"normalOffsetX\", mReverseDirection ? 1f : 0f,\n            mReverseDirection ? 0f : 1f).setDuration(ANIMATION_CYCLE_TIME_MILLIS);\n    mCurrentScrollAnimator.start();\n    mCurrentScrollAnimator.addListener(new AnimatorListenerAdapter() {\n      @Override public void onAnimationEnd(Animator animation) {\n        super.onAnimationEnd(animation);\n        mReverseDirection = !mReverseDirection;\n        runAnimation();\n      }\n    });\n    if (mAllowFocus) {\n      mHandler.postDelayed(new Runnable() {\n        @Override public void run() {\n          mRenderer.setIsBlurred(false, false);\n          mHandler.postDelayed(new Runnable() {\n            @Override public void run() {\n              mRenderer.setIsBlurred(true, false);\n            }\n          }, FOCUS_TIME_MILLIS);\n        }\n      }, FOCUS_DELAY_TIME_MILLIS);\n    }\n  }\n\n  @Override public void destroy() {\n    super.destroy();\n    if (mCurrentScrollAnimator != null) {\n      mCurrentScrollAnimator.cancel();\n    }\n    mHandler.removeCallbacksAndMessages(null);\n  }\n\n  @Override protected BitmapRegionLoader openDownloadedCurrentArtwork(boolean forceReload) {\n    try {\n      if (mBitmap != null) {\n        return BitmapRegionLoader.newInstance(Bitmap2IS(mBitmap));\n      } else if (mPictureName != null) {\n        InputStream m = new FileInputStream(mPictureName);\n        return BitmapRegionLoader.newInstance(m);\n      }\n    } catch (IOException e) {\n      e.printStackTrace();\n      return null;\n    }\n    return null;\n  }\n\n  private InputStream Bitmap2IS(Bitmap bm) {\n    ByteArrayOutputStream baos = new ByteArrayOutputStream();\n    bm.compress(Bitmap.CompressFormat.JPEG, 100, baos);\n    InputStream sbs = new ByteArrayInputStream(baos.toByteArray());\n    return sbs;\n  }\n}\n"
  },
  {
    "path": "library/src/main/java/com/roger/missview/library/GLColorOverlay.java",
    "content": "/*\n * Copyright 2014 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.roger.missview.library;\n\nimport android.graphics.Color;\nimport android.opengl.GLES20;\n\nimport java.nio.FloatBuffer;\n\nclass GLColorOverlay {\n    private static final String VERTEX_SHADER_CODE = \"\" +\n            // This matrix member variable provides a hook to manipulate\n            // the coordinates of the objects that use this vertex shader\n            \"uniform mat4 uMVPMatrix;\" +\n            \"attribute vec4 aPosition;\" +\n            \"void main(){\" +\n            \"  gl_Position = uMVPMatrix * aPosition;\" +\n            \"}\";\n\n    private static final String FRAGMENT_SHADER_CODE = \"\" +\n            \"precision mediump float;\" +\n            \"uniform sampler2D uTexture;\" +\n            \"uniform vec4 uColor;\" +\n            \"void main(){\" +\n            \"  gl_FragColor = uColor;\" +\n            \"}\";\n\n    // number of coordinates per vertex in this array\n    private static final int COORDS_PER_VERTEX = 3;\n    private static final int VERTEX_STRIDE_BYTES = COORDS_PER_VERTEX * GLUtil.BYTES_PER_FLOAT;\n\n    private float mVertices[] = {\n            -1,  1, 0,   // top left\n            -1, -1, 0,   // bottom left\n             1, -1, 0,   // bottom right\n\n            -1,  1, 0,   // top left\n             1, -1, 0,   // bottom right\n             1,  1, 0,   // top right\n    };\n\n    private int mColor;\n\n    private FloatBuffer mVertexBuffer;\n\n    private static int sProgramHandle;\n    private static int sAttribPositionHandle;\n    private static int sUniformColorHandle;\n    private static int sUniformMVPMatrixHandle;\n\n    public GLColorOverlay(int color) {\n        mColor = color;\n\n        mVertexBuffer = GLUtil.asFloatBuffer(mVertices);\n    }\n\n    public static void initGl() {\n        // Initialize shaders and create/link program\n        int vertexShaderHandle = GLUtil.loadShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_CODE);\n        int fragShaderHandle = GLUtil.loadShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_CODE);\n\n        sProgramHandle = GLUtil.createAndLinkProgram(vertexShaderHandle, fragShaderHandle, null);\n        sAttribPositionHandle = GLES20.glGetAttribLocation(sProgramHandle, \"aPosition\");\n        sUniformMVPMatrixHandle = GLES20.glGetUniformLocation(sProgramHandle, \"uMVPMatrix\");\n        sUniformColorHandle = GLES20.glGetUniformLocation(sProgramHandle, \"uColor\");\n    }\n\n    public void draw(float[] mvpMatrix) {\n        // Add program to OpenGL ES environment\n        GLES20.glUseProgram(sProgramHandle);\n\n        // Pass in the vertex information\n        GLES20.glEnableVertexAttribArray(sAttribPositionHandle);\n        GLES20.glVertexAttribPointer(sAttribPositionHandle,\n                COORDS_PER_VERTEX, GLES20.GL_FLOAT, false,\n                VERTEX_STRIDE_BYTES, mVertexBuffer);\n\n        // Apply the projection and view transformation\n        GLES20.glUniformMatrix4fv(sUniformMVPMatrixHandle, 1, false, mvpMatrix, 0);\n        GLUtil.checkGlError(\"glUniformMatrix4fv\");\n\n        // Set the alpha\n        float r = Color.red(mColor) * 1f / 255;\n        float g = Color.green(mColor) * 1f / 255;\n        float b = Color.blue(mColor) * 1f / 255;\n        float a = Color.alpha(mColor) * 1f / 255;\n        GLES20.glUniform4f(sUniformColorHandle, r, g, b, a);\n\n        // Draw the triangle\n        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, mVertices.length / COORDS_PER_VERTEX);\n\n        GLES20.glDisableVertexAttribArray(sAttribPositionHandle);\n    }\n\n    public void setColor(int color) {\n        mColor = color;\n    }\n\n    public void destroy() {\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/com/roger/missview/library/GLPicture.java",
    "content": "/*\n * Copyright 2014 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.roger.missview.library;\n\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.graphics.Rect;\nimport android.opengl.GLES20;\n\nimport com.roger.missview.library.util.BitmapRegionLoader;\n\nimport java.nio.FloatBuffer;\n\nclass GLPicture {\n    private static final String VERTEX_SHADER_CODE = \"\" +\n            // This matrix member variable provides a hook to manipulate\n            // the coordinates of the objects that use this vertex shader\n            \"uniform mat4 uMVPMatrix;\" +\n            \"attribute vec4 aPosition;\" +\n            \"attribute vec2 aTexCoords;\" +\n            \"varying vec2 vTexCoords;\" +\n            \"void main(){\" +\n            \"  vTexCoords = aTexCoords;\" +\n            \"  gl_Position = uMVPMatrix * aPosition;\" +\n            \"}\";\n\n    private static final String FRAGMENT_SHADER_CODE = \"\" +\n            \"precision mediump float;\" +\n            \"uniform sampler2D uTexture;\" +\n            \"uniform float uAlpha;\" +\n            \"varying vec2 vTexCoords;\" +\n            \"void main(){\" +\n            \"  gl_FragColor = texture2D(uTexture, vTexCoords);\" +\n            \"  gl_FragColor.a = uAlpha;\" +\n            \"}\";\n\n    // number of coordinates per vertex in this array\n    private static final int COORDS_PER_VERTEX = 3;\n    private static final int VERTEX_STRIDE_BYTES = COORDS_PER_VERTEX * GLUtil.BYTES_PER_FLOAT;\n    private static final int VERTICES = 6; // TL, BL, BR, TL, BR, TR\n\n    // S, T (or X, Y)\n    private static final int COORDS_PER_TEXTURE_VERTEX = 2;\n    private static final int TEXTURE_VERTEX_STRIDE_BYTES = COORDS_PER_TEXTURE_VERTEX\n            * GLUtil.BYTES_PER_FLOAT;\n\n    private static final float[] SQUARE_TEXTURE_VERTICES = {\n            0, 0, // top left\n            0, 1, // bottom left\n            1, 1, // bottom right\n\n            0, 0, // top left\n            1, 1, // bottom right\n            1, 0, // top right\n    };\n\n    private boolean mHasContent = false;\n\n    private float[] mVertices = new float[COORDS_PER_VERTEX * VERTICES];\n    private FloatBuffer mVertexBuffer;\n    private FloatBuffer mTextureCoordsBuffer;\n\n    private static int sMaxTextureSize;\n\n    private static int sProgramHandle;\n    private static int sAttribPositionHandle;\n    private static int sAttribTextureCoordsHandle;\n    private static int sUniformAlphaHandle;\n    private static int sUniformTextureHandle;\n    private static int sUniformMVPMatrixHandle;\n\n    private int mCols = 1;\n    private int mRows = 1;\n    private int mWidth = 0;\n    private int mHeight = 0;\n    private int mTileSize = sMaxTextureSize;\n    private int[] mTextureHandles;\n\n    public static void initGl() {\n        // Initialize shaders and create/link program\n        int vertexShaderHandle = GLUtil.loadShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER_CODE);\n        int fragShaderHandle = GLUtil.loadShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_CODE);\n\n        sProgramHandle = GLUtil.createAndLinkProgram(vertexShaderHandle, fragShaderHandle, null);\n        sAttribPositionHandle = GLES20.glGetAttribLocation(sProgramHandle, \"aPosition\");\n        sAttribTextureCoordsHandle = GLES20.glGetAttribLocation(sProgramHandle, \"aTexCoords\");\n        sUniformMVPMatrixHandle = GLES20.glGetUniformLocation(sProgramHandle, \"uMVPMatrix\");\n        sUniformTextureHandle = GLES20.glGetUniformLocation(sProgramHandle, \"uTexture\");\n        sUniformAlphaHandle = GLES20.glGetUniformLocation(sProgramHandle, \"uAlpha\");\n\n        // Compute max texture size\n        int[] maxTextureSize = new int[1];\n        GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxTextureSize, 0);\n        sMaxTextureSize = maxTextureSize[0];\n    }\n\n    public GLPicture(BitmapRegionLoader bitmapRegionLoader, int maxHeight) {\n        if (bitmapRegionLoader == null || maxHeight == 0) {\n            return;\n        }\n\n        mHasContent = true;\n        mVertexBuffer = GLUtil.newFloatBuffer(mVertices.length);\n        mTextureCoordsBuffer = GLUtil.asFloatBuffer(SQUARE_TEXTURE_VERTICES);\n\n        int originalWidth = bitmapRegionLoader.getWidth();\n        int originalHeight = bitmapRegionLoader.getHeight();\n        int sampleSize = 1;\n        while (originalHeight / (sampleSize << 1) > maxHeight) {\n            sampleSize <<= 1;\n        }\n\n        mWidth = originalWidth / sampleSize;\n        mHeight = originalHeight / sampleSize;\n\n        mTileSize = Math.min(512, sMaxTextureSize);\n        int unsampledTileSize = mTileSize * sampleSize;\n        int leftoverHeight = originalHeight % unsampledTileSize;\n\n        // Load m x n textures\n        mCols = mWidth / (mTileSize + 1) + 1;\n        mRows = mHeight / (mTileSize + 1) + 1;\n\n        mTextureHandles = new int[mCols * mRows];\n\n        Bitmap tileBitmap = Bitmap.createBitmap(mTileSize, mTileSize, Bitmap.Config.ARGB_8888);\n        Rect rect = new Rect();\n        BitmapFactory.Options options = new BitmapFactory.Options();\n        options.inSampleSize = sampleSize;\n        options.inBitmap = tileBitmap;\n        for (int y = 0; y < mRows; y++) {\n            for (int x = 0; x < mCols; x++) {\n                rect.set(x * unsampledTileSize,\n                        (mRows - y - 1) * unsampledTileSize,\n                        (x + 1) * unsampledTileSize,\n                        (mRows - y) * unsampledTileSize);\n                // The bottom tiles must be full tiles for drawing, so only allow edge tiles\n                // at the top\n                if (leftoverHeight > 0) {\n                    rect.offset(0, -unsampledTileSize + leftoverHeight);\n                }\n                rect.intersect(0, 0, originalWidth, originalHeight);\n                Bitmap useBitmap = bitmapRegionLoader.decodeRegion(rect, options);\n                mTextureHandles[y * mCols + x] = GLUtil.loadTexture(useBitmap);\n                if (useBitmap != tileBitmap) {\n                    useBitmap.recycle();\n                }\n            }\n        }\n    }\n\n    public GLPicture(Bitmap bitmap) {\n        if (bitmap == null) {\n            return;\n        }\n\n        mTileSize = sMaxTextureSize;\n        mHasContent = true;\n        mVertexBuffer = GLUtil.newFloatBuffer(mVertices.length);\n        mTextureCoordsBuffer = GLUtil.asFloatBuffer(SQUARE_TEXTURE_VERTICES);\n\n        mWidth = bitmap.getWidth();\n        mHeight = bitmap.getHeight();\n        int leftoverHeight = mHeight % mTileSize;\n\n        // Load m x n textures\n        mCols = mWidth / (mTileSize + 1) + 1;\n        mRows = mHeight / (mTileSize + 1) + 1;\n\n        mTextureHandles = new int[mCols * mRows];\n        if (mCols == 1 && mRows == 1) {\n            mTextureHandles[0] = GLUtil.loadTexture(bitmap);\n        } else {\n            Rect rect = new Rect();\n            for (int y = 0; y < mRows; y++) {\n                for (int x = 0; x < mCols; x++) {\n                    rect.set(x * mTileSize,\n                            (mRows - y - 1) * mTileSize,\n                            (x + 1) * mTileSize,\n                            (mRows - y) * mTileSize);\n                    // The bottom tiles must be full tiles for drawing, so only allow edge tiles\n                    // at the top\n                    if (leftoverHeight > 0) {\n                        rect.offset(0, -mTileSize + leftoverHeight);\n                    }\n                    rect.intersect(0, 0, mWidth, mHeight);\n                    Bitmap subBitmap = Bitmap.createBitmap(bitmap,\n                            rect.left, rect.top, rect.width(), rect.height());\n                    mTextureHandles[y * mCols + x] = GLUtil.loadTexture(subBitmap);\n                    subBitmap.recycle();\n                }\n            }\n        }\n    }\n\n    public void draw(float[] mvpMatrix, float alpha) {\n        if (!mHasContent) {\n            return;\n        }\n\n        // Add program to OpenGL ES environment\n        GLES20.glUseProgram(sProgramHandle);\n\n        // Apply the projection and view transformation\n        GLES20.glUniformMatrix4fv(sUniformMVPMatrixHandle, 1, false, mvpMatrix, 0);\n        GLUtil.checkGlError(\"glUniformMatrix4fv\");\n\n        // Set up vertex buffer\n        GLES20.glEnableVertexAttribArray(sAttribPositionHandle);\n        GLES20.glVertexAttribPointer(sAttribPositionHandle,\n                COORDS_PER_VERTEX, GLES20.GL_FLOAT, false,\n                VERTEX_STRIDE_BYTES, mVertexBuffer);\n\n        // Set up texture stuff\n        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);\n        GLES20.glUniform1i(sUniformTextureHandle, 0);\n        GLES20.glVertexAttribPointer(sAttribTextureCoordsHandle,\n                COORDS_PER_TEXTURE_VERTEX, GLES20.GL_FLOAT, false,\n                TEXTURE_VERTEX_STRIDE_BYTES, mTextureCoordsBuffer);\n        GLES20.glEnableVertexAttribArray(sAttribTextureCoordsHandle);\n\n        // Set the alpha\n        GLES20.glUniform1f(sUniformAlphaHandle, alpha);\n\n        // Draw tiles\n        for (int y = 0; y < mRows; y++) {\n            for (int x = 0; x < mCols; x++) {\n                // Pass in the vertex information\n                mVertices[0] = mVertices[3] = mVertices[9]\n                        = Math.min(-1 + 2f * x * mTileSize / mWidth, 1); // left\n                mVertices[1] = mVertices[10] = mVertices[16]\n                        = Math.min(-1 + 2f * (y + 1) * mTileSize / mHeight, 1); // top\n                mVertices[6] = mVertices[12] = mVertices[15]\n                        = Math.min(-1 + 2f * (x + 1) * mTileSize / mWidth, 1); // right\n                mVertices[4] = mVertices[7] = mVertices[13]\n                        = Math.min(-1 + 2f * y * mTileSize / mHeight, 1); // bottom\n                mVertexBuffer.put(mVertices);\n                mVertexBuffer.position(0);\n\n                GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,\n                        mTextureHandles[y * mCols + x]);\n                GLUtil.checkGlError(\"glBindTexture\");\n\n                // Draw the two triangles\n                GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, mVertices.length / COORDS_PER_VERTEX);\n            }\n        }\n\n        GLES20.glDisableVertexAttribArray(sAttribPositionHandle);\n        GLES20.glDisableVertexAttribArray(sAttribTextureCoordsHandle);\n    }\n\n    public void destroy() {\n        if (mTextureHandles != null) {\n            GLES20.glDeleteTextures(mTextureHandles.length, mTextureHandles, 0);\n            GLUtil.checkGlError(\"Destroy picture\");\n            mTextureHandles = null;\n        }\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/com/roger/missview/library/GLTextureView.java",
    "content": "/*\n * Copyright 2014 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.roger.missview.library;\n\nimport android.content.Context;\nimport android.graphics.SurfaceTexture;\nimport android.opengl.GLDebugHelper;\nimport android.opengl.GLSurfaceView;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.SurfaceHolder;\nimport android.view.TextureView;\n\nimport java.io.Writer;\nimport java.lang.ref.WeakReference;\nimport java.util.ArrayList;\n\nimport javax.microedition.khronos.egl.EGL10;\nimport javax.microedition.khronos.egl.EGL11;\nimport javax.microedition.khronos.egl.EGLConfig;\nimport javax.microedition.khronos.egl.EGLContext;\nimport javax.microedition.khronos.egl.EGLDisplay;\nimport javax.microedition.khronos.egl.EGLSurface;\nimport javax.microedition.khronos.opengles.GL;\nimport javax.microedition.khronos.opengles.GL10;\n\n/**\n * See http://stackoverflow.com/questions/12061419/converting-from-glsurfaceview-to-textureview-via-gltextureview\n */\npublic class GLTextureView extends TextureView implements TextureView.SurfaceTextureListener {\n    private final static String TAG = \"GLTextureView\";\n    private final static boolean LOG_ATTACH_DETACH = false;\n    private final static boolean LOG_THREADS = false;\n    private final static boolean LOG_PAUSE_RESUME = false;\n    private final static boolean LOG_SURFACE = false;\n    private final static boolean LOG_RENDERER = false;\n    private final static boolean LOG_RENDERER_DRAW_FRAME = false;\n    private final static boolean LOG_EGL = false;\n    /**\n     * The renderer only renders\n     * when the surface is created, or when {@link #requestRender} is called.\n     *\n     * @see #getRenderMode()\n     * @see #setRenderMode(int)\n     * @see #requestRender()\n     */\n    public final static int RENDERMODE_WHEN_DIRTY = 0;\n    /**\n     * The renderer is called\n     * continuously to re-render the scene.\n     *\n     * @see #getRenderMode()\n     * @see #setRenderMode(int)\n     */\n    public final static int RENDERMODE_CONTINUOUSLY = 1;\n\n    /**\n     * Check glError() after every GL call and throw an exception if glError indicates\n     * that an error has occurred. This can be used to help track down which OpenGL ES call\n     * is causing an error.\n     *\n     * @see #getDebugFlags\n     * @see #setDebugFlags\n     */\n    public final static int DEBUG_CHECK_GL_ERROR = 1;\n\n    /**\n     * Log GL calls to the system log at \"verbose\" level with tag \"GLSurfaceView\".\n     *\n     * @see #getDebugFlags\n     * @see #setDebugFlags\n     */\n    public final static int DEBUG_LOG_GL_CALLS = 2;\n\n    /**\n     * Standard View constructor. In order to render something, you\n     * must call {@link #setRenderer} to register a renderer.\n     */\n    public GLTextureView(Context context) {\n        super(context);\n        init();\n    }\n\n    /**\n     * Standard View constructor. In order to render something, you\n     * must call {@link #setRenderer} to register a renderer.\n     */\n    public GLTextureView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init();\n    }\n\n    @Override\n    protected void finalize() throws Throwable {\n        try {\n            if (mGLThread != null) {\n                // GLThread may still be running if this view was never\n                // attached to a window.\n                mGLThread.requestExitAndWait();\n            }\n        } finally {\n            super.finalize();\n        }\n    }\n\n    private void init() {\n        setSurfaceTextureListener(this);\n    }\n\n    /**\n     * Set the glWrapper. If the glWrapper is not null, its\n     * {@link GLWrapper#wrap(javax.microedition.khronos.opengles.GL)} method is called\n     * whenever a surface is created. A GLWrapper can be used to wrap\n     * the GL object that's passed to the renderer. Wrapping a GL\n     * object enables examining and modifying the behavior of the\n     * GL calls made by the renderer.\n     * <p>\n     * Wrapping is typically used for debugging purposes.\n     * <p>\n     * The default value is null.\n     * @param glWrapper the new GLWrapper\n     */\n    public void setGLWrapper(GLWrapper glWrapper) {\n        mGLWrapper = glWrapper;\n    }\n\n    /**\n     * Set the debug flags to a new value. The value is\n     * constructed by OR-together zero or more\n     * of the DEBUG_CHECK_* constants. The debug flags take effect\n     * whenever a surface is created. The default value is zero.\n     * @param debugFlags the new debug flags\n     * @see #DEBUG_CHECK_GL_ERROR\n     * @see #DEBUG_LOG_GL_CALLS\n     */\n    public void setDebugFlags(int debugFlags) {\n        mDebugFlags = debugFlags;\n    }\n\n    /**\n     * Get the current value of the debug flags.\n     * @return the current value of the debug flags.\n     */\n    public int getDebugFlags() {\n        return mDebugFlags;\n    }\n\n    /**\n     * Control whether the EGL context is preserved when the GLSurfaceView is paused and\n     * resumed.\n     * <p>\n     * If set to true, then the EGL context may be preserved when the GLSurfaceView is paused.\n     * Whether the EGL context is actually preserved or not depends upon whether the\n     * Android device that the program is running on can support an arbitrary number of EGL\n     * contexts or not. Devices that can only support a limited number of EGL contexts must\n     * release the  EGL context in order to allow multiple applications to share the GPU.\n     * <p>\n     * If set to false, the EGL context will be released when the GLSurfaceView is paused,\n     * and recreated when the GLSurfaceView is resumed.\n     * <p>\n     *\n     * The default is false.\n     *\n     * @param preserveOnPause preserve the EGL context when paused\n     */\n    public void setPreserveEGLContextOnPause(boolean preserveOnPause) {\n        mPreserveEGLContextOnPause = preserveOnPause;\n    }\n\n    /**\n     * @return true if the EGL context will be preserved when paused\n     */\n    public boolean getPreserveEGLContextOnPause() {\n        return mPreserveEGLContextOnPause;\n    }\n\n    /**\n     * Set the renderer associated with this view. Also starts the thread that\n     * will call the renderer, which in turn causes the rendering to start.\n     * <p>This method should be called once and only once in the life-cycle of\n     * a GLSurfaceView.\n     * <p>The following GLSurfaceView methods can only be called <em>before</em>\n     * setRenderer is called:\n     * <ul>\n     * <li>{@link #setEGLConfigChooser(boolean)}\n     * <li>{@link #setEGLConfigChooser(EGLConfigChooser)}\n     * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)}\n     * </ul>\n     * <p>\n     * The following GLSurfaceView methods can only be called <em>after</em>\n     * setRenderer is called:\n     * <ul>\n     * <li>{@link #getRenderMode()}\n     * <li>{@link #onPause()}\n     * <li>{@link #onResume()}\n     * <li>{@link #queueEvent(Runnable)}\n     * <li>{@link #requestRender()}\n     * <li>{@link #setRenderMode(int)}\n     * </ul>\n     *\n     * @param renderer the renderer to use to perform OpenGL drawing.\n     */\n    public void setRenderer(GLSurfaceView.Renderer renderer) {\n        checkRenderThreadState();\n        if (mEGLConfigChooser == null) {\n            mEGLConfigChooser = new SimpleEGLConfigChooser(true);\n        }\n        if (mEGLContextFactory == null) {\n            mEGLContextFactory = new DefaultContextFactory();\n        }\n        if (mEGLWindowSurfaceFactory == null) {\n            mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();\n        }\n        mRenderer = renderer;\n        mGLThread = new GLThread(mThisWeakRef);\n        mGLThread.start();\n    }\n\n    /**\n     * Install a custom EGLContextFactory.\n     * <p>If this method is\n     * called, it must be called before {@link #setRenderer(Renderer)}\n     * is called.\n     * <p>\n     * If this method is not called, then by default\n     * a context will be created with no shared context and\n     * with a null attribute list.\n     */\n    public void setEGLContextFactory(EGLContextFactory factory) {\n        checkRenderThreadState();\n        mEGLContextFactory = factory;\n    }\n\n    /**\n     * Install a custom EGLWindowSurfaceFactory.\n     * <p>If this method is\n     * called, it must be called before {@link #setRenderer(Renderer)}\n     * is called.\n     * <p>\n     * If this method is not called, then by default\n     * a window surface will be created with a null attribute list.\n     */\n    public void setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory factory) {\n        checkRenderThreadState();\n        mEGLWindowSurfaceFactory = factory;\n    }\n\n    /**\n     * Install a custom EGLConfigChooser.\n     * <p>If this method is\n     * called, it must be called before {@link #setRenderer(Renderer)}\n     * is called.\n     * <p>\n     * If no setEGLConfigChooser method is called, then by default the\n     * view will choose an EGLConfig that is compatible with the current\n     * android.view.Surface, with a depth buffer depth of\n     * at least 16 bits.\n     * @param configChooser\n     */\n    public void setEGLConfigChooser(EGLConfigChooser configChooser) {\n        checkRenderThreadState();\n        mEGLConfigChooser = configChooser;\n    }\n\n    /**\n     * Install a config chooser which will choose a config\n     * as close to 16-bit RGB as possible, with or without an optional depth\n     * buffer as close to 16-bits as possible.\n     * <p>If this method is\n     * called, it must be called before {@link #setRenderer(Renderer)}\n     * is called.\n     * <p>\n     * If no setEGLConfigChooser method is called, then by default the\n     * view will choose an RGB_888 surface with a depth buffer depth of\n     * at least 16 bits.\n     *\n     * @param needDepth\n     */\n    public void setEGLConfigChooser(boolean needDepth) {\n        setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth));\n    }\n\n    /**\n     * Install a config chooser which will choose a config\n     * with at least the specified depthSize and stencilSize,\n     * and exactly the specified redSize, greenSize, blueSize and alphaSize.\n     * <p>If this method is\n     * called, it must be called before {@link #setRenderer(Renderer)}\n     * is called.\n     * <p>\n     * If no setEGLConfigChooser method is called, then by default the\n     * view will choose an RGB_888 surface with a depth buffer depth of\n     * at least 16 bits.\n     *\n     */\n    public void setEGLConfigChooser(int redSize, int greenSize, int blueSize,\n            int alphaSize, int depthSize, int stencilSize) {\n        setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize,\n                blueSize, alphaSize, depthSize, stencilSize));\n    }\n\n    /**\n     * Inform the default EGLContextFactory and default EGLConfigChooser\n     * which EGLContext client version to pick.\n     * <p>Use this method to create an OpenGL ES 2.0-compatible context.\n     * Example:\n     * <pre class=\"prettyprint\">\n     *     public MyView(Context context) {\n     *         super(context);\n     *         setEGLContextClientVersion(2); // Pick an OpenGL ES 2.0 context.\n     *         setRenderer(new MyRenderer());\n     *     }\n     * </pre>\n     * <p>Note: Activities which require OpenGL ES 2.0 should indicate this by\n     * setting @lt;uses-feature android:glEsVersion=\"0x00020000\" in the activity's\n     * AndroidManifest.xml file.\n     * <p>If this method is called, it must be called before {@link #setRenderer(Renderer)}\n     * is called.\n     * <p>This method only affects the behavior of the default EGLContexFactory and the\n     * default EGLConfigChooser. If\n     * {@link #setEGLContextFactory(EGLContextFactory)} has been called, then the supplied\n     * EGLContextFactory is responsible for creating an OpenGL ES 2.0-compatible context.\n     * If\n     * {@link #setEGLConfigChooser(EGLConfigChooser)} has been called, then the supplied\n     * EGLConfigChooser is responsible for choosing an OpenGL ES 2.0-compatible config.\n     * @param version The EGLContext client version to choose. Use 2 for OpenGL ES 2.0\n     */\n    public void setEGLContextClientVersion(int version) {\n        checkRenderThreadState();\n        mEGLContextClientVersion = version;\n    }\n\n    /**\n     * Set the rendering mode. When renderMode is\n     * RENDERMODE_CONTINUOUSLY, the renderer is called\n     * repeatedly to re-render the scene. When renderMode\n     * is RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface\n     * is created, or when {@link #requestRender} is called. Defaults to RENDERMODE_CONTINUOUSLY.\n     * <p>\n     * Using RENDERMODE_WHEN_DIRTY can improve battery life and overall system performance\n     * by allowing the GPU and CPU to idle when the view does not need to be updated.\n     * <p>\n     * This method can only be called after {@link #setRenderer(Renderer)}\n     *\n     * @param renderMode one of the RENDERMODE_X constants\n     * @see #RENDERMODE_CONTINUOUSLY\n     * @see #RENDERMODE_WHEN_DIRTY\n     */\n    public void setRenderMode(int renderMode) {\n        mGLThread.setRenderMode(renderMode);\n    }\n\n    /**\n     * Get the current rendering mode. May be called\n     * from any thread. Must not be called before a renderer has been set.\n     * @return the current rendering mode.\n     * @see #RENDERMODE_CONTINUOUSLY\n     * @see #RENDERMODE_WHEN_DIRTY\n     */\n    public int getRenderMode() {\n        return mGLThread.getRenderMode();\n    }\n\n    /**\n     * Request that the renderer render a frame.\n     * This method is typically used when the render mode has been set to\n     * {@link #RENDERMODE_WHEN_DIRTY}, so that frames are only rendered on demand.\n     * May be called\n     * from any thread. Must not be called before a renderer has been set.\n     */\n    public void requestRender() {\n        mGLThread.requestRender();\n    }\n\n    @Override\n    public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {\n        mGLThread.surfaceCreated();\n    }\n\n    @Override\n    public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) {\n        mGLThread.onWindowResize(width, height);\n    }\n\n    @Override\n    protected void onSizeChanged(int w, int h, int oldw, int oldh) {\n        super.onSizeChanged(w, h, oldw, oldh);\n        mGLThread.onWindowResize(w, h);\n    }\n\n    @Override\n    public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {\n        mGLThread.surfaceDestroyed();\n        return true;\n    }\n\n    @Override\n    public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {\n        requestRender();\n    }\n\n    /**\n     * This method is part of the SurfaceHolder.Callback interface, and is\n     * not normally called or subclassed by clients of GLSurfaceView.\n     */\n    public void on(SurfaceHolder holder) {\n        mGLThread.surfaceCreated();\n    }\n\n    /**\n     * Inform the view that the activity is paused. The owner of this view must\n     * call this method when the activity is paused. Calling this method will\n     * pause the rendering thread.\n     * Must not be called before a renderer has been set.\n     */\n    public void onPause() {\n        mGLThread.onPause();\n    }\n\n    /**\n     * Inform the view that the activity is resumed. The owner of this view must\n     * call this method when the activity is resumed. Calling this method will\n     * recreate the OpenGL display and resume the rendering\n     * thread.\n     * Must not be called before a renderer has been set.\n     */\n    public void onResume() {\n        mGLThread.onResume();\n    }\n\n    /**\n     * Queue a runnable to be run on the GL rendering thread. This can be used\n     * to communicate with the Renderer on the rendering thread.\n     * Must not be called before a renderer has been set.\n     * @param r the runnable to be run on the GL rendering thread.\n     */\n    public void queueEvent(Runnable r) {\n        mGLThread.queueEvent(r);\n    }\n\n    /**\n     * This method is used as part of the View class and is not normally\n     * called or subclassed by clients of GLSurfaceView.\n     */\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n        if (LOG_ATTACH_DETACH) {\n            Log.d(TAG, \"onAttachedToWindow reattach =\" + mDetached);\n        }\n        if (mDetached && (mRenderer != null)) {\n            int renderMode = RENDERMODE_CONTINUOUSLY;\n            if (mGLThread != null) {\n                renderMode = mGLThread.getRenderMode();\n            }\n            mGLThread = new GLThread(mThisWeakRef);\n            if (renderMode != RENDERMODE_CONTINUOUSLY) {\n                mGLThread.setRenderMode(renderMode);\n            }\n            mGLThread.start();\n        }\n        mDetached = false;\n    }\n\n    /**\n     * This method is used as part of the View class and is not normally\n     * called or subclassed by clients of GLSurfaceView.\n     * Must not be called before a renderer has been set.\n     */\n    @Override\n    protected void onDetachedFromWindow() {\n        if (LOG_ATTACH_DETACH) {\n            Log.d(TAG, \"onDetachedFromWindow\");\n        }\n        if (mGLThread != null) {\n            mGLThread.requestExitAndWait();\n        }\n        mDetached = true;\n        super.onDetachedFromWindow();\n    }\n\n    // ----------------------------------------------------------------------\n\n    /**\n     * An interface used to wrap a GL interface.\n     * <p>Typically\n     * used for implementing debugging and tracing on top of the default\n     * GL interface. You would typically use this by creating your own class\n     * that implemented all the GL methods by delegating to another GL instance.\n     * Then you could add your own behavior before or after calling the\n     * delegate. All the GLWrapper would do was instantiate and return the\n     * wrapper GL instance:\n     * <pre class=\"prettyprint\">\n     * class MyGLWrapper implements GLWrapper {\n     *     GL wrap(GL gl) {\n     *         return new MyGLImplementation(gl);\n     *     }\n     *     static class MyGLImplementation implements GL,GL10,GL11,... {\n     *         ...\n     *     }\n     * }\n     * </pre>\n     * @see #setGLWrapper(GLWrapper)\n     */\n    public interface GLWrapper {\n        /**\n         * Wraps a gl interface in another gl interface.\n         * @param gl a GL interface that is to be wrapped.\n         * @return either the input argument or another GL object that wraps the input argument.\n         */\n        GL wrap(GL gl);\n    }\n\n    /**\n     * An interface for customizing the eglCreateContext and eglDestroyContext calls.\n     * <p>\n     * This interface must be implemented by clients wishing to call\n     *\n     */\n    public interface EGLContextFactory {\n        EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig);\n        void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context);\n    }\n\n    private class DefaultContextFactory implements EGLContextFactory {\n        private int EGL_CONTEXT_CLIENT_VERSION = 0x3098;\n\n        public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {\n            int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion,\n                    EGL10.EGL_NONE };\n\n            return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT,\n                    mEGLContextClientVersion != 0 ? attrib_list : null);\n        }\n\n        public void destroyContext(EGL10 egl, EGLDisplay display,\n                EGLContext context) {\n            if (!egl.eglDestroyContext(display, context)) {\n                Log.e(\"DefaultContextFactory\", \"display:\" + display + \" context: \" + context);\n                if (LOG_THREADS) {\n                    Log.i(\"DefaultContextFactory\", \"tid=\" + Thread.currentThread().getId());\n                }\n                EglHelper.throwEglException(\"eglDestroyContex\", egl.eglGetError());\n            }\n        }\n    }\n\n    /**\n     * An interface for customizing the eglCreateWindowSurface and eglDestroySurface calls.\n     * <p>\n     * This interface must be implemented by clients wishing to call\n     *\n     */\n    public interface EGLWindowSurfaceFactory {\n        /**\n         *  @return null if the surface cannot be constructed.\n         */\n        EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config,\n                                       Object nativeWindow);\n        void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface);\n    }\n\n    private static class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory {\n\n        public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display,\n                EGLConfig config, Object nativeWindow) {\n            EGLSurface result = null;\n            try {\n                result = egl.eglCreateWindowSurface(display, config, nativeWindow, null);\n            } catch (IllegalArgumentException e) {\n                // This exception indicates that the surface flinger surface\n                // is not valid. This can happen if the surface flinger surface has\n                // been torn down, but the application has not yet been\n                // notified via SurfaceHolder.Callback.surfaceDestroyed.\n                // In theory the application should be notified first,\n                // but in practice sometimes it is not. See b/4588890\n                Log.e(TAG, \"eglCreateWindowSurface\", e);\n            }\n            return result;\n        }\n\n        public void destroySurface(EGL10 egl, EGLDisplay display,\n                EGLSurface surface) {\n            egl.eglDestroySurface(display, surface);\n        }\n    }\n\n    /**\n     * An interface for choosing an EGLConfig configuration from a list of\n     * potential configurations.\n     * <p>\n     * This interface must be implemented by clients wishing to call\n     *\n     */\n    public interface EGLConfigChooser {\n        /**\n         * Choose a configuration from the list. Implementors typically\n         * implement this method by calling\n         * {@link javax.microedition.khronos.egl.EGL10#eglChooseConfig} and iterating through the results. Please consult the\n         * EGL specification available from The Khronos Group to learn how to call eglChooseConfig.\n         * @param egl the EGL10 for the current display.\n         * @param display the current display.\n         * @return the chosen configuration.\n         */\n        EGLConfig chooseConfig(EGL10 egl, EGLDisplay display);\n    }\n\n    private abstract class BaseConfigChooser\n            implements EGLConfigChooser {\n        public BaseConfigChooser(int[] configSpec) {\n            mConfigSpec = filterConfigSpec(configSpec);\n        }\n\n        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {\n            int[] num_config = new int[1];\n            if (!egl.eglChooseConfig(display, mConfigSpec, null, 0,\n                    num_config)) {\n                throw new IllegalArgumentException(\"eglChooseConfig failed\");\n            }\n\n            int numConfigs = num_config[0];\n\n            if (numConfigs <= 0) {\n                throw new IllegalArgumentException(\n                        \"No configs match configSpec\");\n            }\n\n            EGLConfig[] configs = new EGLConfig[numConfigs];\n            if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs,\n                    num_config)) {\n                throw new IllegalArgumentException(\"eglChooseConfig#2 failed\");\n            }\n            EGLConfig config = chooseConfig(egl, display, configs);\n            if (config == null) {\n                throw new IllegalArgumentException(\"No config chosen\");\n            }\n            return config;\n        }\n\n        abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,\n                EGLConfig[] configs);\n\n        protected int[] mConfigSpec;\n\n        private int[] filterConfigSpec(int[] configSpec) {\n            if (mEGLContextClientVersion != 2) {\n                return configSpec;\n            }\n            /* We know none of the subclasses define EGL_RENDERABLE_TYPE.\n             * And we know the configSpec is well formed.\n             */\n            int len = configSpec.length;\n            int[] newConfigSpec = new int[len + 2];\n            System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1);\n            newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE;\n            newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */\n            newConfigSpec[len+1] = EGL10.EGL_NONE;\n            return newConfigSpec;\n        }\n    }\n\n    /**\n     * Choose a configuration with exactly the specified r,g,b,a sizes,\n     * and at least the specified depth and stencil sizes.\n     */\n    private class ComponentSizeChooser extends BaseConfigChooser {\n        public ComponentSizeChooser(int redSize, int greenSize, int blueSize,\n                int alphaSize, int depthSize, int stencilSize) {\n            super(new int[] {\n                    EGL10.EGL_RED_SIZE, redSize,\n                    EGL10.EGL_GREEN_SIZE, greenSize,\n                    EGL10.EGL_BLUE_SIZE, blueSize,\n                    EGL10.EGL_ALPHA_SIZE, alphaSize,\n                    EGL10.EGL_DEPTH_SIZE, depthSize,\n                    EGL10.EGL_STENCIL_SIZE, stencilSize,\n                    EGL10.EGL_NONE});\n            mValue = new int[1];\n            mRedSize = redSize;\n            mGreenSize = greenSize;\n            mBlueSize = blueSize;\n            mAlphaSize = alphaSize;\n            mDepthSize = depthSize;\n            mStencilSize = stencilSize;\n       }\n\n        @Override\n        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,\n                EGLConfig[] configs) {\n            for (EGLConfig config : configs) {\n                int d = findConfigAttrib(egl, display, config,\n                        EGL10.EGL_DEPTH_SIZE, 0);\n                int s = findConfigAttrib(egl, display, config,\n                        EGL10.EGL_STENCIL_SIZE, 0);\n                if ((d >= mDepthSize) && (s >= mStencilSize)) {\n                    int r = findConfigAttrib(egl, display, config,\n                            EGL10.EGL_RED_SIZE, 0);\n                    int g = findConfigAttrib(egl, display, config,\n                             EGL10.EGL_GREEN_SIZE, 0);\n                    int b = findConfigAttrib(egl, display, config,\n                              EGL10.EGL_BLUE_SIZE, 0);\n                    int a = findConfigAttrib(egl, display, config,\n                            EGL10.EGL_ALPHA_SIZE, 0);\n                    if ((r == mRedSize) && (g == mGreenSize)\n                            && (b == mBlueSize) && (a == mAlphaSize)) {\n                        return config;\n                    }\n                }\n            }\n            return null;\n        }\n\n        private int findConfigAttrib(EGL10 egl, EGLDisplay display,\n                EGLConfig config, int attribute, int defaultValue) {\n\n            if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {\n                return mValue[0];\n            }\n            return defaultValue;\n        }\n\n        private int[] mValue;\n        // Subclasses can adjust these values:\n        protected int mRedSize;\n        protected int mGreenSize;\n        protected int mBlueSize;\n        protected int mAlphaSize;\n        protected int mDepthSize;\n        protected int mStencilSize;\n        }\n\n    /**\n     * This class will choose a RGB_888 surface with\n     * or without a depth buffer.\n     *\n     */\n    private class SimpleEGLConfigChooser extends ComponentSizeChooser {\n        public SimpleEGLConfigChooser(boolean withDepthBuffer) {\n            super(8, 8, 8, 0, withDepthBuffer ? 16 : 0, 0);\n        }\n    }\n\n    /**\n     * An EGL helper class.\n     */\n\n    private static class EglHelper {\n        public EglHelper(WeakReference<GLTextureView> glTextureViewWeakRef) {\n            mGLTextureViewWeakRef = glTextureViewWeakRef;\n        }\n\n        /**\n         * Initialize EGL for a given configuration spec.\n         * @param configSpec\n         */\n        public void start() {\n            if (LOG_EGL) {\n                Log.w(\"EglHelper\", \"start() tid=\" + Thread.currentThread().getId());\n            }\n            /*\n             * Get an EGL instance\n             */\n            mEgl = (EGL10) EGLContext.getEGL();\n\n            /*\n             * Get to the default display.\n             */\n            mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);\n\n            if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {\n                throw new RuntimeException(\"eglGetDisplay failed\");\n            }\n\n            /*\n             * We can now initialize EGL for that display\n             */\n            int[] version = new int[2];\n            if(!mEgl.eglInitialize(mEglDisplay, version)) {\n                throw new RuntimeException(\"eglInitialize failed\");\n            }\n            GLTextureView view = mGLTextureViewWeakRef.get();\n            if (view == null) {\n                mEglConfig = null;\n                mEglContext = null;\n            } else {\n                mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);\n\n                /*\n                * Create an EGL context. We want to do this as rarely as we can, because an\n                * EGL context is a somewhat heavy object.\n                */\n                mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);\n            }\n            if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {\n                mEglContext = null;\n                throwEglException(\"createContext\");\n            }\n            if (LOG_EGL) {\n                Log.w(\"EglHelper\", \"createContext \" + mEglContext + \" tid=\" + Thread.currentThread().getId());\n            }\n\n            mEglSurface = null;\n        }\n\n        /**\n         * Create an egl surface for the current SurfaceHolder surface. If a surface\n         * already exists, destroy it before creating the new surface.\n         *\n         * @return true if the surface was created successfully.\n         */\n        public boolean createSurface() {\n            if (LOG_EGL) {\n                Log.w(\"EglHelper\", \"createSurface()  tid=\" + Thread.currentThread().getId());\n            }\n            /*\n             * Check preconditions.\n             */\n            if (mEgl == null) {\n                throw new RuntimeException(\"egl not initialized\");\n            }\n            if (mEglDisplay == null) {\n                throw new RuntimeException(\"eglDisplay not initialized\");\n            }\n            if (mEglConfig == null) {\n                throw new RuntimeException(\"mEglConfig not initialized\");\n            }\n\n            /*\n             *  The window size has changed, so we need to create a new\n             *  surface.\n             */\n            destroySurfaceImp();\n\n            /*\n             * Create an EGL surface we can render into.\n             */\n            GLTextureView view = mGLTextureViewWeakRef.get();\n            if (view != null) {\n                mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(mEgl,\n                        mEglDisplay, mEglConfig, view.getSurfaceTexture());\n            } else {\n                mEglSurface = null;\n            }\n\n            if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {\n                int error = mEgl.eglGetError();\n                if (error == EGL10.EGL_BAD_NATIVE_WINDOW) {\n                    Log.e(\"EglHelper\", \"createWindowSurface returned EGL_BAD_NATIVE_WINDOW.\");\n                }\n                return false;\n            }\n\n            /*\n             * Before we can issue GL commands, we need to make sure\n             * the context is current and bound to a surface.\n             */\n            if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {\n                /*\n                 * Could not make the context current, probably because the underlying\n                 * SurfaceView surface has been destroyed.\n                 */\n                logEglErrorAsWarning(\"EGLHelper\", \"eglMakeCurrent\", mEgl.eglGetError());\n                return false;\n            }\n\n            return true;\n        }\n\n        /**\n         * Create a GL object for the current EGL context.\n         * @return\n         */\n        GL createGL() {\n\n            GL gl = mEglContext.getGL();\n            GLTextureView view = mGLTextureViewWeakRef.get();\n            if (view != null) {\n                if (view.mGLWrapper != null) {\n                    gl = view.mGLWrapper.wrap(gl);\n                }\n\n                if ((view.mDebugFlags & (DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS)) != 0) {\n                    int configFlags = 0;\n                    Writer log = null;\n                    if ((view.mDebugFlags & DEBUG_CHECK_GL_ERROR) != 0) {\n                        configFlags |= GLDebugHelper.CONFIG_CHECK_GL_ERROR;\n                    }\n                    if ((view.mDebugFlags & DEBUG_LOG_GL_CALLS) != 0) {\n                        log = new LogWriter();\n                    }\n                    gl = GLDebugHelper.wrap(gl, configFlags, log);\n                }\n            }\n            return gl;\n        }\n\n        /**\n         * Display the current render surface.\n         * @return the EGL error code from eglSwapBuffers.\n         */\n        public int swap() {\n            if (! mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {\n                return mEgl.eglGetError();\n            }\n            return EGL10.EGL_SUCCESS;\n        }\n\n        public void destroySurface() {\n            if (LOG_EGL) {\n                Log.w(\"EglHelper\", \"destroySurface()  tid=\" + Thread.currentThread().getId());\n            }\n            destroySurfaceImp();\n        }\n\n        private void destroySurfaceImp() {\n            if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {\n                mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,\n                        EGL10.EGL_NO_SURFACE,\n                        EGL10.EGL_NO_CONTEXT);\n                GLTextureView view = mGLTextureViewWeakRef.get();\n                if (view != null) {\n                    view.mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);\n                }\n                mEglSurface = null;\n            }\n        }\n\n        public void finish() {\n            if (LOG_EGL) {\n                Log.w(\"EglHelper\", \"finish() tid=\" + Thread.currentThread().getId());\n            }\n            if (mEglContext != null) {\n                GLTextureView view = mGLTextureViewWeakRef.get();\n                if (view != null) {\n                    view.mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext);\n                }\n                mEglContext = null;\n            }\n            if (mEglDisplay != null) {\n                mEgl.eglTerminate(mEglDisplay);\n                mEglDisplay = null;\n            }\n        }\n\n        private void throwEglException(String function) {\n            throwEglException(function, mEgl.eglGetError());\n        }\n\n        public static void throwEglException(String function, int error) {\n            String message = formatEglError(function, error);\n            if (LOG_THREADS) {\n                Log.e(\"EglHelper\", \"throwEglException tid=\" + Thread.currentThread().getId() + \" \"\n                        + message);\n            }\n            throw new RuntimeException(message);\n        }\n\n        public static void logEglErrorAsWarning(String tag, String function, int error) {\n            Log.w(tag, formatEglError(function, error));\n        }\n\n        public static String formatEglError(String function, int error) {\n            return function + \" failed\"; // + EGLLogWrapper.getErrorString(error);\n        }\n\n        private WeakReference<GLTextureView> mGLTextureViewWeakRef;\n        EGL10 mEgl;\n        EGLDisplay mEglDisplay;\n        EGLSurface mEglSurface;\n        EGLConfig mEglConfig;\n        EGLContext mEglContext;\n\n    }\n\n    /**\n     * A generic GL Thread. Takes care of initializing EGL and GL. Delegates\n     * to a Renderer instance to do the actual drawing. Can be configured to\n     * render continuously or on request.\n     *\n     * All potentially blocking synchronization is done through the\n     * sGLThreadManager object. This avoids multiple-lock ordering issues.\n     *\n     */\n    static class GLThread extends Thread {\n        GLThread(WeakReference<GLTextureView> glTextureViewWeakRef) {\n            super();\n            mWidth = 0;\n            mHeight = 0;\n            mRequestRender = true;\n            mRenderMode = RENDERMODE_CONTINUOUSLY;\n            mGLTextureViewWeakRef = glTextureViewWeakRef;\n        }\n\n        @Override\n        public void run() {\n            setName(\"GLThread \" + getId());\n            if (LOG_THREADS) {\n                Log.i(\"GLThread\", \"starting tid=\" + getId());\n            }\n\n            try {\n                guardedRun();\n            } catch (InterruptedException e) {\n                // fall thru and exit normally\n            } finally {\n                sGLThreadManager.threadExiting(this);\n            }\n        }\n\n        /*\n         * This private method should only be called inside a\n         * synchronized(sGLThreadManager) block.\n         */\n        private void stopEglSurfaceLocked() {\n            if (mHaveEglSurface) {\n                mHaveEglSurface = false;\n                mEglHelper.destroySurface();\n            }\n        }\n\n        /*\n         * This private method should only be called inside a\n         * synchronized(sGLThreadManager) block.\n         */\n        private void stopEglContextLocked() {\n            if (mHaveEglContext) {\n                mEglHelper.finish();\n                mHaveEglContext = false;\n                sGLThreadManager.releaseEglContextLocked(this);\n            }\n        }\n        private void guardedRun() throws InterruptedException {\n            mEglHelper = new EglHelper(mGLTextureViewWeakRef);\n            mHaveEglContext = false;\n            mHaveEglSurface = false;\n            try {\n                GL10 gl = null;\n                boolean createEglContext = false;\n                boolean createEglSurface = false;\n                boolean createGlInterface = false;\n                boolean lostEglContext = false;\n                boolean sizeChanged = false;\n                boolean wantRenderNotification = false;\n                boolean doRenderNotification = false;\n                boolean askedToReleaseEglContext = false;\n                int w = 0;\n                int h = 0;\n                Runnable event = null;\n\n                while (true) {\n                    synchronized (sGLThreadManager) {\n                        while (true) {\n                            if (mShouldExit) {\n                                return;\n                            }\n\n                            if (! mEventQueue.isEmpty()) {\n                                event = mEventQueue.remove(0);\n                                break;\n                            }\n\n                            // Update the pause state.\n                            boolean pausing = false;\n                            if (mPaused != mRequestPaused) {\n                                pausing = mRequestPaused;\n                                mPaused = mRequestPaused;\n                                sGLThreadManager.notifyAll();\n                                if (LOG_PAUSE_RESUME) {\n                                    Log.i(\"GLThread\", \"mPaused is now \" + mPaused + \" tid=\" + getId());\n                                }\n                            }\n\n                            // Do we need to give up the EGL context?\n                            if (mShouldReleaseEglContext) {\n                                if (LOG_SURFACE) {\n                                    Log.i(\"GLThread\", \"releasing EGL context because asked to tid=\" + getId());\n                                }\n                                stopEglSurfaceLocked();\n                                stopEglContextLocked();\n                                mShouldReleaseEglContext = false;\n                                askedToReleaseEglContext = true;\n                            }\n\n                            // Have we lost the EGL context?\n                            if (lostEglContext) {\n                                stopEglSurfaceLocked();\n                                stopEglContextLocked();\n                                lostEglContext = false;\n                            }\n\n                            // When pausing, release the EGL surface:\n                            if (pausing && mHaveEglSurface) {\n                                if (LOG_SURFACE) {\n                                    Log.i(\"GLThread\", \"releasing EGL surface because paused tid=\" + getId());\n                                }\n                                stopEglSurfaceLocked();\n                            }\n\n                            // When pausing, optionally release the EGL Context:\n                            if (pausing && mHaveEglContext) {\n                                GLTextureView view = mGLTextureViewWeakRef.get();\n                                boolean preserveEglContextOnPause = view == null ?\n                                        false : view.mPreserveEGLContextOnPause;\n                                if (!preserveEglContextOnPause || sGLThreadManager.shouldReleaseEGLContextWhenPausing()) {\n                                    stopEglContextLocked();\n                                    if (LOG_SURFACE) {\n                                        Log.i(\"GLThread\", \"releasing EGL context because paused tid=\" + getId());\n                                    }\n                                }\n                            }\n\n                            // When pausing, optionally terminate EGL:\n                            if (pausing) {\n                                if (sGLThreadManager.shouldTerminateEGLWhenPausing()) {\n                                    mEglHelper.finish();\n                                    if (LOG_SURFACE) {\n                                        Log.i(\"GLThread\", \"terminating EGL because paused tid=\" + getId());\n                                    }\n                                }\n                            }\n\n                            // Have we lost the SurfaceView surface?\n                            if ((! mHasSurface) && (! mWaitingForSurface)) {\n                                if (LOG_SURFACE) {\n                                    Log.i(\"GLThread\", \"noticed surfaceView surface lost tid=\" + getId());\n                                }\n                                if (mHaveEglSurface) {\n                                    stopEglSurfaceLocked();\n                                }\n                                mWaitingForSurface = true;\n                                mSurfaceIsBad = false;\n                                sGLThreadManager.notifyAll();\n                            }\n\n                            // Have we acquired the surface view surface?\n                            if (mHasSurface && mWaitingForSurface) {\n                                if (LOG_SURFACE) {\n                                    Log.i(\"GLThread\", \"noticed surfaceView surface acquired tid=\" + getId());\n                                }\n                                mWaitingForSurface = false;\n                                sGLThreadManager.notifyAll();\n                            }\n\n                            if (doRenderNotification) {\n                                if (LOG_SURFACE) {\n                                    Log.i(\"GLThread\", \"sending render notification tid=\" + getId());\n                                }\n                                wantRenderNotification = false;\n                                doRenderNotification = false;\n                                mRenderComplete = true;\n                                sGLThreadManager.notifyAll();\n                            }\n\n                            // Ready to draw?\n                            if (readyToDraw()) {\n\n                                // If we don't have an EGL context, try to acquire one.\n                                if (! mHaveEglContext) {\n                                    if (askedToReleaseEglContext) {\n                                        askedToReleaseEglContext = false;\n                                    } else if (sGLThreadManager.tryAcquireEglContextLocked(this)) {\n                                        try {\n                                            mEglHelper.start();\n                                        } catch (RuntimeException t) {\n                                            sGLThreadManager.releaseEglContextLocked(this);\n                                            throw t;\n                                        }\n                                        mHaveEglContext = true;\n                                        createEglContext = true;\n\n                                        sGLThreadManager.notifyAll();\n                                    }\n                                }\n\n                                if (mHaveEglContext && !mHaveEglSurface) {\n                                    mHaveEglSurface = true;\n                                    createEglSurface = true;\n                                    createGlInterface = true;\n                                    sizeChanged = true;\n                                }\n\n                                if (mHaveEglSurface) {\n                                    if (mSizeChanged) {\n                                        sizeChanged = true;\n                                        w = mWidth;\n                                        h = mHeight;\n                                        wantRenderNotification = true;\n                                        if (LOG_SURFACE) {\n                                            Log.i(\"GLThread\",\n                                                    \"noticing that we want render notification tid=\"\n                                                    + getId());\n                                        }\n\n                                        // Destroy and recreate the EGL surface.\n                                        createEglSurface = true;\n\n                                        mSizeChanged = false;\n                                    }\n                                    mRequestRender = false;\n                                    sGLThreadManager.notifyAll();\n                                    break;\n                                }\n                            }\n\n                            // By design, this is the only place in a GLThread thread where we wait().\n                            if (LOG_THREADS) {\n                                Log.i(\"GLThread\", \"waiting tid=\" + getId()\n                                    + \" mHaveEglContext: \" + mHaveEglContext\n                                    + \" mHaveEglSurface: \" + mHaveEglSurface\n                                    + \" mFinishedCreatingEglSurface: \" + mFinishedCreatingEglSurface\n                                    + \" mPaused: \" + mPaused\n                                    + \" mHasSurface: \" + mHasSurface\n                                    + \" mSurfaceIsBad: \" + mSurfaceIsBad\n                                    + \" mWaitingForSurface: \" + mWaitingForSurface\n                                    + \" mWidth: \" + mWidth\n                                    + \" mHeight: \" + mHeight\n                                    + \" mRequestRender: \" + mRequestRender\n                                    + \" mRenderMode: \" + mRenderMode);\n                            }\n                            sGLThreadManager.wait();\n                        }\n                    } // end of synchronized(sGLThreadManager)\n\n                    if (event != null) {\n                        event.run();\n                        event = null;\n                        continue;\n                    }\n\n                    if (createEglSurface) {\n                        if (LOG_SURFACE) {\n                            Log.w(\"GLThread\", \"egl createSurface\");\n                        }\n                        if (mEglHelper.createSurface()) {\n                            synchronized(sGLThreadManager) {\n                                mFinishedCreatingEglSurface = true;\n                                sGLThreadManager.notifyAll();\n                            }\n                        } else {\n                            synchronized(sGLThreadManager) {\n                                mFinishedCreatingEglSurface = true;\n                                mSurfaceIsBad = true;\n                                sGLThreadManager.notifyAll();\n                            }\n                            continue;\n                        }\n                        createEglSurface = false;\n                    }\n\n                    if (createGlInterface) {\n                        gl = (GL10) mEglHelper.createGL();\n\n                        sGLThreadManager.checkGLDriver(gl);\n                        createGlInterface = false;\n                    }\n\n                    if (createEglContext) {\n                        if (LOG_RENDERER) {\n                            Log.w(\"GLThread\", \"onSurfaceCreated\");\n                        }\n                        GLTextureView view = mGLTextureViewWeakRef.get();\n                        if (view != null) {\n                            view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);\n                        }\n                        createEglContext = false;\n                    }\n\n                    if (sizeChanged) {\n                        if (LOG_RENDERER) {\n                            Log.w(\"GLThread\", \"onSurfaceChanged(\" + w + \", \" + h + \")\");\n                        }\n                        GLTextureView view = mGLTextureViewWeakRef.get();\n                        if (view != null) {\n                            view.mRenderer.onSurfaceChanged(gl, w, h);\n                        }\n                        sizeChanged = false;\n                    }\n\n                    if (LOG_RENDERER_DRAW_FRAME) {\n                        Log.w(\"GLThread\", \"onDrawFrame tid=\" + getId());\n                    }\n                    {\n                        GLTextureView view = mGLTextureViewWeakRef.get();\n                        if (view != null) {\n                            view.mRenderer.onDrawFrame(gl);\n                        }\n                    }\n                    int swapError = mEglHelper.swap();\n                    switch (swapError) {\n                        case EGL10.EGL_SUCCESS:\n                            break;\n                        case EGL11.EGL_CONTEXT_LOST:\n                            if (LOG_SURFACE) {\n                                Log.i(\"GLThread\", \"egl context lost tid=\" + getId());\n                            }\n                            lostEglContext = true;\n                            break;\n                        default:\n                            // Other errors typically mean that the current surface is bad,\n                            // probably because the SurfaceView surface has been destroyed,\n                            // but we haven't been notified yet.\n                            // Log the error to help developers understand why rendering stopped.\n                            EglHelper.logEglErrorAsWarning(\"GLThread\", \"eglSwapBuffers\", swapError);\n\n                            synchronized(sGLThreadManager) {\n                                mSurfaceIsBad = true;\n                                sGLThreadManager.notifyAll();\n                            }\n                            break;\n                    }\n\n                    if (wantRenderNotification) {\n                        doRenderNotification = true;\n                    }\n                }\n\n            } finally {\n                /*\n                 * clean-up everything...\n                 */\n                synchronized (sGLThreadManager) {\n                    stopEglSurfaceLocked();\n                    stopEglContextLocked();\n                }\n            }\n        }\n\n        public boolean ableToDraw() {\n            return mHaveEglContext && mHaveEglSurface && readyToDraw();\n        }\n\n        private boolean readyToDraw() {\n            return (!mPaused) && mHasSurface && (!mSurfaceIsBad)\n                && (mWidth > 0) && (mHeight > 0)\n                && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY));\n        }\n\n        public void setRenderMode(int renderMode) {\n            if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) {\n                throw new IllegalArgumentException(\"renderMode\");\n            }\n            synchronized(sGLThreadManager) {\n                mRenderMode = renderMode;\n                sGLThreadManager.notifyAll();\n            }\n        }\n\n        public int getRenderMode() {\n            synchronized(sGLThreadManager) {\n                return mRenderMode;\n            }\n        }\n\n        public void requestRender() {\n            synchronized(sGLThreadManager) {\n                mRequestRender = true;\n                sGLThreadManager.notifyAll();\n            }\n        }\n\n        public void surfaceCreated() {\n            synchronized(sGLThreadManager) {\n                if (LOG_THREADS) {\n                    Log.i(\"GLThread\", \"surfaceCreated tid=\" + getId());\n                }\n                mHasSurface = true;\n                mFinishedCreatingEglSurface = false;\n                sGLThreadManager.notifyAll();\n                while (mWaitingForSurface\n                       && !mFinishedCreatingEglSurface\n                       && !mExited) {\n                    try {\n                        sGLThreadManager.wait();\n                    } catch (InterruptedException e) {\n                        Thread.currentThread().interrupt();\n                    }\n                }\n            }\n        }\n\n        public void surfaceDestroyed() {\n            synchronized(sGLThreadManager) {\n                if (LOG_THREADS) {\n                    Log.i(\"GLThread\", \"surfaceDestroyed tid=\" + getId());\n                }\n                mHasSurface = false;\n                sGLThreadManager.notifyAll();\n                while((!mWaitingForSurface) && (!mExited)) {\n                    try {\n                        sGLThreadManager.wait();\n                    } catch (InterruptedException e) {\n                        Thread.currentThread().interrupt();\n                    }\n                }\n            }\n        }\n\n        public void onPause() {\n            synchronized (sGLThreadManager) {\n                if (LOG_PAUSE_RESUME) {\n                    Log.i(\"GLThread\", \"onPause tid=\" + getId());\n                }\n                mRequestPaused = true;\n                sGLThreadManager.notifyAll();\n                while ((! mExited) && (! mPaused)) {\n                    if (LOG_PAUSE_RESUME) {\n                        Log.i(\"Main thread\", \"onPause waiting for mPaused.\");\n                    }\n                    try {\n                        sGLThreadManager.wait();\n                    } catch (InterruptedException ex) {\n                        Thread.currentThread().interrupt();\n                    }\n                }\n            }\n        }\n\n        public void onResume() {\n            synchronized (sGLThreadManager) {\n                if (LOG_PAUSE_RESUME) {\n                    Log.i(\"GLThread\", \"onResume tid=\" + getId());\n                }\n                mRequestPaused = false;\n                mRequestRender = true;\n                mRenderComplete = false;\n                sGLThreadManager.notifyAll();\n                while ((! mExited) && mPaused && (!mRenderComplete)) {\n                    if (LOG_PAUSE_RESUME) {\n                        Log.i(\"Main thread\", \"onResume waiting for !mPaused.\");\n                    }\n                    try {\n                        sGLThreadManager.wait();\n                    } catch (InterruptedException ex) {\n                        Thread.currentThread().interrupt();\n                    }\n                }\n            }\n        }\n\n        public void onWindowResize(int w, int h) {\n            synchronized (sGLThreadManager) {\n                mWidth = w;\n                mHeight = h;\n                mSizeChanged = true;\n                mRequestRender = true;\n                mRenderComplete = false;\n                sGLThreadManager.notifyAll();\n\n                // Wait for thread to react to resize and render a frame\n                while (! mExited && !mPaused && !mRenderComplete\n                        && ableToDraw()) {\n                    if (LOG_SURFACE) {\n                        Log.i(\"Main thread\", \"onWindowResize waiting for render complete from tid=\" + getId());\n                    }\n                    try {\n                        sGLThreadManager.wait();\n                    } catch (InterruptedException ex) {\n                        Thread.currentThread().interrupt();\n                    }\n                }\n            }\n        }\n\n        public void requestExitAndWait() {\n            // don't call this from GLThread thread or it is a guaranteed\n            // deadlock!\n            synchronized(sGLThreadManager) {\n                mShouldExit = true;\n                sGLThreadManager.notifyAll();\n                while (! mExited) {\n                    try {\n                        sGLThreadManager.wait();\n                    } catch (InterruptedException ex) {\n                        Thread.currentThread().interrupt();\n                    }\n                }\n            }\n        }\n\n        public void requestReleaseEglContextLocked() {\n            mShouldReleaseEglContext = true;\n            sGLThreadManager.notifyAll();\n        }\n\n        /**\n         * Queue an \"event\" to be run on the GL rendering thread.\n         * @param r the runnable to be run on the GL rendering thread.\n         */\n        public void queueEvent(Runnable r) {\n            if (r == null) {\n                throw new IllegalArgumentException(\"r must not be null\");\n            }\n            synchronized(sGLThreadManager) {\n                mEventQueue.add(r);\n                sGLThreadManager.notifyAll();\n            }\n        }\n\n        // Once the thread is started, all accesses to the following member\n        // variables are protected by the sGLThreadManager monitor\n        private boolean mShouldExit;\n        private boolean mExited;\n        private boolean mRequestPaused;\n        private boolean mPaused;\n        private boolean mHasSurface;\n        private boolean mSurfaceIsBad;\n        private boolean mWaitingForSurface;\n        private boolean mHaveEglContext;\n        private boolean mHaveEglSurface;\n        private boolean mFinishedCreatingEglSurface;\n        private boolean mShouldReleaseEglContext;\n        private int mWidth;\n        private int mHeight;\n        private int mRenderMode;\n        private boolean mRequestRender;\n        private boolean mRenderComplete;\n        private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>();\n        private boolean mSizeChanged = true;\n\n        // End of member variables protected by the sGLThreadManager monitor.\n\n        private EglHelper mEglHelper;\n\n        /**\n         * Set once at thread construction time, nulled out when the parent view is garbage\n         * called. This weak reference allows the GLSurfaceView to be garbage collected while\n         * the GLThread is still alive.\n         */\n        private WeakReference<GLTextureView> mGLTextureViewWeakRef;\n\n    }\n\n    static class LogWriter extends Writer {\n\n        @Override public void close() {\n            flushBuilder();\n        }\n\n        @Override public void flush() {\n            flushBuilder();\n        }\n\n        @Override public void write(char[] buf, int offset, int count) {\n            for(int i = 0; i < count; i++) {\n                char c = buf[offset + i];\n                if ( c == '\\n') {\n                    flushBuilder();\n                }\n                else {\n                    mBuilder.append(c);\n                }\n            }\n        }\n\n        private void flushBuilder() {\n            if (mBuilder.length() > 0) {\n                Log.v(\"GLSurfaceView\", mBuilder.toString());\n                mBuilder.delete(0, mBuilder.length());\n            }\n        }\n\n        private StringBuilder mBuilder = new StringBuilder();\n    }\n\n\n    private void checkRenderThreadState() {\n        if (mGLThread != null) {\n            throw new IllegalStateException(\n                    \"setRenderer has already been called for this instance.\");\n        }\n    }\n\n    private static class GLThreadManager {\n        private static String TAG = \"GLThreadManager\";\n\n        public synchronized void threadExiting(GLThread thread) {\n            if (LOG_THREADS) {\n                Log.i(\"GLThread\", \"exiting tid=\" +  thread.getId());\n            }\n            thread.mExited = true;\n            if (mEglOwner == thread) {\n                mEglOwner = null;\n            }\n            notifyAll();\n        }\n\n        /*\n         * Tries once to acquire the right to use an EGL\n         * context. Does not block. Requires that we are already\n         * in the sGLThreadManager monitor when this is called.\n         *\n         * @return true if the right to use an EGL context was acquired.\n         */\n        public boolean tryAcquireEglContextLocked(GLThread thread) {\n            if (mEglOwner == thread || mEglOwner == null) {\n                mEglOwner = thread;\n                notifyAll();\n                return true;\n            }\n            checkGLESVersion();\n            if (mMultipleGLESContextsAllowed) {\n                return true;\n            }\n            // Notify the owning thread that it should release the context.\n            // TODO: implement a fairness policy. Currently\n            // if the owning thread is drawing continuously it will just\n            // reacquire the EGL context.\n            if (mEglOwner != null) {\n                mEglOwner.requestReleaseEglContextLocked();\n            }\n            return false;\n        }\n\n        /*\n         * Releases the EGL context. Requires that we are already in the\n         * sGLThreadManager monitor when this is called.\n         */\n        public void releaseEglContextLocked(GLThread thread) {\n            if (mEglOwner == thread) {\n                mEglOwner = null;\n            }\n            notifyAll();\n        }\n\n        public synchronized boolean shouldReleaseEGLContextWhenPausing() {\n            // Release the EGL context when pausing even if\n            // the hardware supports multiple EGL contexts.\n            // Otherwise the device could run out of EGL contexts.\n            return mLimitedGLESContexts;\n        }\n\n        public synchronized boolean shouldTerminateEGLWhenPausing() {\n            checkGLESVersion();\n            return !mMultipleGLESContextsAllowed;\n        }\n\n        public synchronized void checkGLDriver(GL10 gl) {\n            if (! mGLESDriverCheckComplete) {\n                checkGLESVersion();\n                String renderer = gl.glGetString(GL10.GL_RENDERER);\n                if (mGLESVersion < kGLES_20) {\n                    mMultipleGLESContextsAllowed =\n                        ! renderer.startsWith(kMSM7K_RENDERER_PREFIX);\n                    notifyAll();\n                }\n                mLimitedGLESContexts = !mMultipleGLESContextsAllowed;\n                if (LOG_SURFACE) {\n                    Log.w(TAG, \"checkGLDriver renderer = \\\"\" + renderer + \"\\\" multipleContextsAllowed = \"\n                        + mMultipleGLESContextsAllowed\n                        + \" mLimitedGLESContexts = \" + mLimitedGLESContexts);\n                }\n                mGLESDriverCheckComplete = true;\n            }\n        }\n\n        private void checkGLESVersion() {\n            if (! mGLESVersionCheckComplete) {\n//                mGLESVersion = SystemProperties.getInt(\n//                        \"ro.opengles.version\",\n//                        ConfigurationInfo.GL_ES_VERSION_UNDEFINED);\n//                if (mGLESVersion >= kGLES_20) {\n                    mMultipleGLESContextsAllowed = true;\n//                }\n                if (LOG_SURFACE) {\n                    Log.w(TAG, \"checkGLESVersion mGLESVersion =\" +\n                            \" \" + mGLESVersion + \" mMultipleGLESContextsAllowed = \" + mMultipleGLESContextsAllowed);\n                }\n                mGLESVersionCheckComplete = true;\n            }\n        }\n\n        /**\n         * This check was required for some pre-Android-3.0 hardware. Android 3.0 provides\n         * support for hardware-accelerated views, therefore multiple EGL contexts are\n         * supported on all Android 3.0+ EGL drivers.\n         */\n        private boolean mGLESVersionCheckComplete;\n        private int mGLESVersion;\n        private boolean mGLESDriverCheckComplete;\n        private boolean mMultipleGLESContextsAllowed;\n        private boolean mLimitedGLESContexts;\n        private static final int kGLES_20 = 0x20000;\n        private static final String kMSM7K_RENDERER_PREFIX =\n            \"Q3Dimension MSM7500 \";\n        private GLThread mEglOwner;\n    }\n\n    private static final GLThreadManager sGLThreadManager = new GLThreadManager();\n\n    private final WeakReference<GLTextureView> mThisWeakRef =\n            new WeakReference<GLTextureView>(this);\n    private GLThread mGLThread;\n    private GLSurfaceView.Renderer mRenderer;\n    private boolean mDetached;\n    private EGLConfigChooser mEGLConfigChooser;\n    private EGLContextFactory mEGLContextFactory;\n    private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;\n    private GLWrapper mGLWrapper;\n    private int mDebugFlags;\n    private int mEGLContextClientVersion;\n    private boolean mPreserveEGLContextOnPause;\n}\n\n"
  },
  {
    "path": "library/src/main/java/com/roger/missview/library/GLUtil.java",
    "content": "/*\n * Copyright 2014 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.roger.missview.library;\n\nimport android.graphics.Bitmap;\nimport android.opengl.GLES20;\nimport android.opengl.GLUtils;\n\nimport java.nio.ByteBuffer;\nimport java.nio.ByteOrder;\nimport java.nio.FloatBuffer;\n\npublic class GLUtil {\n\n    public static final int BYTES_PER_FLOAT = 4;\n\n    public static int loadShader(int type, String shaderCode) {\n        // create a vertex shader type (GLES20.GL_VERTEX_SHADER)\n        // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)\n        int shaderHandle = GLES20.glCreateShader(type);\n\n        // add the source code to the shader and compile it\n        GLES20.glShaderSource(shaderHandle, shaderCode);\n        GLES20.glCompileShader(shaderHandle);\n        checkGlError(\"glCompileShader\");\n        return shaderHandle;\n    }\n\n    public static int createAndLinkProgram(int vertexShaderHandle, int fragShaderHandle,\n                                           String[] attributes) {\n        int programHandle = GLES20.glCreateProgram();\n        GLUtil.checkGlError(\"glCreateProgram\");\n        GLES20.glAttachShader(programHandle, vertexShaderHandle);\n        GLES20.glAttachShader(programHandle, fragShaderHandle);\n        if (attributes != null) {\n            final int size = attributes.length;\n            for (int i = 0; i < size; i++) {\n                GLES20.glBindAttribLocation(programHandle, i, attributes[i]);\n            }\n        }\n        GLES20.glLinkProgram(programHandle);\n        GLUtil.checkGlError(\"glLinkProgram\");\n        GLES20.glDeleteShader(vertexShaderHandle);\n        GLES20.glDeleteShader(fragShaderHandle);\n        return programHandle;\n    }\n\n    public static int loadTexture(Bitmap bitmap) {\n        final int[] textureHandle = new int[1];\n\n        GLES20.glGenTextures(1, textureHandle, 0);\n        GLUtil.checkGlError(\"glGenTextures\");\n\n        if (textureHandle[0] != 0) {\n            // Bind to the texture in OpenGL\n            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);\n\n            // Set filtering\n            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,\n                    GLES20.GL_CLAMP_TO_EDGE);\n            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,\n                    GLES20.GL_CLAMP_TO_EDGE);\n            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,\n                    GLES20.GL_LINEAR);\n            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,\n                    GLES20.GL_LINEAR);\n\n            // Load the bitmap into the bound texture.\n            GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);\n            GLUtil.checkGlError(\"texImage2D\");\n        }\n\n        if (textureHandle[0] == 0) {\n        }\n\n        return textureHandle[0];\n    }\n\n    public static void checkGlError(String glOperation) {\n        int error;\n        while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {\n        }\n    }\n\n    public static FloatBuffer asFloatBuffer(float[] array) {\n        FloatBuffer buffer = newFloatBuffer(array.length);\n        buffer.put(array);\n        buffer.position(0);\n        return buffer;\n    }\n\n    public static FloatBuffer newFloatBuffer(int size) {\n        FloatBuffer buffer = ByteBuffer.allocateDirect(size * BYTES_PER_FLOAT)\n                .order(ByteOrder.nativeOrder())\n                .asFloatBuffer();\n        buffer.position(0);\n        return buffer;\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/com/roger/missview/library/MissView.java",
    "content": "package com.roger.missview.library;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.opengl.GLSurfaceView;\nimport android.util.AttributeSet;\n\n/**\n * Created by Administrator on 2015/4/30.\n */\npublic class MissView extends GLTextureView\n    implements RenderController.Callbacks, BlurRenderer.Callbacks {\n  private BlurRenderer mRenderer;\n  private RenderController mRenderController;\n\n  public MissView(Context context) {\n    super(context);\n    init();\n  }\n\n  public MissView(Context context, AttributeSet attrs) {\n    super(context, attrs);\n    init();\n  }\n\n  private void init() {\n    mRenderer = new BlurRenderer(getContext(), this);\n    setEGLContextClientVersion(2);\n    setEGLConfigChooser(8, 8, 8, 8, 0, 0);\n    setRenderer(mRenderer);\n    setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);\n  }\n\n  public void initPicture(String mPictureName) {\n    mRenderController = new DemoRenderController(getContext(), mRenderer, this, true, mPictureName);\n    mRenderer.setDemoMode(true);\n    mRenderController.setVisible(true);\n  }\n\n  public void initPicture(Bitmap mBitmap) {\n    mRenderController = new DemoRenderController(getContext(), mRenderer, this, true, mBitmap);\n    mRenderer.setDemoMode(true);\n    mRenderController.setVisible(true);\n  }\n\n  @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {\n    super.onSizeChanged(w, h, oldw, oldh);\n    mRenderer.hintViewportSize(w, h);\n    mRenderController.reloadCurrentArtwork(true);\n  }\n\n  @Override protected void onDetachedFromWindow() {\n    super.onDetachedFromWindow();\n    if (null != mRenderController) {\n      mRenderController.destroy();\n      queueEventOnGlThread(new Runnable() {\n        @Override public void run() {\n          mRenderer.destroy();\n        }\n      });\n    }\n  }\n\n  @Override public void queueEventOnGlThread(Runnable runnable) {\n    if (this == null) {\n      return;\n    }\n    super.queueEvent(runnable);\n  }\n\n  @Override public void requestRender() {\n    if (this == null) {\n      return;\n    }\n    super.requestRender();\n  }\n}\n"
  },
  {
    "path": "library/src/main/java/com/roger/missview/library/RenderController.java",
    "content": "/*\n * Copyright 2014 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.roger.missview.library;\n\nimport android.content.Context;\nimport android.os.AsyncTask;\n\nimport com.roger.missview.library.util.BitmapRegionLoader;\n\n\npublic abstract class RenderController {\n    protected Context mContext;\n    protected BlurRenderer mRenderer;\n    protected Callbacks mCallbacks;\n    protected boolean mVisible;\n    private BitmapRegionLoader mQueuedBitmapRegionLoader;\n\n    public RenderController(Context context, BlurRenderer renderer, Callbacks callbacks) {\n        mRenderer = renderer;\n        mContext = context;\n        mCallbacks = callbacks;\n    }\n\n    public void destroy() {\n        if (mQueuedBitmapRegionLoader != null) {\n            mQueuedBitmapRegionLoader.destroy();\n        }\n    }\n\n\n    protected abstract BitmapRegionLoader openDownloadedCurrentArtwork(boolean forceReload);\n\n    public void reloadCurrentArtwork(final boolean forceReload) {\n        new AsyncTask<Void, Void, BitmapRegionLoader>() {\n            @Override\n            protected BitmapRegionLoader doInBackground(Void... voids) {\n                // openDownloadedCurrentArtwork should be called on a background thread\n                return openDownloadedCurrentArtwork(forceReload);\n            }\n\n            @Override\n            protected void onPostExecute(final BitmapRegionLoader bitmapRegionLoader) {\n                if (bitmapRegionLoader == null) {\n                    return;\n                }\n\n                mCallbacks.queueEventOnGlThread(new Runnable() {\n                    @Override\n                    public void run() {\n                        if (mVisible) {\n                            mRenderer.setAndConsumeBitmapRegionLoader(bitmapRegionLoader);\n                        } else {\n                            mQueuedBitmapRegionLoader = bitmapRegionLoader;\n                        }\n                    }\n                });\n            }\n        }.execute((Void) null);\n    }\n\n    public void setVisible(boolean visible) {\n        mVisible = visible;\n        if (visible) {\n            mCallbacks.queueEventOnGlThread(new Runnable() {\n                @Override\n                public void run() {\n                    if (mQueuedBitmapRegionLoader != null) {\n                        mRenderer.setAndConsumeBitmapRegionLoader(mQueuedBitmapRegionLoader);\n                        mQueuedBitmapRegionLoader = null;\n                    }\n                }\n            });\n            mCallbacks.requestRender();\n        }\n    }\n\n    public static interface Callbacks {\n        void queueEventOnGlThread(Runnable runnable);\n\n        void requestRender();\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/com/roger/missview/library/util/ArtDetailViewport.java",
    "content": "/*\n * Copyright 2014 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.roger.missview.library.util;\n\nimport android.graphics.RectF;\n\n\n// Singleton that also behaves as an event\npublic class ArtDetailViewport {\n    private volatile RectF mViewport0 = new RectF();\n    private volatile RectF mViewport1 = new RectF();\n    private boolean mFromUser;\n\n    private static ArtDetailViewport sInstance = new ArtDetailViewport();\n\n    public static ArtDetailViewport getInstance() {\n        return sInstance;\n    }\n\n    private ArtDetailViewport() {\n    }\n\n    public RectF getViewport(int id) {\n        return (id == 0) ? mViewport0 : mViewport1;\n    }\n\n    public ArtDetailViewport setViewport(int id, RectF viewport, boolean fromUser) {\n        return setViewport(id, viewport.left, viewport.top, viewport.right, viewport.bottom,\n                fromUser);\n    }\n\n    public ArtDetailViewport setViewport(int id, float left, float top, float right, float bottom,\n            boolean fromUser) {\n        mFromUser = fromUser;\n        getViewport(id).set(left, top, right, bottom);\n        return this;\n    }\n\n    public boolean isFromUser() {\n        return mFromUser;\n    }\n\n    public ArtDetailViewport setDefaultViewport(int id, float bitmapAspectRatio,\n            float screenAspectRatio) {\n        mFromUser = false;\n        if (bitmapAspectRatio > screenAspectRatio) {\n            getViewport(id).set(\n                    0.5f - screenAspectRatio / bitmapAspectRatio / 2,\n                    0,\n                    0.5f + screenAspectRatio / bitmapAspectRatio / 2,\n                    1);\n        } else {\n            getViewport(id).set(\n                    0,\n                    0.5f - bitmapAspectRatio / screenAspectRatio / 2,\n                    1,\n                    0.5f + bitmapAspectRatio / screenAspectRatio / 2);\n        }\n        return this;\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/com/roger/missview/library/util/BitmapRegionLoader.java",
    "content": "/*\n * Copyright 2014 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.roger.missview.library.util;\n\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapRegionDecoder;\nimport android.graphics.Matrix;\nimport android.graphics.Rect;\nimport android.util.Log;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\nimport static android.graphics.BitmapFactory.Options;\n\n/**\n * Wrapper for {@link android.graphics.BitmapRegionDecoder} with some extra functionality.\n */\npublic class BitmapRegionLoader {\n    private boolean mValid = false;\n    private int mRotation = 0;\n    private int mOriginalWidth;\n    private int mOriginalHeight;\n    private Rect mTempRect = new Rect();\n    private InputStream mInputStream;\n    private volatile BitmapRegionDecoder mBitmapRegionDecoder;\n    private Matrix mRotateMatrix;\n\n    public static BitmapRegionLoader newInstance(InputStream in) {\n        return newInstance(in, 0);\n    }\n\n    public static BitmapRegionLoader newInstance(InputStream in, int rotation) {\n        if (in == null) {\n            Log.e(\"BitmapRegionLoader\", \"InputStream is null!\");\n            return null;\n        }\n\n        BitmapRegionLoader loader = new BitmapRegionLoader(in);\n        if (loader.mValid) {\n            loader.mRotation = rotation;\n            if (loader.mRotation != 0) {\n                loader.mRotateMatrix = new Matrix();\n                loader.mRotateMatrix.postRotate(rotation);\n            }\n            return loader;\n        }\n\n        return null;\n    }\n\n    private BitmapRegionLoader(InputStream in) {\n        mInputStream = in;\n        try {\n            mBitmapRegionDecoder = BitmapRegionDecoder.newInstance(in, false);\n            mOriginalWidth = mBitmapRegionDecoder.getWidth();\n            mOriginalHeight = mBitmapRegionDecoder.getHeight();\n            mValid = true;\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n    }\n\n    /**\n     * Key difference, aside from support for ration, from\n     * {@link android.graphics.BitmapRegionDecoder#decodeRegion(android.graphics.Rect, android.graphics.BitmapFactory.Options)} in this implementation is that even\n     * if <code>inBitmap</code> is given, a sub-bitmap might be returned.\n     */\n    public synchronized Bitmap decodeRegion(Rect rect, Options options) {\n        int unsampledInBitmapWidth = -1;\n        int unsampledInBitmapHeight = -1;\n        int sampleSize = Math.max(1, options != null ? options.inSampleSize : 1);\n        if (options != null && options.inBitmap != null) {\n            unsampledInBitmapWidth = options.inBitmap.getWidth() * sampleSize;\n            unsampledInBitmapHeight = options.inBitmap.getHeight() * sampleSize;\n        }\n\n        // Decode with rotation\n        switch (mRotation) {\n            case 90:\n                mTempRect.set(\n                        rect.top, mOriginalHeight - rect.right,\n                        rect.bottom, mOriginalHeight - rect.left);\n                break;\n\n            case 180:\n                mTempRect.set(\n                        mOriginalWidth - rect.right, mOriginalHeight - rect.bottom,\n                        mOriginalWidth - rect.left, mOriginalHeight - rect.top);\n                break;\n\n            case 270:\n                mTempRect.set(\n                        mOriginalWidth - rect.bottom, rect.left,\n                        mOriginalWidth - rect.top, rect.right);\n                break;\n\n            default:\n                mTempRect.set(rect);\n        }\n\n        Bitmap bitmap = mBitmapRegionDecoder.decodeRegion(mTempRect, options);\n        if (options != null && options.inBitmap != null &&\n                ((mTempRect.width() != unsampledInBitmapWidth\n                        || mTempRect.height() != unsampledInBitmapHeight))) {\n            // Need to extract the sub-bitmap\n            Bitmap subBitmap = Bitmap.createBitmap(\n                    bitmap, 0, 0,\n                    mTempRect.width() / sampleSize,\n                    mTempRect.height() / sampleSize);\n            if (bitmap != options.inBitmap) {\n                bitmap.recycle();\n            }\n            bitmap = subBitmap;\n        }\n\n        if (mRotateMatrix != null) {\n            // Rotate decoded bitmap\n            Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0,\n                    bitmap.getWidth(), bitmap.getHeight(),\n                    mRotateMatrix, true);\n            if (options == null || bitmap != options.inBitmap) {\n                bitmap.recycle();\n            }\n            bitmap = rotatedBitmap;\n        }\n\n        return bitmap;\n    }\n\n    public synchronized int getWidth() {\n        return (mRotation == 90 || mRotation == 270) ? mOriginalHeight : mOriginalWidth;\n    }\n\n    public synchronized int getHeight() {\n        return (mRotation == 90 || mRotation == 270) ? mOriginalWidth : mOriginalHeight;\n    }\n\n    public synchronized void destroy() {\n        mBitmapRegionDecoder.recycle();\n        mBitmapRegionDecoder = null;\n        try {\n            mInputStream.close();\n        } catch (IOException ignored) {\n            ignored.printStackTrace();\n        }\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/com/roger/missview/library/util/ImageBlurrer.java",
    "content": "/*\n * Copyright 2014 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.roger.missview.library.util;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.renderscript.Allocation;\nimport android.renderscript.Element;\nimport android.renderscript.Matrix3f;\nimport android.renderscript.RenderScript;\nimport android.renderscript.ScriptIntrinsicBlur;\nimport android.renderscript.ScriptIntrinsicColorMatrix;\n\npublic class ImageBlurrer {\n    public static final int MAX_SUPPORTED_BLUR_PIXELS = 25;\n    private RenderScript mRS;\n\n    private ScriptIntrinsicBlur mSIBlur;\n    private ScriptIntrinsicColorMatrix mSIGrey;\n    private Allocation mTmp1;\n    private Allocation mTmp2;\n\n    public ImageBlurrer(Context context) {\n        mRS = RenderScript.create(context);\n        mSIBlur = ScriptIntrinsicBlur.create(mRS, Element.U8_4(mRS));\n        mSIGrey = ScriptIntrinsicColorMatrix.create(mRS);\n    }\n\n    public Bitmap blurBitmap(Bitmap src, float radius, float desaturateAmount) {\n        Bitmap dest = Bitmap.createBitmap(src);\n        if ((int) radius == 0) {\n            return dest;\n        }\n\n        if (mTmp1 != null) {\n            mTmp1.destroy();\n        }\n        if (mTmp2 != null) {\n            mTmp2.destroy();\n        }\n\n        mTmp1 = Allocation.createFromBitmap(mRS, src);\n        mTmp2 = Allocation.createFromBitmap(mRS, dest);\n\n        mSIBlur.setRadius((int) radius);\n        mSIBlur.setInput(mTmp1);\n        mSIBlur.forEach(mTmp2);\n\n        if (desaturateAmount > 0) {\n            desaturateAmount = MathUtil.constrain(0, 1, desaturateAmount);\n            Matrix3f m = new Matrix3f(new float[]{\n                    MathUtil.interpolate(1, 0.299f, desaturateAmount),\n                    MathUtil.interpolate(0, 0.299f, desaturateAmount),\n                    MathUtil.interpolate(0, 0.299f, desaturateAmount),\n\n                    MathUtil.interpolate(0, 0.587f, desaturateAmount),\n                    MathUtil.interpolate(1, 0.587f, desaturateAmount),\n                    MathUtil.interpolate(0, 0.587f, desaturateAmount),\n\n                    MathUtil.interpolate(0, 0.114f, desaturateAmount),\n                    MathUtil.interpolate(0, 0.114f, desaturateAmount),\n                    MathUtil.interpolate(1, 0.114f, desaturateAmount),\n            });\n            mSIGrey.setColorMatrix(m);\n            mSIGrey.forEach(mTmp2, mTmp1);\n            mTmp1.copyTo(dest);\n        } else {\n            mTmp2.copyTo(dest);\n        }\n        return dest;\n    }\n\n    public void destroy() {\n        mSIBlur.destroy();\n        if (mTmp1 != null) {\n            mTmp1.destroy();\n        }\n        if (mTmp2 != null) {\n            mTmp2.destroy();\n        }\n        mRS.destroy();\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/com/roger/missview/library/util/ImageUtil.java",
    "content": "/*\n * Copyright 2014 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.roger.missview.library.util;\n\nimport android.graphics.Bitmap;\nimport android.graphics.Color;\n\npublic class ImageUtil {\n    // Make sure input images are very small!\n    public static float calculateDarkness(Bitmap bitmap) {\n        if (bitmap == null) {\n            return 0;\n        }\n\n        int width = bitmap.getWidth();\n        int height = bitmap.getHeight();\n\n        int totalLum = 0;\n        int n = 0;\n        int x, y, color;\n        for (y = 0; y < height; y++) {\n            for (x = 0; x < width; x++) {\n                ++n;\n                color = bitmap.getPixel(x, y);\n                totalLum += (0.21f * Color.red(color)\n                        + 0.71f * Color.green(color)\n                        + 0.07f * Color.blue(color));\n            }\n        }\n\n        return (totalLum / n) / 256f;\n    }\n\n    private ImageUtil() {\n    }\n\n    public static int calculateSampleSize(int rawHeight, int targetHeight) {\n        int sampleSize = 1;\n        while (rawHeight / (sampleSize << 1) > targetHeight) {\n            sampleSize <<= 1;\n        }\n        return sampleSize;\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/com/roger/missview/library/util/MathUtil.java",
    "content": "/*\n * Copyright 2014 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.roger.missview.library.util;\n\nimport android.util.FloatMath;\n\npublic class MathUtil {\n    public static float constrain(float min, float max, float v) {\n        return Math.max(min, Math.min(max, v));\n    }\n\n    public static float interpolate(float x1, float x2, float f) {\n        return x1 + (x2 - x1) * f;\n    }\n\n    public static float uninterpolate(float x1, float x2, float v) {\n        if (x2 - x1 == 0) {\n            throw new IllegalArgumentException(\"Can't reverse interpolate with domain size of 0\");\n        }\n        return (v - x1) / (x2 - x1);\n    }\n\n    public static float dist(float x, float y) {\n        return FloatMath.sqrt(x * x + y * y);\n    }\n\n    public static int floorEven(int num) {\n        return num & ~0x01;\n    }\n\n    public static int roundMult4(int num) {\n        return (num + 2) & ~0x03;\n    }\n\n    public static boolean isEven(int num) {\n        return num % 2 == 0;\n    }\n\n    private MathUtil() {\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/com/roger/missview/library/util/TickingFloatAnimator.java",
    "content": "/*\n * Copyright 2014 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.roger.missview.library.util;\n\nimport android.animation.TimeInterpolator;\nimport android.os.SystemClock;\nimport android.view.animation.AccelerateDecelerateInterpolator;\n\n// Non thread-safe\npublic class TickingFloatAnimator {\n    private float mStartValue = 0;\n    private float mCurrentValue;\n    private float mEndValue;\n    private boolean mRunning = false;\n    private long mStartTime;\n    private int mDuration = 1000;\n    private Runnable mEndCallback;\n    private TimeInterpolator mInterpolator = new AccelerateDecelerateInterpolator();\n\n    public static TickingFloatAnimator create() {\n        return new TickingFloatAnimator();\n    }\n\n    public TickingFloatAnimator from(float startValue) {\n        cancel();\n        mStartValue = startValue;\n        mCurrentValue = startValue;\n        return this;\n    }\n\n    public TickingFloatAnimator to(float endValue) {\n        mEndValue = endValue;\n        return this;\n    }\n\n    public TickingFloatAnimator withDuration(int duration) {\n        mDuration = Math.max(0, duration);\n        return this;\n    }\n\n    public TickingFloatAnimator withInterpolator(TimeInterpolator interpolator) {\n        mInterpolator = interpolator;\n        return this;\n    }\n\n    public TickingFloatAnimator withEndListener(Runnable listener) {\n        mEndCallback = listener;\n        return this;\n    }\n\n    public void cancel() {\n        mRunning = false;\n        mEndValue = mCurrentValue;\n    }\n\n    public boolean tick() {\n        if (!mRunning) {\n            return false;\n        }\n\n        float t;\n        if (mDuration <= 0) {\n            t = 1;\n        } else {\n            t = (float) (SystemClock.elapsedRealtime() - mStartTime) * 1f / mDuration;\n            if (t >= 1) {\n                t = 1;\n            }\n        }\n\n        if (t >= 1) {\n            // Ended\n            mRunning = false;\n            mCurrentValue = mEndValue;\n            if (mEndCallback != null) {\n                mEndCallback.run();\n            }\n            return false;\n        }\n\n        // Still running; compute value\n        mCurrentValue = mStartValue + mInterpolator.getInterpolation(t) * (mEndValue - mStartValue);\n        mRunning = true;\n        return true;\n    }\n\n    public void start() {\n        mRunning = true;\n        mStartValue = mCurrentValue;\n        mStartTime = SystemClock.elapsedRealtime();\n        tick();\n    }\n\n    public boolean isRunning() {\n        return mRunning;\n    }\n\n    public float currentValue() {\n        return mCurrentValue;\n    }\n\n    private TickingFloatAnimator() {\n    }\n}\n"
  },
  {
    "path": "library/src/main/java/com/roger/missview/library/util/UriUtil.java",
    "content": "package com.roger.missview.library.util;\n\nimport android.annotation.TargetApi;\nimport android.content.ContentUris;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.os.Environment;\nimport android.provider.DocumentsContract;\nimport android.provider.MediaStore;\n\n/**\n * Created by Administrator on 2015/4/28.\n */\npublic class UriUtil {\n    /**\n     * 根据Uri获取图片绝对路径，解决Android4.4以上版本Uri转换\n     * @param context\n     * @param imageUri\n     */\n    @TargetApi(19)\n    public static String getImageAbsolutePath(Context context, Uri imageUri) {\n        if (context == null || imageUri == null)\n            return null;\n        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri(context, imageUri)) {\n            if (isExternalStorageDocument(imageUri)) {\n                String docId = DocumentsContract.getDocumentId(imageUri);\n                String[] split = docId.split(\":\");\n                String type = split[0];\n                if (\"primary\".equalsIgnoreCase(type)) {\n                    return Environment.getExternalStorageDirectory() + \"/\" + split[1];\n                }\n            } else if (isDownloadsDocument(imageUri)) {\n                String id = DocumentsContract.getDocumentId(imageUri);\n                Uri contentUri = ContentUris.withAppendedId(Uri.parse(\"content://downloads/public_downloads\"), Long.valueOf(id));\n                return getDataColumn(context, contentUri, null, null);\n            } else if (isMediaDocument(imageUri)) {\n                String docId = DocumentsContract.getDocumentId(imageUri);\n                String[] split = docId.split(\":\");\n                String type = split[0];\n                Uri contentUri = null;\n                if (\"image\".equals(type)) {\n                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;\n                } else if (\"video\".equals(type)) {\n                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;\n                } else if (\"audio\".equals(type)) {\n                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;\n                }\n                String selection = MediaStore.Images.Media._ID + \"=?\";\n                String[] selectionArgs = new String[] { split[1] };\n                return getDataColumn(context, contentUri, selection, selectionArgs);\n            }\n        } // MediaStore (and general)\n        else if (\"content\".equalsIgnoreCase(imageUri.getScheme())) {\n            // Return the remote address\n            if (isGooglePhotosUri(imageUri))\n                return imageUri.getLastPathSegment();\n            return getDataColumn(context, imageUri, null, null);\n        }\n        // File\n        else if (\"file\".equalsIgnoreCase(imageUri.getScheme())) {\n            return imageUri.getPath();\n        }\n        return null;\n    }\n\n    public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {\n        Cursor cursor = null;\n        String column = MediaStore.Images.Media.DATA;\n        String[] projection = { column };\n        try {\n            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);\n            if (cursor != null && cursor.moveToFirst()) {\n                int index = cursor.getColumnIndexOrThrow(column);\n                return cursor.getString(index);\n            }\n        } finally {\n            if (cursor != null)\n                cursor.close();\n        }\n        return null;\n    }\n\n    /**\n     * @param uri The Uri to check.\n     * @return Whether the Uri authority is ExternalStorageProvider.\n     */\n    public static boolean isExternalStorageDocument(Uri uri) {\n        return \"com.android.externalstorage.documents\".equals(uri.getAuthority());\n    }\n\n    /**\n     * @param uri The Uri to check.\n     * @return Whether the Uri authority is DownloadsProvider.\n     */\n    public static boolean isDownloadsDocument(Uri uri) {\n        return \"com.android.providers.downloads.documents\".equals(uri.getAuthority());\n    }\n\n    /**\n     * @param uri The Uri to check.\n     * @return Whether the Uri authority is MediaProvider.\n     */\n    public static boolean isMediaDocument(Uri uri) {\n        return \"com.android.providers.media.documents\".equals(uri.getAuthority());\n    }\n\n    /**\n     * @param uri The Uri to check.\n     * @return Whether the Uri authority is Google Photos.\n     */\n    public static boolean isGooglePhotosUri(Uri uri) {\n        return \"com.google.android.apps.photos.content\".equals(uri.getAuthority());\n    }\n}\n"
  },
  {
    "path": "settings.gradle",
    "content": "include ':library', ':demo'\n"
  }
]