Browse Source

Commit inicial

Hugo 5 years ago
commit
7c75b0c04b
100 changed files with 5683 additions and 0 deletions
  1. 40 0
      .gitignore
  2. 55 0
      .travis.yml
  3. 5 0
      CONTRIBUTING.md
  4. 1 0
      FishBun/.gitignore
  5. 50 0
      FishBun/build.gradle
  6. 34 0
      FishBun/proguard-rules.pro
  7. 28 0
      FishBun/src/main/AndroidManifest.xml
  8. 24 0
      FishBun/src/main/java/com/sangcomz/fishbun/BaseActivity.java
  9. 27 0
      FishBun/src/main/java/com/sangcomz/fishbun/BaseFragment.java
  10. 12 0
      FishBun/src/main/java/com/sangcomz/fishbun/BaseParams.java
  11. 27 0
      FishBun/src/main/java/com/sangcomz/fishbun/BaseProperty.java
  12. 55 0
      FishBun/src/main/java/com/sangcomz/fishbun/CustomizationProperty.java
  13. 38 0
      FishBun/src/main/java/com/sangcomz/fishbun/FishBun.java
  14. 267 0
      FishBun/src/main/java/com/sangcomz/fishbun/FishBunCreator.java
  15. 7 0
      FishBun/src/main/java/com/sangcomz/fishbun/FishBunFileProvider.java
  16. 158 0
      FishBun/src/main/java/com/sangcomz/fishbun/Fishton.java
  17. 13 0
      FishBun/src/main/java/com/sangcomz/fishbun/adapter/image/ImageAdapter.java
  18. 32 0
      FishBun/src/main/java/com/sangcomz/fishbun/adapter/image/impl/PicassoAdapter.java
  19. 106 0
      FishBun/src/main/java/com/sangcomz/fishbun/adapter/view/AlbumListAdapter.java
  20. 67 0
      FishBun/src/main/java/com/sangcomz/fishbun/adapter/view/DetailViewPagerAdapter.java
  21. 274 0
      FishBun/src/main/java/com/sangcomz/fishbun/adapter/view/PickerGridAdapter.java
  22. 52 0
      FishBun/src/main/java/com/sangcomz/fishbun/bean/Album.java
  23. 26 0
      FishBun/src/main/java/com/sangcomz/fishbun/define/Define.java
  24. 65 0
      FishBun/src/main/java/com/sangcomz/fishbun/permission/PermissionCheck.java
  25. 295 0
      FishBun/src/main/java/com/sangcomz/fishbun/ui/album/AlbumActivity.java
  26. 144 0
      FishBun/src/main/java/com/sangcomz/fishbun/ui/album/AlbumController.java
  27. 161 0
      FishBun/src/main/java/com/sangcomz/fishbun/ui/detail/DetailActivity.java
  28. 13 0
      FishBun/src/main/java/com/sangcomz/fishbun/ui/detail/DetailController.java
  29. 285 0
      FishBun/src/main/java/com/sangcomz/fishbun/ui/picker/PickerActivity.java
  30. 160 0
      FishBun/src/main/java/com/sangcomz/fishbun/ui/picker/PickerController.java
  31. 73 0
      FishBun/src/main/java/com/sangcomz/fishbun/util/CameraUtil.java
  32. 34 0
      FishBun/src/main/java/com/sangcomz/fishbun/util/CustomPagerSnapHelper.java
  33. 168 0
      FishBun/src/main/java/com/sangcomz/fishbun/util/RadioWithTextButton.java
  34. 14 0
      FishBun/src/main/java/com/sangcomz/fishbun/util/RegexUtil.java
  35. 9 0
      FishBun/src/main/java/com/sangcomz/fishbun/util/ScanListener.java
  36. 44 0
      FishBun/src/main/java/com/sangcomz/fishbun/util/SingleMediaScanner.java
  37. 26 0
      FishBun/src/main/java/com/sangcomz/fishbun/util/SquareFrameLayout.java
  38. 26 0
      FishBun/src/main/java/com/sangcomz/fishbun/util/SquareImageView.java
  39. 70 0
      FishBun/src/main/java/com/sangcomz/fishbun/util/TextDrawable.java
  40. 1292 0
      FishBun/src/main/java/com/sangcomz/fishbun/util/TouchImageView.java
  41. 37 0
      FishBun/src/main/java/com/sangcomz/fishbun/util/UiUtil.java
  42. 10 0
      FishBun/src/main/res/drawable/ic_add_a_photo_gray_100dp.xml
  43. 9 0
      FishBun/src/main/res/drawable/ic_add_a_photo_white_24dp.xml
  44. 9 0
      FishBun/src/main/res/drawable/ic_arrow_back_white_24dp.xml
  45. 9 0
      FishBun/src/main/res/drawable/ic_done_white_24dp.xml
  46. 9 0
      FishBun/src/main/res/drawable/ic_photo_library_gray_100dp.xml
  47. 9 0
      FishBun/src/main/res/drawable/ic_photo_white_24dp.xml
  48. 44 0
      FishBun/src/main/res/layout/activity_detail_actiivy.xml
  49. 68 0
      FishBun/src/main/res/layout/activity_photo_album.xml
  50. 29 0
      FishBun/src/main/res/layout/activity_photo_picker.xml
  51. 44 0
      FishBun/src/main/res/layout/album_item.xml
  52. 20 0
      FishBun/src/main/res/layout/detail_item.xml
  53. 21 0
      FishBun/src/main/res/layout/header_item.xml
  54. 25 0
      FishBun/src/main/res/layout/thumb_item.xml
  55. 9 0
      FishBun/src/main/res/menu/menu_photo_album.xml
  56. 13 0
      FishBun/src/main/res/values-ko/strings.xml
  57. 3 0
      FishBun/src/main/res/values/dimens.xml
  58. 14 0
      FishBun/src/main/res/values/strings.xml
  59. 12 0
      FishBun/src/main/res/values/styles.xml
  60. 4 0
      FishBun/src/main/res/xml/provider_paths.xml
  61. 23 0
      FishBun/src/test/java/com/sangcomz/fishbun/RegexTest.java
  62. 1 0
      FishBunDemo/.gitignore
  63. 93 0
      FishBunDemo/build.gradle
  64. 28 0
      FishBunDemo/proguard-rules.pro
  65. 27 0
      FishBunDemo/src/main/AndroidManifest.xml
  66. BIN
      FishBunDemo/src/main/ic_launcher-web.png
  67. 14 0
      FishBunDemo/src/main/java/com/sangcomz/fishbundemo/CommonApplication.java
  68. 72 0
      FishBunDemo/src/main/java/com/sangcomz/fishbundemo/ImageAdapter.java
  69. 26 0
      FishBunDemo/src/main/java/com/sangcomz/fishbundemo/ImageController.java
  70. 60 0
      FishBunDemo/src/main/java/com/sangcomz/fishbundemo/MainActivity.java
  71. 82 0
      FishBunDemo/src/main/java/com/sangcomz/fishbundemo/SubFragment.java
  72. 134 0
      FishBunDemo/src/main/java/com/sangcomz/fishbundemo/WithActivityActivity.java
  73. 22 0
      FishBunDemo/src/main/java/com/sangcomz/fishbundemo/WithFragmentActivity.java
  74. 0 0
      FishBunDemo/src/main/play/en-US/whatsnew
  75. 9 0
      FishBunDemo/src/main/res/drawable/ic_arrow_back_black_24dp.xml
  76. 9 0
      FishBunDemo/src/main/res/drawable/ic_arrow_back_white_24dp.xml
  77. 9 0
      FishBunDemo/src/main/res/drawable/ic_check_black_24dp.xml
  78. 9 0
      FishBunDemo/src/main/res/drawable/ic_check_white_24dp.xml
  79. 9 0
      FishBunDemo/src/main/res/drawable/ic_custom_back_white.xml
  80. 10 0
      FishBunDemo/src/main/res/drawable/ic_custom_ok.xml
  81. 43 0
      FishBunDemo/src/main/res/layout/activity_main.xml
  82. 22 0
      FishBunDemo/src/main/res/layout/activity_withactivity.xml
  83. 13 0
      FishBunDemo/src/main/res/layout/activity_withfragment.xml
  84. 29 0
      FishBunDemo/src/main/res/layout/fragment_sub.xml
  85. 7 0
      FishBunDemo/src/main/res/layout/item.xml
  86. 9 0
      FishBunDemo/src/main/res/menu/menu_main.xml
  87. BIN
      FishBunDemo/src/main/res/mipmap-hdpi/ic_add_white.png
  88. BIN
      FishBunDemo/src/main/res/mipmap-hdpi/ic_launcher.png
  89. BIN
      FishBunDemo/src/main/res/mipmap-mdpi/ic_launcher.png
  90. BIN
      FishBunDemo/src/main/res/mipmap-xhdpi/ic_launcher.png
  91. BIN
      FishBunDemo/src/main/res/mipmap-xxhdpi/ic_launcher.png
  92. BIN
      FishBunDemo/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  93. 7 0
      FishBunDemo/src/main/res/values-ko/strings.xml
  94. 6 0
      FishBunDemo/src/main/res/values-w820dp/dimens.xml
  95. 6 0
      FishBunDemo/src/main/res/values/colors.xml
  96. 5 0
      FishBunDemo/src/main/res/values/dimens.xml
  97. 11 0
      FishBunDemo/src/main/res/values/strings.xml
  98. 11 0
      FishBunDemo/src/main/res/values/styles.xml
  99. 202 0
      LICENSE
  100. 0 0
      README.md

+ 40 - 0
.gitignore

@@ -0,0 +1,40 @@
+.idea/**
+.idea
+.gradle
+/local.properties
+.DS_Store
+/build
+/captures
+
+# Built application files
+*.apk
+*.ap_
+
+# Files for the Dalvik VM
+*.dex
+
+# Java class files
+*.class
+
+# Generated files
+bin/
+gen/
+
+# Gradle files
+.gradle/
+build/
+/*/build/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Proguard folder generated by Eclipse
+proguard/
+
+# Log Files
+*.log
+
+*.iml
+
+playstore_key.json
+/playstore

+ 55 - 0
.travis.yml

@@ -0,0 +1,55 @@
+
+language: android
+
+
+android:
+  components:
+    - build-tools-28.0.3
+    - android-15
+    - android-22
+    - android-28
+    - tools
+    - platform-tools
+    - add-on
+    - extra
+    - extra-google-google_play_services
+    - extra-google-m2repository
+    - extra-android-m2repository
+# Images
+    - sys-img-armeabi-v7a-android-22
+    - sys-img-${ANDROID_ABI}-${ANDROID_TARGET}
+
+licenses:
+  - 'android-sdk-preview-license-.+'
+  - 'android-sdk-license-.+'
+  - 'google-gdk-license-.+'
+
+notifications:
+  email: true
+
+env:
+  matrix:
+    - ANDROID_TARGET=google_apis-25  ANDROID_ABI=armeabi-v7a
+
+  global:
+    - ADB_INSTALL_TIMEOUT=20
+
+
+
+before_install:
+  - chmod +x gradlew
+
+
+before_script:
+  - echo no | android create avd --force -n test -t android-22 --abi armeabi-v7a
+  - emulator -avd test -no-skin -no-audio -no-window &
+  - android-wait-for-emulator
+  - adb shell input keyevent 82 &
+
+script:
+  - ./gradlew build jacocoTestReport assembleAndroidTest
+  - ./gradlew build connectedCheck
+  - ./gradlew connectedAndroidTest
+
+after_success:
+  - bash <(curl -s https://codecov.io/bash)

+ 5 - 0
CONTRIBUTING.md

@@ -0,0 +1,5 @@
+I actively welcome your pull requests.
+
+1. Fork the repo and create your branch from `develop`.
+2. If you've added code that should be tested, add tests.
+3. New pull request to `develop`.

+ 1 - 0
FishBun/.gitignore

@@ -0,0 +1 @@
+/build

+ 50 - 0
FishBun/build.gradle

@@ -0,0 +1,50 @@
+apply plugin: 'com.android.library'
+apply plugin: 'jacoco-android'
+
+buildscript {
+    repositories {
+        jcenter()
+        mavenCentral()
+    }
+
+    dependencies {
+        classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1'
+        classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3"
+        classpath 'com.dicedmelon.gradle:jacoco-android:0.1.1'
+        classpath 'co.riiid:gradle-github-plugin:0.4.2'
+    }
+}
+
+
+android {
+    compileSdkVersion gradle.compileSdk
+    buildToolsVersion gradle.buildTools
+    lintOptions {
+        abortOnError false
+    }
+    defaultConfig {
+        minSdkVersion gradle.minSdk
+        targetSdkVersion gradle.targetSdk
+        consumerProguardFile('proguard-rules.pro')
+        versionCode gradle.versionCode
+        versionName gradle.versionName
+    }
+}
+
+apply plugin: 'com.github.dcendents.android-maven'
+apply plugin: "com.jfrog.bintray"
+apply plugin: 'co.riiid.gradle'
+
+apply from: '../gradle/release.gradle'
+
+dependencies {
+    compileOnly "com.android.support:appcompat-v7:$rootProject.support_version"
+    compile "com.android.support.constraint:constraint-layout:$rootProject.constraint_version"
+    compile "com.android.support:design:$rootProject.support_version"
+    compile "com.android.support:recyclerview-v7:$rootProject.support_version"
+
+    testImplementation 'junit:junit:4.12'
+    testImplementation 'org.mockito:mockito-core:2.8.9'
+
+    compileOnly "com.squareup.picasso:picasso:$rootProject.picasso_version"
+}

+ 34 - 0
FishBun/proguard-rules.pro

@@ -0,0 +1,34 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in C:\sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+# JSR 305 annotations are for embedding nullability information.
+-dontwarn javax.annotation.**
+
+# A resource is loaded with a relative path so the package of this class must be preserved.
+-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
+
+# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
+-dontwarn org.codehaus.mojo.animal_sniffer.*
+
+# OkHttp platform used only on JVM and when Conscrypt dependency is available.
+-dontwarn okhttp3.internal.platform.ConscryptPlatform
+
+-keep class com.sangcomz.fishbun.bean.** { *; }
+-keep class com.sangcomz.fishbun.util.TouchImageView.** {*;}
+-keep class com.sangcomz.fishbun.util.TouchImageView$State{*;}
+-keep class com.sangcomz.fishbun.util.TouchImageView$OnTouchImageViewListener{*;}
+

+ 28 - 0
FishBun/src/main/AndroidManifest.xml

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.sangcomz.fishbun">
+
+    <application>
+        <provider
+            android:name="com.sangcomz.fishbun.FishBunFileProvider"
+            android:authorities="${applicationId}.provider"
+            android:exported="false"
+            android:grantUriPermissions="true">
+            <meta-data
+                android:name="android.support.FILE_PROVIDER_PATHS"
+                android:resource="@xml/provider_paths" />
+        </provider>
+
+        <activity
+            android:name=".ui.album.AlbumActivity"
+            android:label="@string/album"
+            android:theme="@style/FishBunTheme"/>
+        <activity
+            android:name=".ui.picker.PickerActivity"
+            android:theme="@style/FishBunTheme" />
+        <activity
+            android:name=".ui.detail.DetailActivity"
+            android:theme="@style/DetailViewTheme" />
+    </application>
+
+</manifest>

+ 24 - 0
FishBun/src/main/java/com/sangcomz/fishbun/BaseActivity.java

@@ -0,0 +1,24 @@
+package com.sangcomz.fishbun;
+
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.Window;
+
+import com.sangcomz.fishbun.define.Define;
+import com.sangcomz.fishbun.util.UiUtil;
+
+public abstract class BaseActivity extends AppCompatActivity {
+    protected Define define = new Define();
+    protected UiUtil uiUtil = new UiUtil();
+    protected Fishton fishton;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
+        }
+        super.onCreate(savedInstanceState);
+        fishton = Fishton.getInstance();
+    }
+}

+ 27 - 0
FishBun/src/main/java/com/sangcomz/fishbun/BaseFragment.java

@@ -0,0 +1,27 @@
+package com.sangcomz.fishbun;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.sangcomz.fishbun.define.Define;
+import com.sangcomz.fishbun.util.UiUtil;
+
+/**
+ * Created by sangcomz on 04/06/2017.
+ */
+
+public class BaseFragment extends Fragment {
+
+    protected Define define = new Define();
+    protected UiUtil uiUtil = new UiUtil();
+
+    @Nullable
+    @Override
+    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+        return super.onCreateView(inflater, container, savedInstanceState);
+    }
+}

+ 12 - 0
FishBun/src/main/java/com/sangcomz/fishbun/BaseParams.java

@@ -0,0 +1,12 @@
+package com.sangcomz.fishbun;
+
+/**
+ * Created by sangcomz on 21/05/2017.
+ */
+
+public enum BaseParams {
+    ARRAY_PATHS,
+    INT_MAX_COUNT,
+    INT_MIN_COUNT,
+    BOOLEAN_EXCEPT_GIF,
+}

+ 27 - 0
FishBun/src/main/java/com/sangcomz/fishbun/BaseProperty.java

@@ -0,0 +1,27 @@
+package com.sangcomz.fishbun;
+
+import android.net.Uri;
+
+import java.util.ArrayList;
+
+/**
+ * Created by sangcomz on 13/05/2017.
+ */
+
+interface BaseProperty {
+    FishBunCreator setSelectedImages(ArrayList<Uri> arrayPaths);
+
+    FishBunCreator setPickerCount(int count);
+
+    FishBunCreator setMaxCount(int count);
+
+    FishBunCreator setMinCount(int count);
+
+    FishBunCreator setRequestCode(int RequestCode);
+
+    FishBunCreator setReachLimitAutomaticClose(boolean isAutomaticClose);
+
+    FishBunCreator exceptGif(boolean isExcept);
+
+    void startAlbum();
+}

+ 55 - 0
FishBun/src/main/java/com/sangcomz/fishbun/CustomizationProperty.java

@@ -0,0 +1,55 @@
+package com.sangcomz.fishbun;
+
+import android.graphics.drawable.Drawable;
+
+/**
+ * Created by sangcomz on 13/05/2017.
+ */
+
+interface CustomizationProperty {
+    FishBunCreator setAlbumThumbnailSize(int size);
+
+    FishBunCreator setPickerSpanCount(int spanCount);
+
+    FishBunCreator setActionBarColor(int actionbarColor);
+
+    FishBunCreator setActionBarTitleColor(int actionbarTitleColor);
+
+    FishBunCreator setActionBarColor(int actionbarColor, int statusBarColor);
+
+    FishBunCreator setActionBarColor(int actionbarColor, int statusBarColor, boolean isStatusBarLight);
+
+    FishBunCreator setCamera(boolean isCamera);
+
+    FishBunCreator textOnNothingSelected(String message);
+
+    FishBunCreator textOnImagesSelectionLimitReached(String message);
+
+    FishBunCreator setButtonInAlbumActivity(boolean isButton);
+
+    FishBunCreator setAlbumSpanCount(int portraitSpanCount, int landscapeSpanCount);
+
+    FishBunCreator setAlbumSpanCountOnlyLandscape(int landscapeSpanCount);
+
+    FishBunCreator setAlbumSpanCountOnlPortrait(int portraitSpanCount);
+
+    FishBunCreator setAllViewTitle(String allViewTitle);
+
+    FishBunCreator setActionBarTitle(String actionBarTitle);
+
+    FishBunCreator setHomeAsUpIndicatorDrawable(Drawable icon);
+
+    FishBunCreator setOkButtonDrawable(Drawable icon);
+
+    FishBunCreator setMenuText(String text);
+
+    FishBunCreator setMenuTextColor(int color);
+
+    FishBunCreator setIsUseDetailView(boolean isUse);
+
+    FishBunCreator setIsShowCount(boolean isShow);
+
+    FishBunCreator setSelectCircleStrokeColor(int strokeColor);
+
+    FishBunCreator isStartInAllView(boolean isStartInAllView);
+}

+ 38 - 0
FishBun/src/main/java/com/sangcomz/fishbun/FishBun.java

@@ -0,0 +1,38 @@
+package com.sangcomz.fishbun;
+
+import android.app.Activity;
+import android.support.v4.app.Fragment;
+
+import com.sangcomz.fishbun.adapter.image.ImageAdapter;
+
+import java.lang.ref.WeakReference;
+
+
+public final class FishBun {
+
+    protected WeakReference<Activity> activity = null;
+    protected WeakReference<Fragment> fragment = null;
+
+
+    public static FishBun with(Activity activity) {
+        return new FishBun(activity, null);
+    }
+
+    public static FishBun with(Fragment fragment) {
+        return new FishBun(null, fragment);
+    }
+
+
+    FishBun(Activity activity, Fragment fragment) {
+        this.activity = new WeakReference<>(activity);
+        this.fragment = new WeakReference<>(fragment);
+    }
+
+    public FishBunCreator setImageAdapter(ImageAdapter imageAdapter) {
+        Fishton fishton = Fishton.getInstance();
+        fishton.refresh();
+
+        fishton.imageAdapter = imageAdapter;
+        return new FishBunCreator(this);
+    }
+}

+ 267 - 0
FishBun/src/main/java/com/sangcomz/fishbun/FishBunCreator.java

@@ -0,0 +1,267 @@
+package com.sangcomz.fishbun;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.support.v4.app.Fragment;
+
+import com.sangcomz.fishbun.bean.Album;
+import com.sangcomz.fishbun.define.Define;
+import com.sangcomz.fishbun.ui.album.AlbumActivity;
+import com.sangcomz.fishbun.ui.picker.PickerActivity;
+
+import java.util.ArrayList;
+
+/**
+ * Created by sangcomz on 17/05/2017.
+ */
+
+public final class FishBunCreator implements BaseProperty, CustomizationProperty {
+
+    private FishBun fishBun;
+    private Fishton fishton;
+
+    private int requestCode = 27;
+
+
+    FishBunCreator(FishBun fishBun) {
+        this.fishBun = fishBun;
+        this.fishton = Fishton.getInstance();
+    }
+
+    public FishBunCreator setSelectedImages(ArrayList<Uri> selectedImages) {
+        fishton.selectedImages = selectedImages;
+        return this;
+    }
+
+    public FishBunCreator setAlbumThumbnailSize(int size) {
+        fishton.albumThumbnailSize = size;
+        return this;
+    }
+
+    @Override
+    public FishBunCreator setPickerSpanCount(int spanCount) {
+        if (spanCount <= 0)
+            spanCount = 3;
+
+        fishton.photoSpanCount = spanCount;
+        return this;
+
+    }
+
+    @Deprecated
+    @Override
+    public FishBunCreator setPickerCount(int count) {
+        if (count <= 0)
+            count = 1;
+
+        fishton.maxCount = count;
+
+        return this;
+    }
+
+    @Override
+    public FishBunCreator setMaxCount(int count) {
+        if (count <= 0)
+            count = 1;
+
+        fishton.maxCount = count;
+        return this;
+    }
+
+    @Override
+    public FishBunCreator setMinCount(int count) {
+        if (count <= 0)
+            count = 1;
+        fishton.minCount = count;
+        return this;
+    }
+
+    @Override
+    public FishBunCreator setActionBarColor(int actionbarColor) {
+        fishton.colorActionBar = actionbarColor;
+        return this;
+    }
+
+    @Override
+    public FishBunCreator setActionBarTitleColor(int actionbarTitleColor) {
+        fishton.colorActionBarTitle = actionbarTitleColor;
+        return this;
+    }
+
+    @Override
+    public FishBunCreator setActionBarColor(int actionbarColor, int statusBarColor) {
+        fishton.colorActionBar = actionbarColor;
+        fishton.colorStatusBar = statusBarColor;
+        return this;
+    }
+
+    @Override
+    public FishBunCreator setActionBarColor(int actionbarColor, int statusBarColor, boolean isStatusBarLight) {
+        fishton.colorActionBar = actionbarColor;
+        fishton.colorStatusBar = statusBarColor;
+        fishton.isStatusBarLight = isStatusBarLight;
+        return this;
+    }
+
+    @Override
+    public FishBunCreator setCamera(boolean isCamera) {
+        fishton.isCamera = isCamera;
+        return this;
+    }
+
+    @Override
+    public FishBunCreator setRequestCode(int requestCode) {
+        this.requestCode = requestCode;
+        return this;
+    }
+
+    @Override
+    public FishBunCreator textOnNothingSelected(String message) {
+        fishton.messageNothingSelected = message;
+        return this;
+    }
+
+    @Override
+    public FishBunCreator textOnImagesSelectionLimitReached(String message) {
+        fishton.messageLimitReached = message;
+        return this;
+    }
+
+    @Override
+    public FishBunCreator setButtonInAlbumActivity(boolean isButton) {
+        fishton.isButton = isButton;
+        return this;
+    }
+
+    @Override
+    public FishBunCreator setReachLimitAutomaticClose(boolean isAutomaticClose) {
+        fishton.isAutomaticClose = isAutomaticClose;
+        return this;
+    }
+
+    @Override
+    public FishBunCreator setAlbumSpanCount(int portraitSpanCount, int landscapeSpanCount) {
+        fishton.albumPortraitSpanCount = portraitSpanCount;
+        fishton.albumLandscapeSpanCount = landscapeSpanCount;
+        return this;
+    }
+
+    @Override
+    public FishBunCreator setAlbumSpanCountOnlyLandscape(int landscapeSpanCount) {
+        fishton.albumLandscapeSpanCount = landscapeSpanCount;
+        return this;
+    }
+
+    @Override
+    public FishBunCreator setAlbumSpanCountOnlPortrait(int portraitSpanCount) {
+        fishton.albumPortraitSpanCount = portraitSpanCount;
+        return this;
+    }
+
+    @Override
+    public FishBunCreator setAllViewTitle(String allViewTitle) {
+        fishton.titleAlbumAllView = allViewTitle;
+        return this;
+    }
+
+    @Override
+    public FishBunCreator setActionBarTitle(String actionBarTitle) {
+        fishton.titleActionBar = actionBarTitle;
+        return this;
+    }
+
+    @Override
+    public FishBunCreator setHomeAsUpIndicatorDrawable(Drawable icon) {
+        fishton.drawableHomeAsUpIndicator = icon;
+        return this;
+    }
+
+    @Override
+    public FishBunCreator setOkButtonDrawable(Drawable icon) {
+        fishton.drawableOkButton = icon;
+        return this;
+    }
+
+    @Override
+    public FishBunCreator exceptGif(boolean isExcept) {
+        fishton.isExceptGif = isExcept;
+        return this;
+    }
+
+    @Override
+    public FishBunCreator setMenuText(String text) {
+        fishton.strTextMenu = text;
+        return this;
+    }
+
+    @Override
+    public FishBunCreator setMenuTextColor(int textColor) {
+        fishton.colorTextMenu = textColor;
+        return this;
+    }
+
+    @Override
+    public FishBunCreator setIsUseDetailView(boolean isUse) {
+        fishton.isUseDetailView = isUse;
+        return this;
+    }
+
+    @Override
+    public FishBunCreator setIsShowCount(boolean isShow) {
+        fishton.isShowCount = isShow;
+        return this;
+    }
+
+    @Override
+    public FishBunCreator setSelectCircleStrokeColor(int strokeColor) {
+        fishton.colorSelectCircleStroke = strokeColor;
+        return this;
+    }
+
+    @Override
+    public FishBunCreator isStartInAllView(boolean isStartInAllView) {
+        fishton.isStartInAllView = isStartInAllView;
+        return this;
+    }
+
+    @Override
+    public void startAlbum() {
+        Context context = null;
+        Activity activity = fishBun.activity.get();
+        Fragment fragment = fishBun.fragment.get();
+        if (activity != null)
+            context = activity;
+        else if (fragment != null)
+            context = fragment.getActivity();
+        else
+            try {
+                throw new NullPointerException("Activity or Fragment Null");
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+
+        if (fishton.imageAdapter == null)
+            throw new NullPointerException("ImageAdapter is Null");
+
+        fishton.setDefaultMessage(context);
+        fishton.setMenuTextColor();
+        fishton.setDefaultDimen(context);
+
+
+        if (fishton.isStartInAllView) {
+            Intent i = new Intent(context, PickerActivity.class);
+            i.putExtra(Define.BUNDLE_NAME.ALBUM.name(), new Album(0, fishton.titleAlbumAllView, null, 0));
+            i.putExtra(Define.BUNDLE_NAME.POSITION.name(), 0);
+            if (activity != null) activity.startActivityForResult(i, requestCode);
+            else if (fragment != null) fragment.startActivityForResult(i, requestCode);
+        } else {
+            Intent i = new Intent(context, AlbumActivity.class);
+            if (activity != null) activity.startActivityForResult(i, requestCode);
+            else if (fragment != null) fragment.startActivityForResult(i, requestCode);
+        }
+
+    }
+}

+ 7 - 0
FishBun/src/main/java/com/sangcomz/fishbun/FishBunFileProvider.java

@@ -0,0 +1,7 @@
+package com.sangcomz.fishbun;
+
+import android.support.v4.content.FileProvider;
+
+public class FishBunFileProvider extends FileProvider {
+
+}

+ 158 - 0
FishBun/src/main/java/com/sangcomz/fishbun/Fishton.java

@@ -0,0 +1,158 @@
+package com.sangcomz.fishbun;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+
+import com.sangcomz.fishbun.adapter.image.ImageAdapter;
+
+import java.util.ArrayList;
+
+/**
+ * Created by seokwon.jeong on 04/01/2018.
+ */
+
+public class Fishton {
+    public ImageAdapter imageAdapter;
+    public Uri[] pickerImages;
+
+    //BaseParams
+    public int maxCount;
+    public int minCount;
+    public boolean isExceptGif;
+    public ArrayList<Uri> selectedImages = new ArrayList<>();
+
+
+    //CustomizationParams
+    public int photoSpanCount;
+    public int albumPortraitSpanCount;
+    public int albumLandscapeSpanCount;
+
+    public boolean isAutomaticClose;
+    public boolean isButton;
+
+    public int colorActionBar;
+    public int colorActionBarTitle;
+    public int colorStatusBar;
+
+    public boolean isStatusBarLight;
+    public boolean isCamera;
+
+    public int albumThumbnailSize;
+
+    public String messageNothingSelected;
+    public String messageLimitReached;
+    public String titleAlbumAllView;
+    public String titleActionBar;
+
+    public Drawable drawableHomeAsUpIndicator;
+    public Drawable drawableOkButton;
+
+    public String strTextMenu;
+
+    public int colorTextMenu;
+
+    public boolean isUseDetailView;
+
+    public boolean isShowCount;
+
+    public int colorSelectCircleStroke;
+
+    public boolean isStartInAllView;
+
+
+    private Fishton() {
+        init();
+    }
+
+    public static Fishton getInstance() {
+        return FishtonHolder.INSTANCE;
+    }
+
+    private static class FishtonHolder {
+        public static final Fishton INSTANCE = new Fishton();
+    }
+
+    public void refresh() {
+        init();
+    }
+
+    private void init() {
+        //Adapter
+        imageAdapter = null;
+
+        //BaseParams
+        maxCount = 10;
+        minCount = 1;
+        isExceptGif = true;
+        selectedImages = new ArrayList<>();
+
+        //CustomizationParams
+        photoSpanCount = 3;
+        albumPortraitSpanCount = 1;
+        albumLandscapeSpanCount = 2;
+
+        isAutomaticClose = false;
+        isButton = false;
+
+        colorActionBar = Color.parseColor("#3F51B5");
+        colorActionBarTitle = Color.parseColor("#ffffff");
+        colorStatusBar = Color.parseColor("#303F9F");
+
+        isStatusBarLight = false;
+        isCamera = false;
+
+        albumThumbnailSize = Integer.MAX_VALUE;
+
+        messageNothingSelected = null;
+        messageLimitReached = null;
+        titleAlbumAllView = null;
+        titleActionBar = null;
+
+        drawableHomeAsUpIndicator = null;
+        drawableOkButton = null;
+
+        strTextMenu = null;
+
+
+        colorTextMenu = Integer.MAX_VALUE;
+
+        isUseDetailView = true;
+        isShowCount = true;
+
+        colorSelectCircleStroke = Color.parseColor("#c1ffffff");
+        isStartInAllView = false;
+    }
+
+    void setDefaultMessage(Context context) {
+        if (messageNothingSelected == null)
+            messageNothingSelected = context.getResources().getString(R.string.msg_no_selected);
+
+        if (messageLimitReached == null)
+            messageLimitReached = context.getResources().getString(R.string.msg_full_image);
+
+        if (titleAlbumAllView == null)
+            titleAlbumAllView = context.getResources().getString(R.string.str_all_view);
+
+        if (titleActionBar == null)
+            titleActionBar = context.getResources().getString(R.string.album);
+    }
+
+    void setMenuTextColor() {
+        if (drawableOkButton != null
+                || strTextMenu == null
+                || colorTextMenu != Integer.MAX_VALUE)
+            return;
+
+        if (isStatusBarLight)
+            colorTextMenu = Color.BLACK;
+        else
+            colorTextMenu = Color.WHITE;
+    }
+
+    void setDefaultDimen(Context context) {
+        if (albumThumbnailSize == Integer.MAX_VALUE)
+            albumThumbnailSize = (int) context.getResources().getDimension(R.dimen.album_thum_size);
+    }
+}

+ 13 - 0
FishBun/src/main/java/com/sangcomz/fishbun/adapter/image/ImageAdapter.java

@@ -0,0 +1,13 @@
+package com.sangcomz.fishbun.adapter.image;
+
+import android.net.Uri;
+import android.widget.ImageView;
+
+/**
+ * Created by sangcomz on 23/07/2017.
+ */
+
+public interface ImageAdapter {
+    void loadImage(ImageView target, Uri loadUrl);
+    void loadDetailImage(ImageView target, Uri loadUrl);
+}

+ 32 - 0
FishBun/src/main/java/com/sangcomz/fishbun/adapter/image/impl/PicassoAdapter.java

@@ -0,0 +1,32 @@
+package com.sangcomz.fishbun.adapter.image.impl;
+
+import android.net.Uri;
+import android.widget.ImageView;
+
+import com.sangcomz.fishbun.adapter.image.ImageAdapter;
+import com.squareup.picasso.Picasso;
+
+/**
+ * Created by sangcomz on 23/07/2017.
+ */
+
+public class PicassoAdapter implements ImageAdapter {
+    @Override
+    public void loadImage(ImageView target, Uri loadUrl) {
+        Picasso
+                .get()
+                .load(loadUrl)
+                .fit()
+                .centerCrop()
+                .into(target);
+    }
+
+    @Override
+    public void loadDetailImage(ImageView target, Uri loadUrl) {
+        Picasso.get()
+                .load(loadUrl)
+                .fit()
+                .centerInside()
+                .into(target);
+    }
+}

+ 106 - 0
FishBun/src/main/java/com/sangcomz/fishbun/adapter/view/AlbumListAdapter.java

@@ -0,0 +1,106 @@
+package com.sangcomz.fishbun.adapter.view;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.support.annotation.NonNull;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.sangcomz.fishbun.Fishton;
+import com.sangcomz.fishbun.R;
+import com.sangcomz.fishbun.bean.Album;
+import com.sangcomz.fishbun.define.Define;
+import com.sangcomz.fishbun.ui.picker.PickerActivity;
+
+import java.util.List;
+
+
+public class AlbumListAdapter
+        extends RecyclerView.Adapter<AlbumListAdapter.ViewHolder> {
+
+    private Fishton fishton;
+    private List<Album> albumList;
+
+
+    public AlbumListAdapter() {
+        fishton = Fishton.getInstance();
+    }
+
+    public void setAlbumList(List<Album> albumList) {
+        this.albumList = albumList;
+
+    }
+
+    @Override
+    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        View view = LayoutInflater.from(parent.getContext())
+                .inflate(R.layout.album_item, parent, false);
+        return new ViewHolder(view, fishton.albumThumbnailSize);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull final ViewHolder holder, final int position) {
+        holder.imgAlbumThumb.setImageDrawable(null);
+
+        Uri loadUrl = Uri.parse(albumList.get(position).thumbnailPath);
+
+        if (holder.imgAlbumThumb != null && loadUrl != null)
+            Fishton.getInstance().imageAdapter
+                    .loadImage(holder.imgAlbumThumb, loadUrl);
+
+        holder.view.setTag(albumList.get(position));
+        Album a = (Album) holder.view.getTag();
+        holder.txtAlbumName.setText(albumList.get(position).bucketName);
+        holder.txtAlbumCount.setText(String.valueOf(a.counter));
+
+
+        holder.view.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Album a = (Album) v.getTag();
+                Context context = holder.view.getContext();
+                Intent i = new Intent(context, PickerActivity.class);
+                i.putExtra(Define.BUNDLE_NAME.ALBUM.name(), a);
+                i.putExtra(Define.BUNDLE_NAME.POSITION.name(), position);
+                ((Activity) context).startActivityForResult(i, new Define().ENTER_ALBUM_REQUEST_CODE);
+            }
+        });
+    }
+
+    @Override
+    public int getItemCount() {
+        return albumList.size();
+    }
+
+
+    public List<Album> getAlbumList() {
+        return albumList;
+    }
+
+
+    public static class ViewHolder extends RecyclerView.ViewHolder {
+        private View view;
+        private ImageView imgAlbumThumb;
+        private TextView txtAlbumName;
+        private TextView txtAlbumCount;
+
+        public ViewHolder(View view, int albumSize) {
+            super(view);
+            this.view = view;
+            imgAlbumThumb = view.findViewById(R.id.img_album_thumb);
+            imgAlbumThumb.setLayoutParams(new LinearLayout.LayoutParams(albumSize, albumSize));
+
+            txtAlbumName = view.findViewById(R.id.txt_album_name);
+            txtAlbumCount = view.findViewById(R.id.txt_album_count);
+        }
+    }
+}
+
+

+ 67 - 0
FishBun/src/main/java/com/sangcomz/fishbun/adapter/view/DetailViewPagerAdapter.java

@@ -0,0 +1,67 @@
+package com.sangcomz.fishbun.adapter.view;
+
+import android.net.Uri;
+import android.support.annotation.NonNull;
+import android.support.constraint.ConstraintLayout;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.sangcomz.fishbun.Fishton;
+import com.sangcomz.fishbun.R;
+import com.sangcomz.fishbun.util.TouchImageView;
+
+/**
+ * Created by sangcomz on 15/06/2017.
+ */
+
+public class DetailViewPagerAdapter extends PagerAdapter {
+
+    private Fishton fishton;
+    private LayoutInflater inflater;
+    private Uri[] images;
+
+    public DetailViewPagerAdapter(LayoutInflater inflater, Uri[] images) {
+        this.inflater = inflater;
+        this.images = images;
+        fishton = Fishton.getInstance();
+    }
+
+
+    @NonNull
+    @Override
+    public Object instantiateItem(@NonNull ViewGroup container, int position) {
+
+        View itemView = inflater.inflate(R.layout.detail_item, container, false);
+        container.addView(itemView);
+
+        TouchImageView imageView = itemView.findViewById(R.id.img_detail_image);
+
+        if (imageView != null
+                && images[position] != null)
+            fishton
+                    .imageAdapter
+                    .loadDetailImage(imageView, images[position]);
+
+        return itemView;
+    }
+
+    @Override
+    public int getCount() {
+        return images.length;
+    }
+
+    @Override
+    public void destroyItem(ViewGroup container, int position, Object object) {
+        if (container instanceof ViewPager) {
+            container.removeView((ConstraintLayout) object);
+        }
+    }
+
+    @Override
+    public boolean isViewFromObject(View view, Object object) {
+        return view == object;
+    }
+}

+ 274 - 0
FishBun/src/main/java/com/sangcomz/fishbun/adapter/view/PickerGridAdapter.java

@@ -0,0 +1,274 @@
+package com.sangcomz.fishbun.adapter.view;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.support.design.widget.Snackbar;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.view.ViewCompat;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+
+import com.sangcomz.fishbun.Fishton;
+import com.sangcomz.fishbun.R;
+import com.sangcomz.fishbun.define.Define;
+import com.sangcomz.fishbun.ui.detail.DetailActivity;
+import com.sangcomz.fishbun.ui.picker.PickerActivity;
+import com.sangcomz.fishbun.ui.picker.PickerController;
+import com.sangcomz.fishbun.util.RadioWithTextButton;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+
+public class PickerGridAdapter
+        extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+    private static final int TYPE_HEADER = Integer.MIN_VALUE;
+
+    private Fishton fishton;
+    private PickerController pickerController;
+    private OnPhotoActionListener actionListener;
+
+
+    private String saveDir;
+
+
+    public PickerGridAdapter(PickerController pickerController,
+                             String saveDir) {
+        this.pickerController = pickerController;
+        this.saveDir = saveDir;
+        this.fishton = Fishton.getInstance();
+    }
+
+
+    @Override
+    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        View view;
+        if (viewType == TYPE_HEADER) {
+            view = LayoutInflater.from(parent.getContext()).inflate(R.layout.header_item, parent, false);
+            return new ViewHolderHeader(view);
+        }
+
+        view = LayoutInflater.from(parent.getContext())
+                .inflate(R.layout.thumb_item, parent, false);
+        return new ViewHolderImage(view);
+    }
+
+    @Override
+    public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
+
+        if (holder instanceof ViewHolderHeader) {
+            final ViewHolderHeader vh = (ViewHolderHeader) holder;
+            vh.header.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    pickerController.takePicture((Activity) vh.header.getContext(), saveDir);
+                }
+            });
+        }
+
+        if (holder instanceof ViewHolderImage) {
+            final int imagePos;
+            if (fishton.isCamera) imagePos = position - 1;
+            else imagePos = position;
+
+            final ViewHolderImage vh = (ViewHolderImage) holder;
+            final Uri image = fishton.pickerImages[imagePos];
+            final Context context = vh.item.getContext();
+            vh.item.setTag(image);
+            vh.btnThumbCount.unselect();
+            vh.btnThumbCount.setCircleColor(fishton.colorActionBar);
+            vh.btnThumbCount.setTextColor(fishton.colorActionBarTitle);
+            vh.btnThumbCount.setStrokeColor(fishton.colorSelectCircleStroke);
+
+            initState(fishton.selectedImages.indexOf(image), vh);
+            if (image != null
+                    && vh.imgThumbImage != null)
+                Fishton.getInstance().imageAdapter
+                        .loadImage(vh.imgThumbImage, image);
+
+
+            vh.btnThumbCount.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    onCheckStateChange(vh.item, image);
+                }
+            });
+
+            vh.imgThumbImage.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    if (fishton.isUseDetailView) {
+                        if (context instanceof PickerActivity) {
+                            PickerActivity activity = (PickerActivity) context;
+                            Intent i = new Intent(activity, DetailActivity.class);
+                            i.putExtra(Define.BUNDLE_NAME.POSITION.name(), imagePos);
+                            activity.startActivityForResult(i, new Define().ENTER_DETAIL_REQUEST_CODE);
+                        }
+                    } else
+                        onCheckStateChange(vh.item, image);
+
+                }
+            });
+        }
+    }
+
+    private void initState(int selectedIndex, ViewHolderImage vh) {
+        if (selectedIndex != -1) {
+            animScale(vh.imgThumbImage, true, false);
+            updateRadioButton(vh.btnThumbCount, String.valueOf(selectedIndex + 1));
+        } else {
+            animScale(vh.imgThumbImage, false, false);
+        }
+    }
+
+    private void onCheckStateChange(View v, Uri image) {
+        ArrayList<Uri> pickedImages = fishton.selectedImages;
+        boolean isContained = pickedImages.contains(image);
+        if (fishton.maxCount == pickedImages.size()
+                && !isContained) {
+            Snackbar.make(v, fishton.messageLimitReached, Snackbar.LENGTH_SHORT).show();
+            return;
+        }
+        ImageView imgThumbImage = v.findViewById(R.id.img_thumb_image);
+        RadioWithTextButton btnThumbCount = v.findViewById(R.id.btn_thumb_count);
+        if (isContained) {
+            pickedImages.remove(image);
+            btnThumbCount.unselect();
+            animScale(imgThumbImage, false, true);
+        } else {
+            animScale(imgThumbImage, true, true);
+            pickedImages.add(image);
+            if (fishton.isAutomaticClose
+                    && fishton.maxCount == pickedImages.size()) {
+                pickerController.finishActivity();
+            }
+            updateRadioButton(btnThumbCount, String.valueOf(pickedImages.size()));
+        }
+        pickerController.setToolbarTitle(pickedImages.size());
+    }
+
+    public void updateRadioButton(RadioWithTextButton v, String text) {
+        if (fishton.maxCount == 1)
+            v.setDrawable(ContextCompat.getDrawable(v.getContext(), R.drawable.ic_done_white_24dp));
+        else
+            v.setText(text);
+    }
+
+    public void updateRadioButton(ImageView imageView, RadioWithTextButton v, String text, boolean isSelected) {
+        if (isSelected) {
+            animScale(imageView, isSelected, false);
+            if (fishton.maxCount == 1)
+                v.setDrawable(ContextCompat.getDrawable(v.getContext(), R.drawable.ic_done_white_24dp));
+            else
+                v.setText(text);
+        } else {
+            v.unselect();
+        }
+
+    }
+
+
+    private void animScale(View view,
+                           final boolean isSelected,
+                           final boolean isAnimation) {
+        int duration = 200;
+        if (!isAnimation) duration = 0;
+        float toScale;
+        if (isSelected)
+            toScale = .8f;
+        else
+            toScale = 1.0f;
+
+        ViewCompat.animate(view)
+                .setDuration(duration)
+                .withStartAction(new Runnable() {
+                    @Override
+                    public void run() {
+
+                    }
+                })
+                .scaleX(toScale)
+                .scaleY(toScale)
+                .withEndAction(new Runnable() {
+                    @Override
+                    public void run() {
+                        if (isAnimation && !isSelected) actionListener.onDeselect();
+                    }
+                })
+                .start();
+
+    }
+
+    @Override
+    public int getItemCount() {
+        int count;
+        if (fishton.pickerImages == null) count = 0;
+        else count = fishton.pickerImages.length;
+
+        if (fishton.isCamera)
+            return count + 1;
+
+        if (fishton.pickerImages == null) return 0;
+        else return count;
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        if (position == 0 && fishton.isCamera) {
+            return TYPE_HEADER;
+        }
+        return super.getItemViewType(position);
+    }
+
+
+    public void addImage(Uri path) {
+        ArrayList<Uri> al = new ArrayList<>();
+        Collections.addAll(al, fishton.pickerImages);
+        al.add(0, path);
+        fishton.pickerImages = al.toArray(new Uri[al.size()]);
+
+        notifyDataSetChanged();
+
+        pickerController.setAddImagePath(path);
+    }
+
+    public void setActionListener(OnPhotoActionListener actionListener) {
+        this.actionListener = actionListener;
+    }
+
+    public interface OnPhotoActionListener {
+        void onDeselect();
+    }
+
+    public class ViewHolderImage extends RecyclerView.ViewHolder {
+
+
+        View item;
+        ImageView imgThumbImage;
+        RadioWithTextButton btnThumbCount;
+
+        public ViewHolderImage(View view) {
+            super(view);
+            item = view;
+            imgThumbImage = view.findViewById(R.id.img_thumb_image);
+            btnThumbCount = view.findViewById(R.id.btn_thumb_count);
+        }
+    }
+
+    public class ViewHolderHeader extends RecyclerView.ViewHolder {
+
+
+        RelativeLayout header;
+
+        public ViewHolderHeader(View view) {
+            super(view);
+            header = itemView.findViewById(R.id.rel_header_area);
+        }
+    }
+}

+ 52 - 0
FishBun/src/main/java/com/sangcomz/fishbun/bean/Album.java

@@ -0,0 +1,52 @@
+package com.sangcomz.fishbun.bean;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class Album implements Parcelable {
+
+    final public long bucketId;
+    final public String bucketName;
+    public int counter;
+    public String thumbnailPath;
+
+
+    public Album(long bucketId, String bucketName, String thumbnailPath, int counter) {
+        this.bucketId = bucketId;
+        this.bucketName = bucketName;
+        this.counter = counter;
+        this.thumbnailPath = thumbnailPath;
+    }
+
+    protected Album(Parcel in) {
+        bucketId = in.readLong();
+        bucketName = in.readString();
+        counter = in.readInt();
+        thumbnailPath = in.readString();
+    }
+
+    public static final Creator<Album> CREATOR = new Creator<Album>() {
+        @Override
+        public Album createFromParcel(Parcel in) {
+            return new Album(in);
+        }
+
+        @Override
+        public Album[] newArray(int size) {
+            return new Album[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int i) {
+        parcel.writeLong(bucketId);
+        parcel.writeString(bucketName);
+        parcel.writeInt(counter);
+        parcel.writeString(thumbnailPath);
+    }
+}

+ 26 - 0
FishBun/src/main/java/com/sangcomz/fishbun/define/Define.java

@@ -0,0 +1,26 @@
+package com.sangcomz.fishbun.define;
+
+public class Define {
+
+    public static final int ALBUM_REQUEST_CODE = 27;
+    public final int PERMISSION_STORAGE = 28;
+    public final int TRANS_IMAGES_RESULT_CODE = 29;
+    public final int TAKE_A_PICK_REQUEST_CODE = 128;
+    public final int ENTER_ALBUM_REQUEST_CODE = 129;
+    public final int ENTER_DETAIL_REQUEST_CODE = 130;
+
+    public final static String INTENT_PATH = "intent_path";
+    public final String INTENT_ADD_PATH = "intent_add_path";
+    public final String INTENT_POSITION = "intent_position";
+
+    public final String SAVE_INSTANCE_ALBUM_LIST = "instance_album_list";
+    public final String SAVE_INSTANCE_ALBUM_THUMB_LIST = "instance_album_thumb_list";
+
+    public final String SAVE_INSTANCE_NEW_IMAGES = "instance_new_images";
+    public final String SAVE_INSTANCE_SAVED_IMAGE = "instance_saved_image";
+
+    public enum BUNDLE_NAME {
+        POSITION,
+        ALBUM,
+    }
+}

+ 65 - 0
FishBun/src/main/java/com/sangcomz/fishbun/permission/PermissionCheck.java

@@ -0,0 +1,65 @@
+package com.sangcomz.fishbun.permission;
+
+import android.Manifest;
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+import android.widget.Toast;
+
+import com.sangcomz.fishbun.R;
+import com.sangcomz.fishbun.define.Define;
+
+
+/**
+ * Created by sangc on 2015-10-12.
+ */
+public class PermissionCheck {
+    private Context context;
+
+    public PermissionCheck(Context context) {
+        this.context = context;
+    }
+
+
+    @TargetApi(Build.VERSION_CODES.M)
+    public boolean CheckStoragePermission() {
+        Define define = new Define();
+        int permissionCheckRead = ContextCompat.checkSelfPermission(context,
+                Manifest.permission.READ_EXTERNAL_STORAGE);
+        int permissionCheckWrite = ContextCompat.checkSelfPermission(context,
+                Manifest.permission.WRITE_EXTERNAL_STORAGE);
+        if (permissionCheckRead != PackageManager.PERMISSION_GRANTED || permissionCheckWrite != PackageManager.PERMISSION_GRANTED) {
+            if (ActivityCompat.shouldShowRequestPermissionRationale((Activity) context,
+                    Manifest.permission.READ_EXTERNAL_STORAGE)) {
+                // Show an expanation to the user *asynchronously* -- don't block
+                // this thread waiting for the user's response! After the user
+                // sees the explanation, try again to request the permission.
+                ActivityCompat.requestPermissions((Activity) context,
+                        new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE},
+                        define.PERMISSION_STORAGE);
+            } else {
+                // No explanation needed, we can request the permission.
+                ActivityCompat.requestPermissions((Activity) context,
+                        new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE},
+                        define.PERMISSION_STORAGE);
+
+                // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
+                // app-defined int constant. The callback method gets the
+                // result of the request.
+            }
+            return false;
+        } else
+            return true;
+    }
+
+
+    public void showPermissionDialog() {
+        Toast.makeText(context, R.string.msg_permission, Toast.LENGTH_SHORT).show();
+    }
+
+
+}

+ 295 - 0
FishBun/src/main/java/com/sangcomz/fishbun/ui/album/AlbumActivity.java

@@ -0,0 +1,295 @@
+package com.sangcomz.fishbun.ui.album;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.support.annotation.NonNull;
+import android.support.design.widget.Snackbar;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.Toolbar;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.sangcomz.fishbun.BaseActivity;
+import com.sangcomz.fishbun.R;
+import com.sangcomz.fishbun.adapter.view.AlbumListAdapter;
+import com.sangcomz.fishbun.bean.Album;
+import com.sangcomz.fishbun.define.Define;
+import com.sangcomz.fishbun.permission.PermissionCheck;
+import com.sangcomz.fishbun.util.ScanListener;
+import com.sangcomz.fishbun.util.SingleMediaScanner;
+import com.sangcomz.fishbun.util.TextDrawable;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class AlbumActivity extends BaseActivity {
+    private AlbumController albumController;
+    private ArrayList<Album> albumList = new ArrayList<>();
+
+    private RecyclerView recyclerAlbumList;
+    private RelativeLayout relAlbumEmpty;
+
+    private AlbumListAdapter adapter;
+    private TextView progressAlbumText;
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        if (adapter != null) {
+            outState.putParcelableArrayList(define.SAVE_INSTANCE_ALBUM_LIST, (ArrayList<? extends Parcelable>) adapter.getAlbumList());
+        }
+        super.onSaveInstanceState(outState);
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle outState) {
+        // Always call the superclass so it can restore the view hierarchy
+        super.onRestoreInstanceState(outState);
+        // Restore state members from saved instance
+        List<Album> albumList = outState.getParcelableArrayList(define.SAVE_INSTANCE_ALBUM_LIST);
+        List<Uri> thumbList = outState.getParcelableArrayList(define.SAVE_INSTANCE_ALBUM_THUMB_LIST);
+
+        if (albumList != null && thumbList != null && fishton.selectedImages != null) {
+            adapter = new AlbumListAdapter();
+            adapter.setAlbumList(albumList);
+        }
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_photo_album);
+        initView();
+        initController();
+        if (albumController.checkPermission())
+            albumController.getAlbumList(fishton.titleAlbumAllView, fishton.isExceptGif);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if (recyclerAlbumList != null &&
+                recyclerAlbumList.getLayoutManager() != null) {
+            if (uiUtil.isLandscape(this))
+                ((GridLayoutManager) recyclerAlbumList.getLayoutManager())
+                        .setSpanCount(fishton.albumLandscapeSpanCount);
+            else
+                ((GridLayoutManager) recyclerAlbumList.getLayoutManager())
+                        .setSpanCount(fishton.albumPortraitSpanCount);
+        }
+    }
+
+    private void initView() {
+        LinearLayout linearAlbumCamera = findViewById(R.id.lin_album_camera);
+        linearAlbumCamera.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                albumController.takePicture(AlbumActivity.this, albumController.getPathDir());
+            }
+        });
+        initToolBar();
+    }
+
+
+    private void initRecyclerView() {
+        recyclerAlbumList = findViewById(R.id.recycler_album_list);
+
+        GridLayoutManager layoutManager;
+        if (uiUtil.isLandscape(this))
+            layoutManager = new GridLayoutManager(this, fishton.albumLandscapeSpanCount);
+        else
+            layoutManager = new GridLayoutManager(this, fishton.albumPortraitSpanCount);
+
+        if (recyclerAlbumList != null) {
+            recyclerAlbumList.setLayoutManager(layoutManager);
+        }
+    }
+
+    private void initToolBar() {
+        Toolbar toolbar = findViewById(R.id.toolbar_album_bar);
+        relAlbumEmpty = findViewById(R.id.rel_album_empty);
+        progressAlbumText = findViewById(R.id.txt_album_msg);
+        progressAlbumText.setText(R.string.msg_loading_image);
+
+        setSupportActionBar(toolbar);
+
+        toolbar.setBackgroundColor(fishton.colorActionBar);
+        toolbar.setTitleTextColor(fishton.colorActionBarTitle);
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            uiUtil.setStatusBarColor(this, fishton.colorStatusBar);
+        }
+        if (getSupportActionBar() != null) {
+            getSupportActionBar().setTitle(fishton.titleActionBar);
+            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+            if (fishton.drawableHomeAsUpIndicator != null)
+                getSupportActionBar().setHomeAsUpIndicator(fishton.drawableHomeAsUpIndicator);
+        }
+
+        if (fishton.isStatusBarLight
+                && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            toolbar.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+        }
+
+    }
+
+    private void initController() {
+        albumController = new AlbumController(this);
+    }
+
+    private void setAlbumListAdapter() {
+        if (adapter == null) {
+            adapter = new AlbumListAdapter();
+        }
+        adapter.setAlbumList(albumList);
+        recyclerAlbumList.setAdapter(adapter);
+        adapter.notifyDataSetChanged();
+        changeToolbarTitle();
+    }
+
+    protected void setAlbumList(ArrayList<Album> albumList) {
+        this.albumList = albumList;
+        if (albumList.size() > 0) {
+            relAlbumEmpty.setVisibility(View.GONE);
+            initRecyclerView();
+            setAlbumListAdapter();
+        } else {
+            relAlbumEmpty.setVisibility(View.VISIBLE);
+            progressAlbumText.setText(R.string.msg_no_image);
+        }
+    }
+
+    private void refreshList(int position, ArrayList<Uri> imagePath) {
+        if (imagePath.size() > 0) {
+            if (position == 0) {
+                albumController.getAlbumList(fishton.titleAlbumAllView, fishton.isExceptGif);
+            } else {
+                albumList.get(0).counter += imagePath.size();
+                albumList.get(position).counter += imagePath.size();
+
+                albumList.get(0).thumbnailPath = imagePath.get(imagePath.size() - 1).toString();
+                albumList.get(position).thumbnailPath = imagePath.get(imagePath.size() - 1).toString();
+
+                adapter.notifyItemChanged(0);
+                adapter.notifyItemChanged(position);
+            }
+        }
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        // Inflate the menu; this adds items to the action bar if it is present.
+        if (fishton.isButton) {
+            getMenuInflater().inflate(R.menu.menu_photo_album, menu);
+            MenuItem item = menu.findItem(R.id.action_ok);
+            if (fishton.drawableOkButton != null) {
+                item.setIcon(fishton.drawableOkButton);
+            } else if (fishton.strTextMenu != null) {
+                if (fishton.colorTextMenu != Integer.MAX_VALUE) {
+                    item.setIcon(new TextDrawable(getResources(), fishton.strTextMenu, fishton.colorTextMenu));
+                } else {
+                    item.setTitle(fishton.strTextMenu);
+                    item.setIcon(null);
+                }
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        int id = item.getItemId();
+        if (id == android.R.id.home) {
+            finish();
+        } else if (id == R.id.action_ok) {
+            if (adapter != null) {
+                if (fishton.selectedImages.size() < fishton.minCount) {
+                    Snackbar.make(recyclerAlbumList, fishton.messageNothingSelected, Snackbar.LENGTH_SHORT).show();
+                } else {
+                    finishActivity();
+                }
+            }
+
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    public void changeToolbarTitle() {
+        if (adapter == null) return;
+        int total = fishton.selectedImages.size();
+
+        if (getSupportActionBar() != null) {
+            if (fishton.maxCount == 1 || !fishton.isShowCount)
+                getSupportActionBar().setTitle(fishton.titleActionBar);
+            else
+                getSupportActionBar().setTitle(fishton.titleActionBar + "(" + String.valueOf(total) + "/" + fishton.maxCount + ")");
+        }
+    }
+
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        if (requestCode == define.ENTER_ALBUM_REQUEST_CODE) {
+            if (resultCode == RESULT_OK) {
+                finishActivity();
+            } else if (resultCode == define.TRANS_IMAGES_RESULT_CODE) {
+//                ArrayList<Uri> path = data.getParcelableArrayListExtra(Define.INTENT_PATH);
+                ArrayList<Uri> addPath = data.getParcelableArrayListExtra(define.INTENT_ADD_PATH);
+                int position = data.getIntExtra(define.INTENT_POSITION, -1);
+                refreshList(position, addPath);
+                changeToolbarTitle();
+            }
+        } else if (requestCode == define.TAKE_A_PICK_REQUEST_CODE) {
+            if (resultCode == RESULT_OK) {
+                new SingleMediaScanner(this, new File(albumController.getSavePath()), new ScanListener() {
+                    @Override
+                    protected void onScanCompleted() {
+                        albumController.getAlbumList(fishton.titleAlbumAllView, fishton.isExceptGif);
+                    }
+                });
+            } else {
+                new File(albumController.getSavePath()).delete();
+            }
+            changeToolbarTitle();
+        }
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode,
+                                           @NonNull String permissions[], @NonNull int[] grantResults) {
+
+
+        switch (requestCode) {
+            case 28: {
+                if (grantResults.length > 0) {
+                    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                        // permission was granted, yay!
+                        albumController.getAlbumList(fishton.titleAlbumAllView, fishton.isExceptGif);
+                    } else {
+                        new PermissionCheck(this).showPermissionDialog();
+                        finish();
+                    }
+                }
+            }
+        }
+    }
+
+
+    private void finishActivity() {
+        Intent i = new Intent();
+        i.putParcelableArrayListExtra(Define.INTENT_PATH, fishton.selectedImages);
+        setResult(RESULT_OK, i);
+        finish();
+    }
+}

+ 144 - 0
FishBun/src/main/java/com/sangcomz/fishbun/ui/album/AlbumController.java

@@ -0,0 +1,144 @@
+package com.sangcomz.fishbun.ui.album;
+
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Environment;
+import android.provider.MediaStore;
+
+import com.sangcomz.fishbun.bean.Album;
+import com.sangcomz.fishbun.permission.PermissionCheck;
+import com.sangcomz.fishbun.util.CameraUtil;
+import com.sangcomz.fishbun.util.RegexUtil;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+class AlbumController {
+
+    private AlbumActivity albumActivity;
+    private ContentResolver resolver;
+    private CameraUtil cameraUtil = new CameraUtil();
+
+
+    AlbumController(AlbumActivity albumActivity) {
+        this.albumActivity = albumActivity;
+        this.resolver = albumActivity.getContentResolver();
+    }
+
+
+    boolean checkPermission() {
+        PermissionCheck permissionCheck = new PermissionCheck(albumActivity);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            if (permissionCheck.CheckStoragePermission())
+                return true;
+        } else
+            return true;
+        return false;
+    }
+
+    void getAlbumList(String allViewTitle,
+                      Boolean exceptGif) {
+        new LoadAlbumList(allViewTitle, exceptGif).execute();
+    }
+
+    private class LoadAlbumList extends AsyncTask<Void, Void, ArrayList<Album>> {
+
+        String allViewTitle;
+        Boolean exceptGif;
+
+        LoadAlbumList(String allViewTitle,
+                      Boolean exceptGif) {
+            this.allViewTitle = allViewTitle;
+            this.exceptGif = exceptGif;
+
+        }
+
+        @Override
+        protected ArrayList<Album> doInBackground(Void... params) {
+            HashMap<Long, Album> albumHashMap = new HashMap<>();
+            final String orderBy = MediaStore.Images.Media._ID + " DESC";
+            String[] projection = new String[]{
+                    MediaStore.Images.Media.DATA,
+                    MediaStore.Images.Media._ID,
+                    MediaStore.Images.Media.BUCKET_DISPLAY_NAME,
+                    MediaStore.Images.Media.BUCKET_ID};
+
+            Cursor c = resolver.query(
+                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection,
+                    null, null, orderBy);
+
+            int totalCounter = 0;
+            if (c != null) {
+                int bucketData = c
+                        .getColumnIndex(MediaStore.Images.Media.DATA);
+                int bucketColumn = c
+                        .getColumnIndex(MediaStore.Images.Media.BUCKET_DISPLAY_NAME);
+                int bucketColumnId = c
+                        .getColumnIndex(MediaStore.Images.Media.BUCKET_ID);
+
+                albumHashMap.put((long) 0, new Album(0, allViewTitle, null, 0));
+
+                RegexUtil regexUtil = new RegexUtil();
+                while (c.moveToNext()) {
+                    if (exceptGif && regexUtil.checkGif(c.getString(bucketData))) continue;
+                    totalCounter++;
+                    long bucketId = c.getInt(bucketColumnId);
+                    Album album = albumHashMap.get(bucketId);
+                    if (album == null) {
+                        int imgId = c.getInt(c.getColumnIndex(MediaStore.MediaColumns._ID));
+                        Uri path = Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "" + imgId);
+                        albumHashMap.put(bucketId,
+                                new Album(bucketId,
+                                        c.getString(bucketColumn),
+                                        path.toString(), 1));
+                        if (albumHashMap.get((long) 0).thumbnailPath == null)
+                            albumHashMap.get((long) 0).thumbnailPath = path.toString();
+                    } else {
+                        album.counter++;
+                    }
+                }
+                Album allAlbum = albumHashMap.get((long) 0);
+                if (allAlbum != null) {
+                    allAlbum.counter = totalCounter;
+                }
+                c.close();
+            }
+
+            if (totalCounter == 0)
+                albumHashMap.clear();
+
+            ArrayList<Album> albumList = new ArrayList<>();
+            for (Album album : albumHashMap.values()) {
+                if (album.bucketId == 0)
+                    albumList.add(0, album);
+                else
+                    albumList.add(album);
+            }
+            return albumList;
+        }
+
+        @Override
+        protected void onPostExecute(ArrayList<Album> albumList) {
+            super.onPostExecute(albumList);
+            albumActivity.setAlbumList(albumList);
+        }
+    }
+
+    void takePicture(Activity activity, String saveDir) {
+        cameraUtil.takePicture(activity, saveDir);
+    }
+
+    String getSavePath() {
+        return cameraUtil.getSavePath();
+    }
+
+
+    String getPathDir() {
+        return Environment.getExternalStoragePublicDirectory(
+                Environment.DIRECTORY_DCIM + "/Camera").getAbsolutePath();
+    }
+}

+ 161 - 0
FishBun/src/main/java/com/sangcomz/fishbun/ui/detail/DetailActivity.java

@@ -0,0 +1,161 @@
+package com.sangcomz.fishbun.ui.detail;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.design.widget.Snackbar;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.view.ViewPager;
+import android.view.View;
+import android.view.Window;
+import android.widget.ImageButton;
+import android.widget.Toast;
+
+import com.sangcomz.fishbun.BaseActivity;
+import com.sangcomz.fishbun.R;
+import com.sangcomz.fishbun.adapter.view.DetailViewPagerAdapter;
+import com.sangcomz.fishbun.define.Define;
+import com.sangcomz.fishbun.util.RadioWithTextButton;
+
+public class DetailActivity extends BaseActivity implements View.OnClickListener, ViewPager.OnPageChangeListener {
+    private static final String TAG = "DetailActivity";
+
+    private DetailController controller;
+    private int initPosition;
+    private RadioWithTextButton btnDetailCount;
+    private ViewPager vpDetailPager;
+    private ImageButton btnDetailBack;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
+        }
+        setContentView(R.layout.activity_detail_actiivy);
+        initController();
+        initValue();
+        initView();
+        initAdapter();
+        initToolBar();
+    }
+
+    private void initController() {
+        controller = new DetailController(this);
+    }
+
+    private void initView() {
+        btnDetailCount = findViewById(R.id.btn_detail_count);
+        vpDetailPager = findViewById(R.id.vp_detail_pager);
+        btnDetailBack = findViewById(R.id.btn_detail_back);
+        btnDetailCount.unselect();
+        btnDetailCount.setCircleColor(fishton.colorActionBar);
+        btnDetailCount.setTextColor(fishton.colorActionBarTitle);
+        btnDetailCount.setStrokeColor(fishton.colorSelectCircleStroke);
+        btnDetailCount.setOnClickListener(this);
+        btnDetailBack.setOnClickListener(this);
+        initToolBar();
+    }
+
+    private void initValue() {
+        Intent intent = getIntent();
+        initPosition = intent.getIntExtra(Define.BUNDLE_NAME.POSITION.name(), -1);
+    }
+
+    private void initToolBar() {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            uiUtil.setStatusBarColor(this, fishton.colorStatusBar);
+        }
+        if (fishton.isStatusBarLight
+                && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            vpDetailPager.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+        }
+
+    }
+
+    private void initAdapter() {
+        if (fishton.pickerImages == null) {
+            Toast.makeText(this, R.string.msg_error, Toast.LENGTH_SHORT).show();
+            finish();
+            return;
+        }
+
+        onCheckStateChange(fishton.pickerImages[initPosition]);
+
+        DetailViewPagerAdapter adapter = new DetailViewPagerAdapter(getLayoutInflater(), fishton.pickerImages);
+        vpDetailPager.setAdapter(adapter);
+        vpDetailPager.setCurrentItem(initPosition);
+
+        vpDetailPager.addOnPageChangeListener(this);
+    }
+
+    public void onCheckStateChange(Uri image) {
+        boolean isContained = fishton.selectedImages.contains(image);
+        if (isContained) {
+            updateRadioButton(btnDetailCount,
+                    String.valueOf(fishton.selectedImages.indexOf(image) + 1));
+        } else {
+            btnDetailCount.unselect();
+        }
+    }
+
+
+    public void updateRadioButton(RadioWithTextButton v, String text) {
+        if (fishton.maxCount == 1)
+            v.setDrawable(ContextCompat.getDrawable(v.getContext(), R.drawable.ic_done_white_24dp));
+        else
+            v.setText(text);
+    }
+
+    @Override
+    public void onBackPressed() {
+        finishActivity();
+    }
+
+    @Override
+    public void onClick(View v) {
+        int id = v.getId();
+        if (id == R.id.btn_detail_count) {
+            Uri image = fishton.pickerImages[vpDetailPager.getCurrentItem()];
+            if (fishton.selectedImages.contains(image)) {
+                fishton.selectedImages.remove(image);
+                onCheckStateChange(image);
+            } else {
+                if (fishton.selectedImages.size() == fishton.maxCount) {
+                    Snackbar.make(v, fishton.messageLimitReached, Snackbar.LENGTH_SHORT).show();
+                } else {
+                    fishton.selectedImages.add(image);
+                    onCheckStateChange(image);
+
+                    if (fishton.isAutomaticClose && fishton.selectedImages.size() == fishton.maxCount)
+                        finishActivity();
+                }
+            }
+
+        } else if (id == R.id.btn_detail_back) {
+            finishActivity();
+        }
+    }
+
+    @Override
+    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+
+    }
+
+    @Override
+    public void onPageSelected(int position) {
+        onCheckStateChange(fishton.pickerImages[position]);
+    }
+
+    @Override
+    public void onPageScrollStateChanged(int state) {
+
+    }
+
+    void finishActivity() {
+        Intent i = new Intent();
+        setResult(RESULT_OK, i);
+        finish();
+    }
+}

+ 13 - 0
FishBun/src/main/java/com/sangcomz/fishbun/ui/detail/DetailController.java

@@ -0,0 +1,13 @@
+package com.sangcomz.fishbun.ui.detail;
+
+/**
+ * Created by sangcomz on 11/06/2017.
+ */
+
+class DetailController {
+    private DetailActivity detailActivity;
+
+    DetailController(DetailActivity detailActivity) {
+        this.detailActivity = detailActivity;
+    }
+}

+ 285 - 0
FishBun/src/main/java/com/sangcomz/fishbun/ui/picker/PickerActivity.java

@@ -0,0 +1,285 @@
+package com.sangcomz.fishbun.ui.picker;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.design.widget.Snackbar;
+import android.support.v7.app.ActionBar;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.sangcomz.fishbun.BaseActivity;
+import com.sangcomz.fishbun.R;
+import com.sangcomz.fishbun.adapter.view.PickerGridAdapter;
+import com.sangcomz.fishbun.bean.Album;
+import com.sangcomz.fishbun.define.Define;
+import com.sangcomz.fishbun.permission.PermissionCheck;
+import com.sangcomz.fishbun.util.RadioWithTextButton;
+import com.sangcomz.fishbun.util.SingleMediaScanner;
+import com.sangcomz.fishbun.util.SquareFrameLayout;
+import com.sangcomz.fishbun.util.TextDrawable;
+
+import java.io.File;
+import java.util.ArrayList;
+
+
+public class PickerActivity extends BaseActivity {
+
+    private static final String TAG = "PickerActivity";
+
+    private RecyclerView recyclerView;
+    private PickerController pickerController;
+    private Album album;
+    private int position;
+    private PickerGridAdapter adapter;
+    private GridLayoutManager layoutManager;
+
+    private void initValue() {
+        Intent intent = getIntent();
+        album = intent.getParcelableExtra(Define.BUNDLE_NAME.ALBUM.name());
+        position = intent.getIntExtra(Define.BUNDLE_NAME.POSITION.name(), -1);
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        try {
+            outState.putString(define.SAVE_INSTANCE_SAVED_IMAGE, pickerController.getSavePath());
+            outState.putParcelableArrayList(define.SAVE_INSTANCE_NEW_IMAGES, pickerController.getAddImagePaths());
+        } catch (Exception e) {
+            Log.d(TAG, e.toString());
+        }
+
+        super.onSaveInstanceState(outState);
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle outState) {
+        // Always call the superclass so it can restore the view hierarchy
+        super.onRestoreInstanceState(outState);
+        // Restore state members from saved instance
+        try {
+            ArrayList<Uri> addImages = outState.getParcelableArrayList(define.SAVE_INSTANCE_NEW_IMAGES);
+            String savedImage = outState.getString(define.SAVE_INSTANCE_SAVED_IMAGE);
+            setAdapter(fishton.pickerImages);
+            if (addImages != null) {
+                pickerController.setAddImagePaths(addImages);
+            }
+            if (savedImage != null) {
+                pickerController.setSavePath(savedImage);
+            }
+        } catch (Exception e) {
+            Log.d(TAG, e.toString());
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_photo_picker);
+        initController();
+        initValue();
+        initView();
+        if (pickerController.checkPermission())
+            pickerController.displayImage(album.bucketId, fishton.isExceptGif);
+
+    }
+
+    @Override
+    public void onBackPressed() {
+        transImageFinish(position);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == define.TAKE_A_PICK_REQUEST_CODE) {
+            if (resultCode == RESULT_OK) {
+                File savedFile = new File(pickerController.getSavePath());
+                new SingleMediaScanner(this, savedFile);
+                adapter.addImage(Uri.fromFile(savedFile));
+            } else {
+                new File(pickerController.getSavePath()).delete();
+            }
+        } else if (requestCode == define.ENTER_DETAIL_REQUEST_CODE) {
+            if (resultCode == RESULT_OK) {
+                if (fishton.isAutomaticClose && fishton.selectedImages.size() == fishton.maxCount)
+                    finishActivity();
+                refreshThumb();
+            }
+        }
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode,
+                                           String permissions[], int[] grantResults) {
+        switch (requestCode) {
+            case 28: {
+                if (grantResults.length > 0) {
+                    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                        pickerController.displayImage(album.bucketId, fishton.isExceptGif);
+                        // permission was granted, yay! do the
+                        // calendar task you need to do.
+                    } else {
+                        new PermissionCheck(this).showPermissionDialog();
+                        finish();
+                    }
+                }
+            }
+
+        }
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        // Inflate the menu; this adds items to the action bar if it is present.
+        getMenuInflater().inflate(R.menu.menu_photo_album, menu);
+        MenuItem item = menu.findItem(R.id.action_ok);
+
+        if (fishton.drawableOkButton != null) {
+            item.setIcon(fishton.drawableOkButton);
+        } else if (fishton.strTextMenu != null) {
+            if (fishton.colorTextMenu != Integer.MAX_VALUE) {
+                item.setIcon(new TextDrawable(getResources(), fishton.strTextMenu, fishton.colorTextMenu));
+            } else {
+                item.setTitle(fishton.strTextMenu);
+                item.setIcon(null);
+            }
+        }
+        return true;
+    }
+
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        // Handle action bar item clicks here. The action bar will
+        // automatically handle clicks on the Home/Up button, so long
+        // as you specify album parent activity in AndroidManifest.xml.
+        int id = item.getItemId();
+        if (id == R.id.action_ok) {
+            if (fishton.selectedImages.size() < fishton.minCount) {
+                Snackbar.make(recyclerView, fishton.messageNothingSelected, Snackbar.LENGTH_SHORT).show();
+            } else {
+                finishActivity();
+            }
+            return true;
+        } else if (id == android.R.id.home)
+            transImageFinish(position);
+        return super.onOptionsItemSelected(item);
+    }
+
+    public void showToolbarTitle(int total) {
+        if (getSupportActionBar() != null) {
+            if (fishton.maxCount == 1 || !fishton.isShowCount)
+                getSupportActionBar()
+                        .setTitle(album.bucketName);
+            else
+                getSupportActionBar()
+                        .setTitle(album.bucketName + "(" + String.valueOf(total) + "/" + fishton.maxCount + ")");
+        }
+    }
+
+    private void initController() {
+        pickerController = new PickerController(this);
+    }
+
+    private void initView() {
+        recyclerView = findViewById(R.id.recycler_picker_list);
+        layoutManager = new GridLayoutManager(this, fishton.photoSpanCount, GridLayoutManager.VERTICAL, false);
+        recyclerView.setLayoutManager(layoutManager);
+        initToolBar();
+    }
+
+    private void initToolBar() {
+        Toolbar toolbar = findViewById(R.id.toolbar_picker_bar);
+        setSupportActionBar(toolbar);
+        toolbar.setBackgroundColor(fishton.colorActionBar);
+        toolbar.setTitleTextColor(fishton.colorActionBarTitle);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            uiUtil.setStatusBarColor(this, fishton.colorStatusBar);
+        }
+        ActionBar bar = getSupportActionBar();
+        if (bar != null) {
+            bar.setDisplayHomeAsUpEnabled(true);
+            if (fishton.drawableHomeAsUpIndicator != null)
+                getSupportActionBar().setHomeAsUpIndicator(fishton.drawableHomeAsUpIndicator);
+        }
+
+        if (fishton.isStatusBarLight
+                && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            toolbar.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+        }
+        showToolbarTitle(0);
+    }
+
+
+    public void setAdapter(Uri[] result) {
+        fishton.pickerImages = result;
+        if (adapter == null) {
+            adapter = new PickerGridAdapter(pickerController,
+                    pickerController.getPathDir(album.bucketId));
+            adapter.setActionListener(new PickerGridAdapter.OnPhotoActionListener() {
+                @Override
+                public void onDeselect() {
+                    refreshThumb();
+                }
+            });
+        }
+        recyclerView.setAdapter(adapter);
+        showToolbarTitle(fishton.selectedImages.size());
+    }
+
+    private void refreshThumb() {
+        int firstVisible = layoutManager.findFirstVisibleItemPosition();
+        int lastVisible = layoutManager.findLastVisibleItemPosition();
+        for (int i = firstVisible; i <= lastVisible; i++) {
+            View view = layoutManager.findViewByPosition(i);
+            if (view instanceof SquareFrameLayout) {
+                SquareFrameLayout item = (SquareFrameLayout) view;
+                RadioWithTextButton btnThumbCount = item.findViewById(R.id.btn_thumb_count);
+                ImageView imgThumbImage = item.findViewById(R.id.img_thumb_image);
+                Uri image = (Uri) item.getTag();
+                if (image != null) {
+                    int index = fishton.selectedImages.indexOf(image);
+                    if (index != -1) {
+                        adapter.updateRadioButton(imgThumbImage,
+                                btnThumbCount,
+                                String.valueOf(index + 1),
+                                true);
+                    } else {
+                        adapter.updateRadioButton(imgThumbImage,
+                                btnThumbCount,
+                                "",
+                                false);
+                        showToolbarTitle(fishton.selectedImages.size());
+                    }
+                }
+
+            }
+        }
+    }
+
+    void transImageFinish(int position) {
+        Define define = new Define();
+        Intent i = new Intent();
+        i.putParcelableArrayListExtra(define.INTENT_ADD_PATH, pickerController.getAddImagePaths());
+        i.putExtra(define.INTENT_POSITION, position);
+        setResult(define.TRANS_IMAGES_RESULT_CODE, i);
+        finish();
+    }
+
+    public void finishActivity() {
+        Intent i = new Intent();
+        setResult(RESULT_OK, i);
+        if (fishton.isStartInAllView)
+            i.putParcelableArrayListExtra(Define.INTENT_PATH, fishton.selectedImages);
+        finish();
+    }
+
+}

+ 160 - 0
FishBun/src/main/java/com/sangcomz/fishbun/ui/picker/PickerController.java

@@ -0,0 +1,160 @@
+package com.sangcomz.fishbun.ui.picker;
+
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Environment;
+import android.provider.MediaStore;
+import android.support.annotation.NonNull;
+
+import com.sangcomz.fishbun.permission.PermissionCheck;
+import com.sangcomz.fishbun.util.CameraUtil;
+import com.sangcomz.fishbun.util.RegexUtil;
+
+import java.util.ArrayList;
+
+/**
+ * Created by sangc on 2015-11-05.
+ */
+public class PickerController {
+    private PickerActivity pickerActivity;
+    private ArrayList<Uri> addImagePaths = new ArrayList<>();
+    private ContentResolver resolver;
+    private CameraUtil cameraUtil = new CameraUtil();
+    private String pathDir = "";
+
+
+    PickerController(PickerActivity pickerActivity) {
+        this.pickerActivity = pickerActivity;
+
+        resolver = pickerActivity.getContentResolver();
+    }
+
+
+    public void takePicture(Activity activity, String saveDir) {
+        cameraUtil.takePicture(activity, saveDir);
+    }
+
+
+    public void setToolbarTitle(int total) {
+        pickerActivity.showToolbarTitle(total);
+    }
+
+    String getSavePath() {
+        return cameraUtil.getSavePath();
+    }
+
+    void setSavePath(String savePath) {
+        cameraUtil.setSavePath(savePath);
+    }
+
+    public void setAddImagePath(Uri imagePath) {
+        this.addImagePaths.add(imagePath);
+    }
+
+    protected ArrayList<Uri> getAddImagePaths() {
+        return addImagePaths;
+    }
+
+    public void setAddImagePaths(ArrayList<Uri> addImagePaths) {
+        this.addImagePaths = addImagePaths;
+    }
+
+
+    boolean checkPermission() {
+        PermissionCheck permissionCheck = new PermissionCheck(pickerActivity);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            if (permissionCheck.CheckStoragePermission())
+                return true;
+        } else
+            return true;
+        return false;
+    }
+
+
+    void displayImage(Long bucketId,
+                      Boolean exceptGif) {
+        new DisplayImage(bucketId, exceptGif).execute();
+    }
+
+    private class DisplayImage extends AsyncTask<Void, Void, Uri[]> {
+        private Long bucketId;
+        Boolean exceptGif;
+
+        DisplayImage(Long bucketId,
+                     Boolean exceptGif) {
+            this.bucketId = bucketId;
+            this.exceptGif = exceptGif;
+        }
+
+        @Override
+        protected Uri[] doInBackground(Void... params) {
+            return getAllMediaThumbnailsPath(bucketId, exceptGif);
+        }
+
+        @Override
+        protected void onPostExecute(Uri[] result) {
+            super.onPostExecute(result);
+            pickerActivity.setAdapter(result);
+        }
+    }
+
+
+    @NonNull
+    private Uri[] getAllMediaThumbnailsPath(long id,
+                                            Boolean exceptGif) {
+        String selection = MediaStore.Images.Media.BUCKET_ID + " = ?";
+        String bucketId = String.valueOf(id);
+        String sort = MediaStore.Images.Media._ID + " DESC";
+        String[] selectionArgs = {bucketId};
+
+        Uri images = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+        Cursor c;
+        if (!bucketId.equals("0")) {
+            c = resolver.query(images, null, selection, selectionArgs, sort);
+        } else {
+            c = resolver.query(images, null, null, null, sort);
+        }
+        Uri[] imageUris = new Uri[c == null ? 0 : c.getCount()];
+        if (c != null) {
+            try {
+                if (c.moveToFirst()) {
+                    setPathDir(c.getString(c.getColumnIndex(MediaStore.Images.Media.DATA)),
+                            c.getString(c.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME)));
+                    int position = -1;
+                    RegexUtil regexUtil = new RegexUtil();
+                    do {
+                        if (exceptGif &&
+                                regexUtil.checkGif(c.getString(c.getColumnIndex(MediaStore.Images.Media.DATA))))
+                            continue;
+                        int imgId = c.getInt(c.getColumnIndex(MediaStore.MediaColumns._ID));
+                        Uri path = Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "" + imgId);
+                        imageUris[++position] = path;
+                    } while (c.moveToNext());
+                }
+                c.close();
+            } catch (Exception e) {
+                if (!c.isClosed()) c.close();
+            }
+        }
+        return imageUris;
+    }
+
+    private String setPathDir(String path, String fileName) {
+        return pathDir = path.replace("/" + fileName, "");
+    }
+
+    public String getPathDir(Long bucketId) {
+        if (pathDir.equals("") || bucketId == 0)
+            pathDir = Environment.getExternalStoragePublicDirectory(
+                    Environment.DIRECTORY_DCIM + "/Camera").getAbsolutePath();
+        return pathDir;
+    }
+
+    public void finishActivity() {
+        pickerActivity.finishActivity();
+    }
+}

+ 73 - 0
FishBun/src/main/java/com/sangcomz/fishbun/util/CameraUtil.java

@@ -0,0 +1,73 @@
+package com.sangcomz.fishbun.util;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.provider.MediaStore;
+import android.support.v4.content.FileProvider;
+
+import com.sangcomz.fishbun.define.Define;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * Created by sangcomz on 16/01/2017.
+ */
+
+public class CameraUtil {
+
+    private String savePath;
+
+    public void takePicture(Activity activity, String saveDir) {
+        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+        if (takePictureIntent.resolveActivity(activity.getPackageManager()) != null) {
+            // Create the File where the photo should go
+            File photoFile = null;
+            try {
+                photoFile = createImageFile(saveDir); //make a file
+                setSavePath(photoFile.getAbsolutePath());
+            } catch (IOException ex) {
+                ex.printStackTrace();
+                // Error occurred while creating the File
+            }
+            // Continue only if the File was successfully created
+            if (photoFile != null) {
+                Uri uri;
+                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+                    uri = FileProvider.getUriForFile(activity,
+                            activity.getApplicationContext().getPackageName() + ".provider", photoFile);
+                } else {
+                    uri = Uri.fromFile(photoFile);
+                }
+                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
+
+                activity.startActivityForResult(takePictureIntent, new Define().TAKE_A_PICK_REQUEST_CODE);
+            }
+        }
+    }
+
+    private File createImageFile(String saveDir) throws IOException {
+        // Create an image file name
+        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
+        String imageFileName = "JPEG_" + timeStamp + "_";
+        File storageDir = new File(saveDir);
+        return File.createTempFile(
+                imageFileName,  /* prefix */
+                ".jpg",         /* suffix */
+                storageDir      /* directory */
+        );
+    }
+
+
+    public String getSavePath() {
+        return savePath;
+    }
+
+    public void setSavePath(String savePath) {
+        this.savePath = savePath;
+    }
+}

+ 34 - 0
FishBun/src/main/java/com/sangcomz/fishbun/util/CustomPagerSnapHelper.java

@@ -0,0 +1,34 @@
+package com.sangcomz.fishbun.util;
+
+import android.support.v7.widget.PagerSnapHelper;
+import android.support.v7.widget.RecyclerView;
+
+/**
+ * Created by sangcomz on 11/06/2017.
+ */
+
+public class CustomPagerSnapHelper extends PagerSnapHelper {
+
+    RecyclerView.LayoutManager layoutManager;
+    OnPageChangeListener listener;
+
+    public CustomPagerSnapHelper(RecyclerView.LayoutManager layoutManager) {
+        this.layoutManager = layoutManager;
+    }
+
+    @Override
+    public boolean onFling(int velocityX, int velocityY) {
+        if (listener != null) {
+            listener.onPageChanged(findTargetSnapPosition(layoutManager, velocityX, velocityY));
+        }
+        return super.onFling(velocityX, velocityY);
+    }
+
+    public void setOnPageChangeListener(OnPageChangeListener listener) {
+        this.listener = listener;
+    }
+
+    public interface OnPageChangeListener {
+        void onPageChanged(int position);
+    }
+}

+ 168 - 0
FishBun/src/main/java/com/sangcomz/fishbun/util/RadioWithTextButton.java

@@ -0,0 +1,168 @@
+package com.sangcomz.fishbun.util;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.View;
+
+import com.sangcomz.fishbun.R;
+
+/**
+ * Created by sangcomz on 01/05/2017.
+ */
+
+public class RadioWithTextButton extends View {
+
+    private Paint mStrokePaint;
+    private Paint mCirclePaint;
+    private Paint mTextPaint;
+
+    private String mText = null;
+    private float mTextWidth;
+
+    private Drawable mDrawable = null;
+    private Rect mCenterRect = null;
+
+    public RadioWithTextButton(Context context) {
+        super(context);
+        init();
+    }
+
+    public RadioWithTextButton(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    public RadioWithTextButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init();
+    }
+
+    private void init() {
+        mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mTextPaint.setFakeBoldText(true);
+    }
+
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+        int w = getWidth();
+        int h = getHeight();
+
+        mStrokePaint.setStrokeWidth(w / 18);
+
+        if (isChecked()) {
+            canvas.drawCircle(w / 2, h / 2, w / 3, mCirclePaint);
+            if (mText != null) {
+                int PADDING_TEXT = 20;
+                mTextWidth = (w / 3) * 2 - PADDING_TEXT;
+                drawTextCentred(canvas, mTextPaint, mText, w / 2, h / 2);
+            } else if (mDrawable != null) {
+                mDrawable.setBounds(getCenterRect());
+                mDrawable.draw(canvas);
+            }
+        } else {
+            mStrokePaint.setStyle(Paint.Style.STROKE);
+            canvas.drawCircle(w / 2, h / 2, w / 3, mStrokePaint);
+        }
+
+    }
+
+    public void setCircleColor(int color) {
+        if (mCirclePaint != null)
+            mCirclePaint.setColor(color);
+    }
+
+    public void setTextColor(int color) {
+        if (mTextPaint != null)
+            mTextPaint.setColor(color);
+    }
+
+    public void setStrokeColor(int color) {
+        if (mStrokePaint != null)
+            mStrokePaint.setColor(color);
+    }
+
+    public void setText(String text) {
+        mDrawable = null;
+        mText = text;
+        invalidate();
+    }
+
+    public void setDrawable(Drawable drawable) {
+        mText = null;
+        mDrawable = drawable;
+        invalidate();
+    }
+
+    public boolean isChecked() {
+        return mText != null || mDrawable != null;
+    }
+
+    public void unselect() {
+        mText = null;
+        mDrawable = null;
+        invalidate();
+    }
+
+    private final Rect textBounds = new Rect(); //don't new this up in a draw method
+
+    public void drawTextCentred(Canvas canvas, Paint paint, String text, float cx, float cy) {
+        setTextSizeForWidth(paint, text, mTextWidth);
+        paint.getTextBounds(text, 0, text.length(), textBounds);
+        canvas.drawText(text, cx - textBounds.exactCenterX(), cy - textBounds.exactCenterY(), paint);
+    }
+
+    /**
+     * Sets the text size for a Paint object so a given string of text will be a
+     * given width.
+     *
+     * @param paint        the Paint to set the text size for
+     * @param desiredWidth the desired width
+     */
+    private static void setTextSizeForWidth(Paint paint, String text, float desiredWidth) {
+
+        // Pick a reasonably large value for the test. Larger values produce
+        // more accurate results, but may cause problems with hardware
+        // acceleration. But there are workarounds for that, too; refer to
+        // http://stackoverflow.com/questions/6253528/font-size-too-large-to-fit-in-cache
+        final float defaultTextSize = 44f;
+        paint.setTextSize(defaultTextSize);
+        Rect textBounds = new Rect();
+        paint.getTextBounds(text, 0, text.length(), textBounds);
+        // Calculate the desired size as a proportion of our testTextSize.
+        if (textBounds.width() > desiredWidth) {
+            float desiredTextSize = defaultTextSize * (desiredWidth / textBounds.width());
+
+            // Set the paint for that size.
+            paint.setTextSize(desiredTextSize);
+        }
+
+    }
+
+    private int fetchAccentColor() {
+        TypedValue typedValue = new TypedValue();
+        TypedArray a = getContext().obtainStyledAttributes(typedValue.data, new int[]{R.attr.colorAccent});
+        int color = a.getColor(0, 0);
+        a.recycle();
+        return color;
+    }
+
+    private Rect getCenterRect() {
+        if (mCenterRect == null) {
+            Rect r = new Rect(0, 0, getWidth(), getHeight());
+            int width = getWidth() / 4;
+            mCenterRect = new Rect((int) (r.exactCenterX() - width), (int) (r.exactCenterY() - width), getWidth() - width, getHeight() - width);
+        }
+        return mCenterRect;
+    }
+}

+ 14 - 0
FishBun/src/main/java/com/sangcomz/fishbun/util/RegexUtil.java

@@ -0,0 +1,14 @@
+package com.sangcomz.fishbun.util;
+
+/**
+ * Created by sangcomz on 09/04/2017.
+ */
+
+public class RegexUtil {
+    private static final String GIF_PATTERN =
+            "(.+?)\\.gif$";
+
+    public boolean checkGif(String path) {
+        return path.matches(GIF_PATTERN);
+    }
+}

+ 9 - 0
FishBun/src/main/java/com/sangcomz/fishbun/util/ScanListener.java

@@ -0,0 +1,9 @@
+package com.sangcomz.fishbun.util;
+
+/**
+ * Created by sangcomz on 16/01/2017.
+ */
+
+public abstract class ScanListener {
+    protected abstract void onScanCompleted();
+}

+ 44 - 0
FishBun/src/main/java/com/sangcomz/fishbun/util/SingleMediaScanner.java

@@ -0,0 +1,44 @@
+package com.sangcomz.fishbun.util;
+
+import android.content.Context;
+import android.media.MediaScannerConnection;
+import android.net.Uri;
+
+import java.io.File;
+
+/**
+ * Created by sangcomz on 16/01/2017.
+ */
+
+public class SingleMediaScanner implements MediaScannerConnection.MediaScannerConnectionClient {
+
+    private MediaScannerConnection mMs;
+    private File mFile;
+    private ScanListener scanListener;
+
+    public SingleMediaScanner(Context context, File f) {
+        init(context, f);
+    }
+
+    public SingleMediaScanner(Context context, File f, ScanListener scanListener) {
+        init(context, f);
+        this.scanListener = scanListener;
+    }
+
+    private void init(Context context, File f) {
+        mFile = f;
+        mMs = new MediaScannerConnection(context, this);
+        mMs.connect();
+    }
+
+    @Override
+    public void onMediaScannerConnected() {
+        mMs.scanFile(mFile.getAbsolutePath(), null);
+    }
+
+    @Override
+    public void onScanCompleted(String path, Uri uri) {
+        if (scanListener != null) scanListener.onScanCompleted();
+        mMs.disconnect();
+    }
+}

+ 26 - 0
FishBun/src/main/java/com/sangcomz/fishbun/util/SquareFrameLayout.java

@@ -0,0 +1,26 @@
+package com.sangcomz.fishbun.util;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+/**
+ * Created by sangc on 2015-12-26.
+ */
+public class SquareFrameLayout extends android.support.v7.widget.ContentFrameLayout {
+    public SquareFrameLayout(Context context) {
+        super(context);
+    }
+
+    public SquareFrameLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public SquareFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, widthMeasureSpec);
+    }
+}

+ 26 - 0
FishBun/src/main/java/com/sangcomz/fishbun/util/SquareImageView.java

@@ -0,0 +1,26 @@
+package com.sangcomz.fishbun.util;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+/**
+ * Created by sangc on 2015-12-26.
+ */
+public class SquareImageView extends android.support.v7.widget.AppCompatImageView {
+    public SquareImageView(Context context) {
+        super(context);
+    }
+
+    public SquareImageView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public SquareImageView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, widthMeasureSpec);
+    }
+}

+ 70 - 0
FishBun/src/main/java/com/sangcomz/fishbun/util/TextDrawable.java

@@ -0,0 +1,70 @@
+package com.sangcomz.fishbun.util;
+
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.TypedValue;
+
+import java.util.Locale;
+
+/**
+ * Created by sangcomz on 14/04/2017.
+ */
+
+public class TextDrawable extends Drawable {
+    private static final int DEFAULT_TEXTSIZE = 14;
+    private Paint mPaint;
+    private CharSequence mText;
+    private int mIntrinsicWidth;
+    private int mIntrinsicHeight;
+
+    public TextDrawable(Resources res, CharSequence text, int textColor) {
+        mText = text;
+        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mPaint.setColor(textColor);
+        mPaint.setTextAlign(Paint.Align.CENTER);
+        float textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
+                DEFAULT_TEXTSIZE, res.getDisplayMetrics());
+        mPaint.setTextSize(textSize);
+        if (Locale.getDefault().getLanguage().equals(Locale.ENGLISH.getLanguage()))
+            mPaint.setFakeBoldText(true);
+        mIntrinsicWidth = (int) (mPaint.measureText(mText, 0, mText.length()) + .5);
+        mIntrinsicHeight = mPaint.getFontMetricsInt(null);
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        Rect bounds = getBounds();
+        canvas.drawText(mText, 0, mText.length(),
+                bounds.centerX(), (float) mIntrinsicHeight * 3f / 4f, mPaint);
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    @Override
+    public int getIntrinsicWidth() {
+        return mIntrinsicWidth;
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return mIntrinsicHeight;
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        mPaint.setAlpha(alpha);
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter filter) {
+        mPaint.setColorFilter(filter);
+    }
+}

File diff suppressed because it is too large
+ 1292 - 0
FishBun/src/main/java/com/sangcomz/fishbun/util/TouchImageView.java


+ 37 - 0
FishBun/src/main/java/com/sangcomz/fishbun/util/UiUtil.java

@@ -0,0 +1,37 @@
+package com.sangcomz.fishbun.util;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.view.Window;
+import android.view.WindowManager;
+
+/**
+ * Created by sangc on 2015-11-20.
+ */
+public class UiUtil {
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    public void setStatusBarColor(Activity activity, int colorStatusBar) {
+        if (colorStatusBar == Integer.MAX_VALUE) return;
+        Window window = activity.getWindow();
+        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+        window.setStatusBarColor(colorStatusBar);
+    }
+
+    public boolean isLandscape(Context context) {
+        Configuration configuration = context.getResources().getConfiguration();
+        return configuration.orientation == Configuration.ORIENTATION_LANDSCAPE;
+    }
+
+    public Drawable getBitmapToDrawable(Resources resources, Bitmap bitmap) {
+        if (bitmap == null) return null;
+        return new BitmapDrawable(resources, bitmap);
+    }
+}

+ 10 - 0
FishBun/src/main/res/drawable/ic_add_a_photo_gray_100dp.xml

@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="100dp"
+    android:height="100dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0">
+    <path
+
+        android:fillColor="#FFd9d9d9"
+        android:pathData="M3,4L3,1h2v3h3v2L5,6v3L3,9L3,6L0,6L0,4h3zM6,10L6,7h3L9,4h7l1.83,2L21,6c1.1,0 2,0.9 2,2v12c0,1.1 -0.9,2 -2,2L5,22c-1.1,0 -2,-0.9 -2,-2L3,10h3zM13,19c2.76,0 5,-2.24 5,-5s-2.24,-5 -5,-5 -5,2.24 -5,5 2.24,5 5,5zM9.8,14c0,1.77 1.43,3.2 3.2,3.2s3.2,-1.43 3.2,-3.2 -1.43,-3.2 -3.2,-3.2 -3.2,1.43 -3.2,3.2z" />
+</vector>

+ 9 - 0
FishBun/src/main/res/drawable/ic_add_a_photo_white_24dp.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0">
+    <path
+        android:fillColor="#ffffff"
+        android:pathData="M3,4L3,1h2v3h3v2L5,6v3L3,9L3,6L0,6L0,4h3zM6,10L6,7h3L9,4h7l1.83,2L21,6c1.1,0 2,0.9 2,2v12c0,1.1 -0.9,2 -2,2L5,22c-1.1,0 -2,-0.9 -2,-2L3,10h3zM13,19c2.76,0 5,-2.24 5,-5s-2.24,-5 -5,-5 -5,2.24 -5,5 2.24,5 5,5zM9.8,14c0,1.77 1.43,3.2 3.2,3.2s3.2,-1.43 3.2,-3.2 -1.43,-3.2 -3.2,-3.2 -3.2,1.43 -3.2,3.2z" />
+</vector>

+ 9 - 0
FishBun/src/main/res/drawable/ic_arrow_back_white_24dp.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0">
+    <path
+        android:fillColor="#c1ffffff"
+        android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z" />
+</vector>

+ 9 - 0
FishBun/src/main/res/drawable/ic_done_white_24dp.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#ffffff"
+        android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"/>
+</vector>

+ 9 - 0
FishBun/src/main/res/drawable/ic_photo_library_gray_100dp.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="100dp"
+    android:height="100dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0">
+    <path
+        android:fillColor="#FFd9d9d9"
+        android:pathData="M22,16L22,4c0,-1.1 -0.9,-2 -2,-2L8,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2zM11,12l2.03,2.71L16,11l4,5L8,16l3,-4zM2,6v14c0,1.1 0.9,2 2,2h14v-2L4,20L4,6L2,6z" />
+</vector>

+ 9 - 0
FishBun/src/main/res/drawable/ic_photo_white_24dp.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
+</vector>

+ 44 - 0
FishBun/src/main/res/layout/activity_detail_actiivy.xml

@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/black">
+
+
+    <android.support.v4.view.ViewPager
+        android:id="@+id/vp_detail_pager"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintHorizontal_bias="0.0"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_bias="1.0" />
+
+
+    <android.support.v7.widget.AppCompatImageButton
+        android:id="@+id/btn_detail_back"
+        android:layout_width="30dp"
+        android:layout_height="30dp"
+        android:layout_marginLeft="24dp"
+        android:layout_marginTop="24dp"
+        android:background="?attr/selectableItemBackgroundBorderless"
+        android:src="@drawable/ic_arrow_back_white_24dp"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+    <com.sangcomz.fishbun.util.RadioWithTextButton
+        android:id="@+id/btn_detail_count"
+        android:layout_width="30dp"
+        android:layout_height="30dp"
+        android:layout_marginRight="24dp"
+        android:layout_marginTop="24dp"
+        android:background="?attr/selectableItemBackgroundBorderless"
+        android:padding="5dp"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
+
+</android.support.constraint.ConstraintLayout>

+ 68 - 0
FishBun/src/main/res/layout/activity_photo_album.xml

@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".ui.album.AlbumActivity">
+
+    <android.support.v7.widget.RecyclerView
+        android:id="@+id/recycler_album_list"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
+
+    <android.support.design.widget.AppBarLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:theme="@style/AppTheme.AppBarOverlay">
+
+        <android.support.v7.widget.Toolbar
+            android:id="@+id/toolbar_album_bar"
+            android:layout_width="match_parent"
+            android:layout_height="?attr/actionBarSize"
+            android:background="#3F51B5"
+            app:popupTheme="@style/AppTheme.PopupOverlay" />
+
+    </android.support.design.widget.AppBarLayout>
+
+    <RelativeLayout
+        android:id="@+id/rel_album_empty"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="#ffffff"
+        android:visibility="visible"
+        app:layout_behavior="@string/appbar_scrolling_view_behavior">
+
+        <LinearLayout
+            android:id="@+id/lin_album_camera"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerInParent="true"
+            android:background="?attr/selectableItemBackgroundBorderless"
+            android:clickable="true"
+            android:orientation="vertical">
+
+            <ImageView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_horizontal"
+                android:background="@drawable/ic_add_a_photo_gray_100dp" />
+
+            <TextView
+                android:id="@+id/txt_album_msg"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="10dp"
+                android:fontFamily="sans-serif-thin"
+                android:gravity="center"
+                android:text="@string/msg_loading_image"
+                android:textColor="?android:textColorSecondary"
+                android:textSize="23dp" />
+        </LinearLayout>
+
+
+    </RelativeLayout>
+
+
+</android.support.design.widget.CoordinatorLayout>

+ 29 - 0
FishBun/src/main/res/layout/activity_photo_picker.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <android.support.v7.widget.RecyclerView
+        android:id="@+id/recycler_picker_list"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
+
+    <android.support.design.widget.AppBarLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:theme="@style/AppTheme.AppBarOverlay">
+
+        <android.support.v7.widget.Toolbar
+            android:id="@+id/toolbar_picker_bar"
+            android:layout_width="match_parent"
+            android:layout_height="?attr/actionBarSize"
+            android:background="#3F51B5"
+            app:popupTheme="@style/AppTheme.PopupOverlay" />
+
+    </android.support.design.widget.AppBarLayout>
+
+
+</android.support.design.widget.CoordinatorLayout>

+ 44 - 0
FishBun/src/main/res/layout/album_item.xml

@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_marginBottom="1dp"
+    android:background="#ffffff"
+    android:orientation="horizontal">
+
+    <com.sangcomz.fishbun.util.SquareImageView
+        android:id="@+id/img_album_thumb"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_centerInParent="true"
+        android:scaleType="centerCrop" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginLeft="10dp"
+        android:background="?attr/selectableItemBackground"
+        android:gravity="center_vertical"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/txt_album_name"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:ellipsize="end"
+            android:maxLines="1"
+            android:textColor="#000"
+            android:textSize="16dp" />
+
+        <TextView
+            android:id="@+id/txt_album_count"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:ellipsize="end"
+            android:maxLines="1"
+            android:textSize="12dp" />
+
+    </LinearLayout>
+
+
+</LinearLayout>

+ 20 - 0
FishBun/src/main/res/layout/detail_item.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <com.sangcomz.fishbun.util.TouchImageView
+        android:id="@+id/img_detail_image"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_marginLeft="0dp"
+        android:layout_marginTop="0dp"
+        android:layout_marginRight="0dp"
+        android:layout_marginBottom="0dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintLeft_toLeftOf="parent"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_bias="0.5" />
+</android.support.constraint.ConstraintLayout>

+ 21 - 0
FishBun/src/main/res/layout/header_item.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_margin="1dp"
+    android:background="#EBF7FF">
+
+    <RelativeLayout
+        android:id="@+id/rel_header_area"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="#d9d9d9">
+
+        <ImageView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerInParent="true"
+            android:background="@drawable/ic_add_a_photo_white_24dp" />
+    </RelativeLayout>
+
+</RelativeLayout>

+ 25 - 0
FishBun/src/main/res/layout/thumb_item.xml

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.sangcomz.fishbun.util.SquareFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_margin="1dp"
+    android:background="#d9d9d9"
+    android:clipChildren="false"
+    android:clipToPadding="false">
+
+    <ImageView
+        android:id="@+id/img_thumb_image"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="?attr/selectableItemBackgroundBorderless"
+        android:clipChildren="false"
+        android:clipToPadding="false"
+        android:cropToPadding="false" />
+
+    <com.sangcomz.fishbun.util.RadioWithTextButton
+        android:id="@+id/btn_thumb_count"
+        android:layout_width="30dp"
+        android:layout_height="30dp"
+        android:layout_gravity="right"
+        android:layout_margin="5dp" />
+</com.sangcomz.fishbun.util.SquareFrameLayout>

+ 9 - 0
FishBun/src/main/res/menu/menu_photo_album.xml

@@ -0,0 +1,9 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <item
+        android:id="@+id/action_ok"
+        android:icon="@drawable/ic_done_white_24dp"
+        android:title="@string/done"
+        app:showAsAction="withText|ifRoom" />
+</menu>

+ 13 - 0
FishBun/src/main/res/values-ko/strings.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="album">앨범</string>
+    <string name="camera">카메라</string>
+    <string name="done">완료</string>
+    <string name="msg_no_selected">선택된 이미지가 없습니다.</string>
+    <string name="str_all_view">전체사진</string>
+    <string name="msg_permission">권한이 거부 됐습니다.</string>
+    <string name="msg_no_image">앨범이 없습니다.\n사진을 찍어보세요!</string>
+    <string name="msg_full_image">더 이상 선택할 수 없습니다.</string>
+    <string name="msg_error">일시적인 오류가 발생했습니다. 잠시후에 다시 시도해주세요.</string>
+
+</resources>

+ 3 - 0
FishBun/src/main/res/values/dimens.xml

@@ -0,0 +1,3 @@
+<resources>
+    <dimen name="album_thum_size">70dp</dimen>
+</resources>

+ 14 - 0
FishBun/src/main/res/values/strings.xml

@@ -0,0 +1,14 @@
+<resources>
+    <string name="album">Album</string>
+    <string name="camera">camera</string>
+    <string name="done">done</string>
+    <string name="msg_no_selected">There is no selected image.</string>
+    <string name="str_all_view">All view</string>
+    <string name="msg_permission">Permission denied.</string>
+    <string name="msg_no_image">There is no album.\nTake a Picture!</string>
+    <string name="msg_full_image">Selection full. Deselect an image to choose another.</string>
+    <string name="msg_loading_image">Loading...</string>
+    <string name="image">image</string>
+
+    <string name="msg_error">There was a temporary error. Please try again in a few minutes.</string>
+</resources>

+ 12 - 0
FishBun/src/main/res/values/styles.xml

@@ -0,0 +1,12 @@
+<resources>
+
+    <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+    <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
+
+    <style name="FishBunTheme" parent="Theme.AppCompat.Light.NoActionBar" />
+
+    <style name="DetailViewTheme" parent="FishBunTheme">
+        <item name="colorControlHighlight">@android:color/white</item>
+    </style>
+</resources>

+ 4 - 0
FishBun/src/main/res/xml/provider_paths.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<paths xmlns:android="http://schemas.android.com/apk/res/android">
+    <external-path name="external_files" path="."/>
+</paths>

+ 23 - 0
FishBun/src/test/java/com/sangcomz/fishbun/RegexTest.java

@@ -0,0 +1,23 @@
+package com.sangcomz.fishbun;
+
+import com.sangcomz.fishbun.util.RegexUtil;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Created by Seok-Won on 31/10/2016.
+ */
+
+public class RegexTest {
+    @Test
+    public void checkGif_isCorrect() throws Exception {
+        RegexUtil regexUtil = new RegexUtil();
+        assertEquals(regexUtil.checkGif("/storage/emulated/0/Download/giphy.gif"), true);
+        assertEquals(regexUtil.checkGif("/storage/emulated/0/Download/gif.gif.png"), false);
+        assertEquals(regexUtil.checkGif("/storage/emulated/0/Download/gif.png"), false);
+        assertEquals(regexUtil.checkGif("/storage/emulated/0/Download/giphy (1).gif"), true);
+
+    }
+}

+ 1 - 0
FishBunDemo/.gitignore

@@ -0,0 +1 @@
+/build

+ 93 - 0
FishBunDemo/build.gradle

@@ -0,0 +1,93 @@
+apply plugin: 'com.android.application'
+
+buildscript {
+    repositories {
+        jcenter()
+        mavenCentral()
+    }
+}
+
+
+
+
+android {
+    compileSdkVersion gradle.compileSdk
+    buildToolsVersion gradle.buildTools
+
+    defaultConfig {
+        applicationId "com.sangcomz.fishbundemo"
+        minSdkVersion gradle.minSdk
+        targetSdkVersion gradle.targetSdk
+        versionCode gradle.versionCode
+        versionName gradle.versionName
+        multiDexEnabled true
+    }
+    Properties signProp = new Properties()
+    if (project.rootProject.file('playstore/sign.properties').isFile()) {
+        signProp.load(project.rootProject.file('playstore/sign.properties').newDataInputStream())
+        def password = signProp.get("password", "")
+        signingConfigs {
+            release {
+                keyAlias signProp.get("alias", "")
+                keyPassword password
+                storeFile rootProject.file("playstore/key.jks")
+                storePassword password
+            }
+        }
+    }
+    buildTypes {
+
+        release {
+            minifyEnabled true
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+            if (project.rootProject.file('playstore/sign.properties').isFile()) signingConfig signingConfigs.release
+
+        }
+        debug {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+    lintOptions {
+        abortOnError false
+    }
+}
+
+dependencies {
+    implementation fileTree(include: ['*.jar'], dir: 'libs')
+    implementation project(':FishBun')
+
+    implementation "com.android.support:appcompat-v7:$rootProject.support_version"
+    implementation "com.android.support:recyclerview-v7:$rootProject.support_version"
+
+    debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.2'
+    releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.2'
+
+    implementation "com.android.support:support-v4:$rootProject.support_version"
+    implementation "com.squareup.picasso:picasso:$rootProject.picasso_version"
+}
+
+buildscript {
+    repositories {
+        jcenter()
+    }
+    dependencies {
+        classpath 'com.github.triplet.gradle:play-publisher:1.2.2'
+        // NOTE: Do not place your application dependencies here; they belong
+        // in the individual module build.gradle files
+    }
+}
+
+apply plugin: 'com.github.triplet.play'
+
+play {
+    track = 'production' // or 'rollout' or 'beta' or 'alpha'
+//    userFraction = 0.2 // only necessary for 'rollout', in this case default is 0.1 (10% of the target)
+    jsonFile = rootProject.file('playstore_kye.json')
+}
+
+task printProps {
+    doLast {
+        println System.properties['system']
+    }
+}

+ 28 - 0
FishBunDemo/proguard-rules.pro

@@ -0,0 +1,28 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in C:\sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+
+-dontwarn com.squareup.haha.guava.**
+-dontwarn com.squareup.haha.perflib.**
+-dontwarn com.squareup.haha.trove.**
+-dontwarn com.squareup.leakcanary.**
+-keep class com.squareup.haha.** { *; }
+-keep class com.squareup.leakcanary.** { *; }
+
+# Marshmallow removed Notification.setLatestEventInfo()
+-dontwarn android.app.Notification

+ 27 - 0
FishBunDemo/src/main/AndroidManifest.xml

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.sangcomz.fishbundemo">
+
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+    <application
+        android:name="com.sangcomz.fishbundemo.CommonApplication"
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="@string/app_name"
+        android:supportsRtl="true"
+        android:theme="@style/AppTheme">
+        <activity android:name="com.sangcomz.fishbundemo.WithActivityActivity" />
+        <activity android:name="com.sangcomz.fishbundemo.ActivityWithBaseActivity" />
+        <activity android:name="com.sangcomz.fishbundemo.MainActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity android:name="com.sangcomz.fishbundemo.WithFragmentActivity"/>
+    </application>
+
+</manifest>

BIN
FishBunDemo/src/main/ic_launcher-web.png


+ 14 - 0
FishBunDemo/src/main/java/com/sangcomz/fishbundemo/CommonApplication.java

@@ -0,0 +1,14 @@
+package com.sangcomz.fishbundemo;
+
+import android.app.Application;
+
+import com.squareup.leakcanary.LeakCanary;
+
+
+public class CommonApplication extends Application {
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        LeakCanary.install(this);
+    }
+}

+ 72 - 0
FishBunDemo/src/main/java/com/sangcomz/fishbundemo/ImageAdapter.java

@@ -0,0 +1,72 @@
+package com.sangcomz.fishbundemo;
+
+import android.content.Context;
+import android.net.Uri;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.squareup.picasso.Picasso;
+
+import java.util.ArrayList;
+
+/**
+ * Created by sangc on 2015-11-06.
+ */
+public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder> {
+    Context context;
+    ArrayList<Uri> imagePaths;
+    ImageController imageController;
+
+    public ImageAdapter(Context context, ImageController imageController, ArrayList<Uri> imagePaths) {
+        this.context = context;
+        this.imageController = imageController;
+        this.imagePaths = imagePaths;
+    }
+
+    @Override
+    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        View view = LayoutInflater.from(parent.getContext())
+                .inflate(R.layout.item, parent, false);
+        return new ViewHolder(view);
+    }
+
+    @Override
+    public void onBindViewHolder(final ViewHolder holder, final int position) {
+        final Uri imagePath = imagePaths.get(position);
+        Picasso
+                .get()
+                .load(imagePath)
+                .fit()
+                .centerCrop()
+                .into(holder.imageView);
+        holder.imageView.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                imageController.setImgMain(imagePath);
+            }
+        });
+    }
+
+    public void changePath(ArrayList<Uri> imagePaths) {
+        this.imagePaths = imagePaths;
+        imageController.setImgMain(imagePaths.get(0));
+        notifyDataSetChanged();
+    }
+
+    @Override
+    public int getItemCount() {
+        return imagePaths.size();
+    }
+
+    public class ViewHolder extends RecyclerView.ViewHolder {
+        ImageView imageView;
+
+        public ViewHolder(View itemView) {
+            super(itemView);
+            imageView = (ImageView) itemView.findViewById(R.id.img_item);
+        }
+    }
+}

+ 26 - 0
FishBunDemo/src/main/java/com/sangcomz/fishbundemo/ImageController.java

@@ -0,0 +1,26 @@
+package com.sangcomz.fishbundemo;
+
+import android.net.Uri;
+import android.widget.ImageView;
+
+import com.squareup.picasso.Picasso;
+
+/**
+ * Created by sangc on 2015-11-06.
+ */
+class ImageController {
+    ImageView imgMain;
+
+    ImageController(ImageView imgMain) {
+        this.imgMain = imgMain;
+    }
+
+    void setImgMain(Uri path) {
+        Picasso
+                .get()
+                .load(path)
+                .fit()
+                .centerCrop()
+                .into(imgMain);
+    }
+}

+ 60 - 0
FishBunDemo/src/main/java/com/sangcomz/fishbundemo/MainActivity.java

@@ -0,0 +1,60 @@
+package com.sangcomz.fishbundemo;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+import android.widget.Button;
+
+public class MainActivity extends AppCompatActivity {
+    Button btnWithActivityLight;
+    Button btnWithActivityBase;
+    Button btnWithActivityDark;
+    Button btnWithFragment;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_main);
+
+        btnWithActivityBase = (Button) findViewById(R.id.btn_with_activity_basic);
+        btnWithActivityDark = (Button) findViewById(R.id.btn_with_activity_dark);
+        btnWithActivityLight = (Button) findViewById(R.id.btn_with_activity_light);
+        btnWithFragment = (Button) findViewById(R.id.btn_with_Fragment);
+
+        btnWithActivityBase.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Intent i = new Intent(MainActivity.this, WithActivityActivity.class);
+                i.putExtra("mode", 0);
+                startActivity(i);
+            }
+        });
+
+        btnWithActivityDark.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Intent i = new Intent(MainActivity.this, WithActivityActivity.class);
+                i.putExtra("mode", 1);
+                startActivity(i);
+            }
+        });
+
+        btnWithActivityLight.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Intent i = new Intent(MainActivity.this, WithActivityActivity.class);
+                i.putExtra("mode", 2);
+                startActivity(i);
+            }
+        });
+
+        btnWithFragment.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Intent i = new Intent(MainActivity.this, WithFragmentActivity.class);
+                startActivity(i);
+            }
+        });
+    }
+}

+ 82 - 0
FishBunDemo/src/main/java/com/sangcomz/fishbundemo/SubFragment.java

@@ -0,0 +1,82 @@
+package com.sangcomz.fishbundemo;
+
+
+import android.content.Intent;
+import android.graphics.Color;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ImageView;
+
+import com.sangcomz.fishbun.FishBun;
+import com.sangcomz.fishbun.adapter.image.impl.PicassoAdapter;
+import com.sangcomz.fishbun.define.Define;
+
+import java.util.ArrayList;
+
+
+/**
+ * A simple {@link Fragment} subclass.
+ */
+public class SubFragment extends Fragment {
+
+    ArrayList<Uri> path = new ArrayList<>();
+    ImageView imgMain;
+    Button btnAddImages;
+    RecyclerView recyclerView;
+    LinearLayoutManager linearLayoutManager;
+    ImageAdapter imageAdapter;
+    ImageController withActivityController;
+
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        View rootView = inflater.inflate(R.layout.fragment_sub, container, false);
+        // Inflate the layout for this fragment
+        imgMain = (ImageView) rootView.findViewById(R.id.img_main);
+        recyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerview);
+        btnAddImages = (Button) rootView.findViewById(R.id.btn_add_images);
+        linearLayoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false);
+        withActivityController = new ImageController(imgMain);
+        imageAdapter = new ImageAdapter(getActivity(), withActivityController, path);
+        recyclerView.setLayoutManager(linearLayoutManager);
+        recyclerView.setAdapter(imageAdapter);
+
+
+        btnAddImages.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                FishBun.with(SubFragment.this)
+                        .setImageAdapter(new PicassoAdapter())
+                        .setPickerCount(10)
+                        .setActionBarColor(Color.parseColor("#3F51B5"), Color.parseColor("#303F9F"))
+                        .setSelectedImages(path)
+                        .setCamera(true)
+                        .startAlbum();
+            }
+        });
+
+        return rootView;
+    }
+
+
+    @Override
+    public void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        switch (requestCode) {
+            case Define.ALBUM_REQUEST_CODE:
+                if (resultCode == getActivity().RESULT_OK) {
+                    path = data.getParcelableArrayListExtra(Define.INTENT_PATH);
+                    imageAdapter.changePath(path);
+                    break;
+                }
+        }
+    }
+}

+ 134 - 0
FishBunDemo/src/main/java/com/sangcomz/fishbundemo/WithActivityActivity.java

@@ -0,0 +1,134 @@
+package com.sangcomz.fishbundemo;
+
+import android.content.Intent;
+import android.graphics.Color;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.widget.ImageView;
+
+import com.sangcomz.fishbun.FishBun;
+import com.sangcomz.fishbun.adapter.image.impl.PicassoAdapter;
+import com.sangcomz.fishbun.define.Define;
+
+import java.util.ArrayList;
+
+public class WithActivityActivity extends AppCompatActivity {
+
+    ArrayList<Uri> path = new ArrayList<>();
+    ImageView imgMain;
+    RecyclerView recyclerView;
+    LinearLayoutManager linearLayoutManager;
+    ImageAdapter imageAdapter;
+    ImageController mainController;
+    int mode;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_withactivity);
+        mode = getIntent().getIntExtra("mode", -1);
+        imgMain = findViewById(R.id.img_main);
+        recyclerView = findViewById(R.id.recyclerview);
+        linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
+        mainController = new ImageController(imgMain);
+        imageAdapter = new ImageAdapter(this, mainController, path);
+        recyclerView.setLayoutManager(linearLayoutManager);
+        recyclerView.setAdapter(imageAdapter);
+    }
+
+    protected void onActivityResult(int requestCode, int resultCode,
+                                    Intent imageData) {
+        super.onActivityResult(requestCode, resultCode, imageData);
+
+        switch (requestCode) {
+            case Define.ALBUM_REQUEST_CODE:
+                if (resultCode == RESULT_OK) {
+                    path = imageData.getParcelableArrayListExtra(Define.INTENT_PATH);
+                    imageAdapter.changePath(path);
+                    break;
+                }
+        }
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.menu_main, menu);
+        return super.onCreateOptionsMenu(menu);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        int id = item.getItemId();
+        if (id == R.id.action_plus) {
+            switch (mode) {
+                //basic
+                case 0: {
+                    FishBun.with(WithActivityActivity.this)
+                            .setImageAdapter(new GlideAdapter())
+                            .startAlbum();
+                    break;
+                }
+                //dark
+                case 1: {
+                    FishBun.with(WithActivityActivity.this)
+                            .setImageAdapter(new PicassoAdapter())
+                            .setMaxCount(5)
+                            .setMinCount(3)
+                            .setPickerSpanCount(5)
+                            .setActionBarColor(Color.parseColor("#795548"), Color.parseColor("#5D4037"), false)
+                            .setActionBarTitleColor(Color.parseColor("#ffffff"))
+                            .setSelectedImages(path)
+                            .setAlbumSpanCount(2, 3)
+                            .setButtonInAlbumActivity(false)
+                            .setCamera(true)
+                            .exceptGif(true)
+                            .setReachLimitAutomaticClose(true)
+                            .setHomeAsUpIndicatorDrawable(ContextCompat.getDrawable(this, R.drawable.ic_custom_back_white))
+                            .setOkButtonDrawable(ContextCompat.getDrawable(this, R.drawable.ic_custom_ok))
+                            .setAllViewTitle("All")
+                            .setActionBarTitle("FishBun Dark")
+                            .textOnNothingSelected("Please select three or more!")
+                            .startAlbum();
+                    break;
+                }
+                //Light
+                case 2: {
+                    FishBun.with(WithActivityActivity.this)
+                            .setImageAdapter(new PicassoAdapter())
+                            .setPickerCount(50)
+                            .setPickerSpanCount(4)
+                            .setActionBarColor(Color.parseColor("#ffffff"), Color.parseColor("#ffffff"), true)
+                            .setActionBarTitleColor(Color.parseColor("#000000"))
+                            .setSelectedImages(path)
+                            .setAlbumSpanCount(1, 2)
+                            .setButtonInAlbumActivity(true)
+                            .setCamera(false)
+                            .exceptGif(true)
+                            .setReachLimitAutomaticClose(false)
+                            .setHomeAsUpIndicatorDrawable(ContextCompat.getDrawable(this, R.drawable.ic_arrow_back_black_24dp))
+                            .setOkButtonDrawable(ContextCompat.getDrawable(this, R.drawable.ic_check_black_24dp))
+                            .setAllViewTitle("All of your photos")
+                            .setActionBarTitle("FishBun Light")
+                            .textOnImagesSelectionLimitReached("You can't select any more.")
+                            .textOnNothingSelected("I need a photo!")
+                            .startAlbum();
+                    break;
+                }
+                default: {
+                    finish();
+                }
+            }
+
+            return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+}

+ 22 - 0
FishBunDemo/src/main/java/com/sangcomz/fishbundemo/WithFragmentActivity.java

@@ -0,0 +1,22 @@
+package com.sangcomz.fishbundemo;
+
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.widget.RelativeLayout;
+
+public class WithFragmentActivity extends AppCompatActivity {
+
+    RelativeLayout areaContainer;
+    SubFragment subFragment;
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_withfragment);
+
+        areaContainer = (RelativeLayout)findViewById(R.id.area_container);
+        subFragment = new SubFragment();
+
+        getSupportFragmentManager().beginTransaction().add(areaContainer.getId(), subFragment).commit();
+
+    }
+}

+ 0 - 0
FishBunDemo/src/main/play/en-US/whatsnew


+ 9 - 0
FishBunDemo/src/main/res/drawable/ic_arrow_back_black_24dp.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
+</vector>

+ 9 - 0
FishBunDemo/src/main/res/drawable/ic_arrow_back_white_24dp.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
+</vector>

+ 9 - 0
FishBunDemo/src/main/res/drawable/ic_check_black_24dp.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
+</vector>

+ 9 - 0
FishBunDemo/src/main/res/drawable/ic_check_white_24dp.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0">
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z" />
+</vector>

+ 9 - 0
FishBunDemo/src/main/res/drawable/ic_custom_back_white.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="141.732"
+    android:viewportWidth="141.732">
+    <path
+        android:fillColor="#FFFFFF"
+        android:pathData="M105.61,118.68c3.4,3.4 3.4,8.91 0,12.31c-3.4,3.4 -8.91,3.4 -12.31,0c-0.02,-0.02 -0.04,-0.04 -0.05,-0.06l-0.03,0.02l-57.66,-57.66l0.02,-0.02c-1.61,-1.58 -2.61,-3.78 -2.61,-6.21c-0,-2.73 1.26,-5.17 3.23,-6.76l-0.06,-0.06l57.66,-57.66l0.03,0.02c0.02,-0.02 0.03,-0.04 0.05,-0.06c3.4,-3.4 8.91,-3.4 12.31,0c3.4,3.4 3.4,8.91 0,12.31c-0.02,0.02 -0.04,0.03 -0.06,0.05l0.02,0.02L54.04,67.06l51.54,51.54l-0.03,0.02C105.57,118.65 105.59,118.66 105.61,118.68" />
+</vector>

File diff suppressed because it is too large
+ 10 - 0
FishBunDemo/src/main/res/drawable/ic_custom_ok.xml


+ 43 - 0
FishBunDemo/src/main/res/layout/activity_main.xml

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    tools:context="com.sangcomz.fishbundemo.MainActivity">
+
+    <android.support.v7.widget.AppCompatButton
+        android:id="@+id/btn_with_activity_basic"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:backgroundTint="@color/colorPrimary"
+        android:text="@string/txt_with_activity_basic"
+        android:textColor="@color/switch_thumb_material_light" />
+
+    <android.support.v7.widget.AppCompatButton
+        android:id="@+id/btn_with_activity_dark"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:backgroundTint="@color/colorPrimary"
+        android:text="@string/txt_with_activity_dark"
+        android:textColor="@color/switch_thumb_material_light" />
+
+    <android.support.v7.widget.AppCompatButton
+        android:id="@+id/btn_with_activity_light"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:backgroundTint="@color/colorPrimary"
+        android:text="@string/txt_with_activity_light"
+        android:textColor="@color/switch_thumb_material_light" />
+
+    <android.support.v7.widget.AppCompatButton
+        android:id="@+id/btn_with_Fragment"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/txt_with_Fragment" />
+
+</LinearLayout>

+ 22 - 0
FishBunDemo/src/main/res/layout/activity_withactivity.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".WithActivityActivity">
+
+    <ImageView
+        android:id="@+id/img_main"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_above="@+id/recyclerview"
+        android:layout_marginBottom="1dp" />
+
+
+    <android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/recyclerview"
+        android:layout_width="match_parent"
+        android:layout_height="100dp"
+        android:layout_alignParentBottom="true" />
+
+</RelativeLayout>

+ 13 - 0
FishBunDemo/src/main/res/layout/activity_withfragment.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:id="@+id/area_container"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    tools:context="com.sangcomz.fishbundemo.WithFragmentActivity">
+
+</RelativeLayout>

+ 29 - 0
FishBunDemo/src/main/res/layout/fragment_sub.xml

@@ -0,0 +1,29 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    tools:context=".WithActivityActivity">
+
+    <Button
+        android:id="@+id/btn_add_images"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="5dp"
+        android:text="@string/txt_add_images" />
+
+    <ImageView
+        android:id="@+id/img_main"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_above="@+id/recycler_picker_list"
+        android:layout_marginBottom="1dp"
+        android:layout_weight="1" />
+
+
+    <android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/recyclerview"
+        android:layout_width="match_parent"
+        android:layout_height="100dp" />
+
+</LinearLayout>

+ 7 - 0
FishBunDemo/src/main/res/layout/item.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/img_item"
+    android:layout_margin="1dp"
+    android:layout_width="120dp"
+    android:layout_height="100dp"/>
+

+ 9 - 0
FishBunDemo/src/main/res/menu/menu_main.xml

@@ -0,0 +1,9 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+    <item
+        android:id="@+id/action_plus"
+        android:icon="@mipmap/ic_add_white"
+        app:showAsAction="withText|ifRoom"
+        android:title="@string/plus" />
+
+</menu>

BIN
FishBunDemo/src/main/res/mipmap-hdpi/ic_add_white.png


BIN
FishBunDemo/src/main/res/mipmap-hdpi/ic_launcher.png


BIN
FishBunDemo/src/main/res/mipmap-mdpi/ic_launcher.png


BIN
FishBunDemo/src/main/res/mipmap-xhdpi/ic_launcher.png


BIN
FishBunDemo/src/main/res/mipmap-xxhdpi/ic_launcher.png


BIN
FishBunDemo/src/main/res/mipmap-xxxhdpi/ic_launcher.png


+ 7 - 0
FishBunDemo/src/main/res/values-ko/strings.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">FishBunDemo</string>
+    <string name="plus">사진추가</string>
+
+    <string name="txt_add_images">이미지 추가</string>
+</resources>

+ 6 - 0
FishBunDemo/src/main/res/values-w820dp/dimens.xml

@@ -0,0 +1,6 @@
+<resources>
+    <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+         (such as screen margins) for screens with more than 820dp of available width. This
+         would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+    <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>

+ 6 - 0
FishBunDemo/src/main/res/values/colors.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="colorPrimary">#FFA400</color>
+    <color name="colorPrimaryDark">#DF9613</color>
+    <color name="colorAccent">#ffffff</color>
+</resources>

+ 5 - 0
FishBunDemo/src/main/res/values/dimens.xml

@@ -0,0 +1,5 @@
+<resources>
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>

+ 11 - 0
FishBunDemo/src/main/res/values/strings.xml

@@ -0,0 +1,11 @@
+<resources>
+    <string name="app_name">FishBunDemo</string>
+    <string name="plus">plus</string>
+
+    <string name="txt_with_activity_basic" translatable="false">Activity in startActivityForResult(Basic)</string>
+    <string name="txt_with_activity_light" translatable="false">Activity in startActivityForResult(Light)</string>
+    <string name="txt_with_activity_dark" translatable="false">Activity in startActivityForResult(Dark)</string>
+    <string name="txt_with_Fragment" translatable="false">Fragment in startActivityForResult</string>
+    <string name="txt_add_images">Add Images</string>
+
+</resources>

+ 11 - 0
FishBunDemo/src/main/res/values/styles.xml

@@ -0,0 +1,11 @@
+<resources>
+
+    <!-- Base application theme. -->
+    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+        <!-- Customize your theme here. -->
+        <item name="colorPrimary">@color/colorPrimary</item>
+        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+        <item name="colorAccent">@color/colorAccent</item>
+    </style>
+
+</resources>

+ 202 - 0
LICENSE

@@ -0,0 +1,202 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright 2015 Jeong SeokWon
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+

+ 0 - 0
README.md


Some files were not shown because too many files changed in this diff