Repository: nohana/Laevatein Branch: master Commit: 983fad53933f Files: 259 Total size: 306.5 KB Directory structure: gitextract_gancgo1e/ ├── .gitignore ├── .travis.yml ├── README.md ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── laevatein/ │ ├── .gitignore │ ├── build.gradle │ ├── gradle.properties │ ├── proguard-rules.txt │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── laevatein/ │ │ └── MimeTypeTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── laevatein/ │ │ │ ├── Laevatein.java │ │ │ ├── MimeType.java │ │ │ ├── SelectionSpecBuilder.java │ │ │ ├── internal/ │ │ │ │ ├── entity/ │ │ │ │ │ ├── Album.java │ │ │ │ │ ├── CaptureResources.java │ │ │ │ │ ├── CounterViewResources.java │ │ │ │ │ ├── DialogResources.java │ │ │ │ │ ├── ErrorViewResources.java │ │ │ │ │ ├── ErrorViewSpec.java │ │ │ │ │ ├── Item.java │ │ │ │ │ ├── ItemViewResources.java │ │ │ │ │ ├── PreviewViewResources.java │ │ │ │ │ ├── SelectionSpec.java │ │ │ │ │ ├── UncapableCause.java │ │ │ │ │ └── ViewResourceSpec.java │ │ │ │ ├── loader/ │ │ │ │ │ ├── AlbumLoader.java │ │ │ │ │ └── AlbumPhotoLoader.java │ │ │ │ ├── misc/ │ │ │ │ │ ├── ui/ │ │ │ │ │ │ ├── ConfirmationDialogFragment.java │ │ │ │ │ │ ├── FragmentUtils.java │ │ │ │ │ │ └── helper/ │ │ │ │ │ │ └── options/ │ │ │ │ │ │ ├── OptionsMenu.java │ │ │ │ │ │ └── OptionsMenuHandler.java │ │ │ │ │ └── widget/ │ │ │ │ │ └── SquareGridViewItemRelativeLayout.java │ │ │ │ ├── model/ │ │ │ │ │ ├── AlbumPhotoCollection.java │ │ │ │ │ ├── DevicePhotoAlbumCollection.java │ │ │ │ │ ├── PreviewStateHolder.java │ │ │ │ │ └── SelectedUriCollection.java │ │ │ │ ├── package-info.java │ │ │ │ ├── ui/ │ │ │ │ │ ├── AlbumListFragment.java │ │ │ │ │ ├── PhotoGridFragment.java │ │ │ │ │ ├── PreviewFragment.java │ │ │ │ │ ├── SelectedCountFragment.java │ │ │ │ │ ├── SelectedPhotoGridFragment.java │ │ │ │ │ ├── adapter/ │ │ │ │ │ │ ├── AlbumPhotoAdapter.java │ │ │ │ │ │ ├── DevicePhotoAlbumAdapter.java │ │ │ │ │ │ ├── PreviewPagerAdapter.java │ │ │ │ │ │ ├── RecyclerViewCursorAdapter.java │ │ │ │ │ │ └── SelectedPhotoAdapter.java │ │ │ │ │ ├── helper/ │ │ │ │ │ │ ├── AlbumListViewHelper.java │ │ │ │ │ │ ├── PhotoGridViewHelper.java │ │ │ │ │ │ ├── PhotoSelectionActivityDrawerToggle.java │ │ │ │ │ │ ├── PhotoSelectionViewHelper.java │ │ │ │ │ │ ├── PreviewHelper.java │ │ │ │ │ │ ├── SelectedCountViewHelper.java │ │ │ │ │ │ ├── SelectedGridViewHelper.java │ │ │ │ │ │ └── options/ │ │ │ │ │ │ ├── FinishSelectMenuHandler.java │ │ │ │ │ │ ├── PhotoSelectionOptionsMenu.java │ │ │ │ │ │ ├── PhotoSelectionOptionsMenuHandler.java │ │ │ │ │ │ └── UnknownMenuHandler.java │ │ │ │ │ └── widget/ │ │ │ │ │ └── PhotoDecoration.java │ │ │ │ ├── utils/ │ │ │ │ │ ├── ErrorViewUtils.java │ │ │ │ │ ├── ExifInterfaceUtils.java │ │ │ │ │ ├── MediaStoreUtils.java │ │ │ │ │ └── PhotoMetadataUtils.java │ │ │ │ └── view/ │ │ │ │ └── PreviewViewPager.java │ │ │ └── ui/ │ │ │ ├── ImagePreviewActivity.java │ │ │ └── PhotoSelectionActivity.java │ │ └── res/ │ │ ├── drawable/ │ │ │ ├── l_bg_drawer_item.xml │ │ │ ├── l_ic_capture.xml │ │ │ ├── l_ic_capture_normal.xml │ │ │ ├── l_ic_capture_pressed.xml │ │ │ ├── l_ic_folder.xml │ │ │ ├── l_ic_folder_close.xml │ │ │ ├── l_ic_folder_open.xml │ │ │ └── l_list_selector_drawer_menu.xml │ │ ├── drawable-v21/ │ │ │ └── l_bg_drawer_item.xml │ │ ├── layout/ │ │ │ ├── l_action_layout_checkbox.xml │ │ │ ├── l_activity_preview.xml │ │ │ ├── l_activity_select_photo.xml │ │ │ ├── l_fragment_default_preview.xml │ │ │ ├── l_fragment_grid_photo.xml │ │ │ ├── l_fragment_list_album.xml │ │ │ ├── l_fragment_selected_count.xml │ │ │ ├── l_grid_item_default_photo.xml │ │ │ ├── l_list_item_default_album.xml │ │ │ └── l_view_counter.xml │ │ ├── menu/ │ │ │ ├── l_activity_image_preview.xml │ │ │ └── l_activity_options_select_photo.xml │ │ ├── values/ │ │ │ ├── attrs.xml │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ ├── values-ja/ │ │ │ └── strings.xml │ │ └── values-v21/ │ │ └── styles.xml │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── laevatein/ │ │ ├── LaevateinTest.java │ │ ├── MimeTypeTest.java │ │ ├── SelectionSpecBuilderTest.java │ │ └── internal/ │ │ └── entity/ │ │ └── AlbumTest.java │ └── resources/ │ └── com/ │ └── laevatein/ │ └── robolectric.properties ├── repository/ │ └── com/ │ └── laevatein/ │ └── Laevatein/ │ ├── 0.10.0/ │ │ ├── Laevatein-0.10.0.aar │ │ ├── Laevatein-0.10.0.aar.md5 │ │ ├── Laevatein-0.10.0.aar.sha1 │ │ ├── Laevatein-0.10.0.pom │ │ ├── Laevatein-0.10.0.pom.md5 │ │ └── Laevatein-0.10.0.pom.sha1 │ ├── 0.10.1/ │ │ ├── Laevatein-0.10.1.aar │ │ ├── Laevatein-0.10.1.aar.md5 │ │ ├── Laevatein-0.10.1.aar.sha1 │ │ ├── Laevatein-0.10.1.pom │ │ ├── Laevatein-0.10.1.pom.md5 │ │ └── Laevatein-0.10.1.pom.sha1 │ ├── 0.10.2/ │ │ ├── Laevatein-0.10.2.aar │ │ ├── Laevatein-0.10.2.aar.md5 │ │ ├── Laevatein-0.10.2.aar.sha1 │ │ ├── Laevatein-0.10.2.pom │ │ ├── Laevatein-0.10.2.pom.md5 │ │ └── Laevatein-0.10.2.pom.sha1 │ ├── 0.10.3/ │ │ ├── Laevatein-0.10.3.aar │ │ ├── Laevatein-0.10.3.aar.md5 │ │ ├── Laevatein-0.10.3.aar.sha1 │ │ ├── Laevatein-0.10.3.pom │ │ ├── Laevatein-0.10.3.pom.md5 │ │ └── Laevatein-0.10.3.pom.sha1 │ ├── 0.10.4/ │ │ ├── Laevatein-0.10.4.aar │ │ ├── Laevatein-0.10.4.aar.md5 │ │ ├── Laevatein-0.10.4.aar.sha1 │ │ ├── Laevatein-0.10.4.pom │ │ ├── Laevatein-0.10.4.pom.md5 │ │ └── Laevatein-0.10.4.pom.sha1 │ ├── 0.10.5/ │ │ ├── Laevatein-0.10.5.aar │ │ ├── Laevatein-0.10.5.aar.md5 │ │ ├── Laevatein-0.10.5.aar.sha1 │ │ ├── Laevatein-0.10.5.pom │ │ ├── Laevatein-0.10.5.pom.md5 │ │ └── Laevatein-0.10.5.pom.sha1 │ ├── 0.9.0/ │ │ ├── Laevatein-0.9.0.aar │ │ ├── Laevatein-0.9.0.aar.md5 │ │ ├── Laevatein-0.9.0.aar.sha1 │ │ ├── Laevatein-0.9.0.pom │ │ ├── Laevatein-0.9.0.pom.md5 │ │ └── Laevatein-0.9.0.pom.sha1 │ ├── 0.9.1/ │ │ ├── Laevatein-0.9.1.aar │ │ ├── Laevatein-0.9.1.aar.md5 │ │ ├── Laevatein-0.9.1.aar.sha1 │ │ ├── Laevatein-0.9.1.pom │ │ ├── Laevatein-0.9.1.pom.md5 │ │ └── Laevatein-0.9.1.pom.sha1 │ ├── 0.9.10/ │ │ ├── Laevatein-0.9.10.aar │ │ ├── Laevatein-0.9.10.aar.md5 │ │ ├── Laevatein-0.9.10.aar.sha1 │ │ ├── Laevatein-0.9.10.pom │ │ ├── Laevatein-0.9.10.pom.md5 │ │ └── Laevatein-0.9.10.pom.sha1 │ ├── 0.9.11/ │ │ ├── Laevatein-0.9.11.aar │ │ ├── Laevatein-0.9.11.aar.md5 │ │ ├── Laevatein-0.9.11.aar.sha1 │ │ ├── Laevatein-0.9.11.pom │ │ ├── Laevatein-0.9.11.pom.md5 │ │ └── Laevatein-0.9.11.pom.sha1 │ ├── 0.9.12/ │ │ ├── Laevatein-0.9.12.aar │ │ ├── Laevatein-0.9.12.aar.md5 │ │ ├── Laevatein-0.9.12.aar.sha1 │ │ ├── Laevatein-0.9.12.pom │ │ ├── Laevatein-0.9.12.pom.md5 │ │ └── Laevatein-0.9.12.pom.sha1 │ ├── 0.9.13/ │ │ ├── Laevatein-0.9.13.aar │ │ ├── Laevatein-0.9.13.aar.md5 │ │ ├── Laevatein-0.9.13.aar.sha1 │ │ ├── Laevatein-0.9.13.pom │ │ ├── Laevatein-0.9.13.pom.md5 │ │ └── Laevatein-0.9.13.pom.sha1 │ ├── 0.9.14/ │ │ ├── Laevatein-0.9.14.aar │ │ ├── Laevatein-0.9.14.aar.md5 │ │ ├── Laevatein-0.9.14.aar.sha1 │ │ ├── Laevatein-0.9.14.pom │ │ ├── Laevatein-0.9.14.pom.md5 │ │ └── Laevatein-0.9.14.pom.sha1 │ ├── 0.9.15/ │ │ ├── Laevatein-0.9.15.aar │ │ ├── Laevatein-0.9.15.aar.md5 │ │ ├── Laevatein-0.9.15.aar.sha1 │ │ ├── Laevatein-0.9.15.pom │ │ ├── Laevatein-0.9.15.pom.md5 │ │ └── Laevatein-0.9.15.pom.sha1 │ ├── 0.9.16/ │ │ ├── Laevatein-0.9.16.aar │ │ ├── Laevatein-0.9.16.aar.md5 │ │ ├── Laevatein-0.9.16.aar.sha1 │ │ ├── Laevatein-0.9.16.pom │ │ ├── Laevatein-0.9.16.pom.md5 │ │ └── Laevatein-0.9.16.pom.sha1 │ ├── 0.9.2/ │ │ ├── Laevatein-0.9.2.aar │ │ ├── Laevatein-0.9.2.aar.md5 │ │ ├── Laevatein-0.9.2.aar.sha1 │ │ ├── Laevatein-0.9.2.pom │ │ ├── Laevatein-0.9.2.pom.md5 │ │ └── Laevatein-0.9.2.pom.sha1 │ ├── 0.9.3/ │ │ ├── Laevatein-0.9.3.aar │ │ ├── Laevatein-0.9.3.aar.md5 │ │ ├── Laevatein-0.9.3.aar.sha1 │ │ ├── Laevatein-0.9.3.pom │ │ ├── Laevatein-0.9.3.pom.md5 │ │ └── Laevatein-0.9.3.pom.sha1 │ ├── 0.9.4/ │ │ ├── Laevatein-0.9.4.aar │ │ ├── Laevatein-0.9.4.aar.md5 │ │ ├── Laevatein-0.9.4.aar.sha1 │ │ ├── Laevatein-0.9.4.pom │ │ ├── Laevatein-0.9.4.pom.md5 │ │ └── Laevatein-0.9.4.pom.sha1 │ ├── 0.9.5/ │ │ ├── Laevatein-0.9.5.aar │ │ ├── Laevatein-0.9.5.aar.md5 │ │ ├── Laevatein-0.9.5.aar.sha1 │ │ ├── Laevatein-0.9.5.pom │ │ ├── Laevatein-0.9.5.pom.md5 │ │ └── Laevatein-0.9.5.pom.sha1 │ ├── 0.9.6/ │ │ ├── Laevatein-0.9.6.aar │ │ ├── Laevatein-0.9.6.aar.md5 │ │ ├── Laevatein-0.9.6.aar.sha1 │ │ ├── Laevatein-0.9.6.pom │ │ ├── Laevatein-0.9.6.pom.md5 │ │ └── Laevatein-0.9.6.pom.sha1 │ ├── 0.9.7/ │ │ ├── Laevatein-0.9.7.aar │ │ ├── Laevatein-0.9.7.aar.md5 │ │ ├── Laevatein-0.9.7.aar.sha1 │ │ ├── Laevatein-0.9.7.pom │ │ ├── Laevatein-0.9.7.pom.md5 │ │ └── Laevatein-0.9.7.pom.sha1 │ ├── 0.9.8/ │ │ ├── Laevatein-0.9.8.aar │ │ ├── Laevatein-0.9.8.aar.md5 │ │ ├── Laevatein-0.9.8.aar.sha1 │ │ ├── Laevatein-0.9.8.pom │ │ ├── Laevatein-0.9.8.pom.md5 │ │ └── Laevatein-0.9.8.pom.sha1 │ ├── 0.9.9/ │ │ ├── Laevatein-0.9.9.aar │ │ ├── Laevatein-0.9.9.aar.md5 │ │ ├── Laevatein-0.9.9.aar.sha1 │ │ ├── Laevatein-0.9.9.pom │ │ ├── Laevatein-0.9.9.pom.md5 │ │ └── Laevatein-0.9.9.pom.sha1 │ ├── maven-metadata.xml │ ├── maven-metadata.xml.md5 │ └── maven-metadata.xml.sha1 ├── sample/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.txt │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── com/ │ │ └── laevatein/ │ │ └── sample/ │ │ └── LSampleActivity.java │ └── res/ │ ├── drawable/ │ │ └── selector_background.xml │ ├── layout/ │ │ └── activity_sample.xml │ ├── values/ │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ ├── values-w820dp/ │ │ └── dimens.xml │ └── xml/ │ └── file_paths.xml └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # build app files *.apk *.ap_ # ignore crashlytics config com_crashlytics_export_strings.xml # Dalvik files *.dex # Java class files *.class # Generated files bin/ gen/ build/ lint.xml proguard/ # Local configs local.properties # roboelectric tmp/ # Eclipse workspace .metadata/ .checkstyle # intelli J .idea *.iml *.ipr *.iws # mac .Trashes .DS_Store # Win Thumbs.db # Vim *.swp #emacs *.*~ # Keystore keystore.properties # Gradle .gradle ================================================ FILE: .travis.yml ================================================ language: android jdk: oraclejdk8 env: global: - ANDROID_SDK=android-29 - ANDROID_BUILD_TOOLS_VERSION=28.0.3 - ANDROID_ABI=armeabi-v7a - ANDROID_EMULATOR_SDK=android-22 - ADB_INSTALL_TIMEOUT=10 android: components: - tools - $ANDROID_SDK - $ANDROID_EMULATOR_SDK - build-tools-$ANDROID_BUILD_TOOLS_VERSION - google-google_play_services - extra-google-m2repository - extra-android-m2repository - addon-google_apis-google-22 - sys-img-${ANDROID_ABI}-${ANDROID_EMULATOR_SDK} licenses: - 'android-sdk-preview-license-.+' - 'android-sdk-license-.+' - 'google-gdk-license-.+' before_cache: - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ cache: directories: - $HOME/.gradle/caches/ - $HOME/.gradle/wrapper/ - $HOME/.android/build-cache before_script: - echo no | android create avd --force -n test -t $ANDROID_EMULATOR_SDK --abi $ANDROID_ABI - emulator -avd test -no-skin -no-audio -no-window & - android-wait-for-emulator - adb shell input keyevent 82 & script: - ./gradlew clean build connectedAndroidTest -PdisablePreDex notifications: email: false ================================================ FILE: README.md ================================================ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ **WARNING: This library is no longer maintained.** ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ ⚠️ Thank you for using and contributing! 🙇 --- # Laevatein Photo image selection activity set library. ## Screen Shot ![Selection Activity](https://raw.githubusercontent.com/nohana/Laevatein/master/documents/ss-1.png) ![Album List on DrawerMenu](https://raw.githubusercontent.com/nohana/Laevatein/master/documents/ss-2.png) ![Selected List](https://raw.githubusercontent.com/nohana/Laevatein/master/documents/ss-3.png) ## Usage Call photo image selection activity by the following code snipet. ```java public class SomeActivity extends Activity { public static final int REQUEST_CODE_CHOOSE = 1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_some); } @Override protected void onActivityResult(int requestCode, int resultcode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case REQUEST_CODE_CHOOSE: if (resultCode == RESULT_OK) { // Get result and proceed your work from here... List selected = Laevatein.obtainResult(data); } break; default: break; } } public void onClickButton(View view) { // call chooser on click button like this Laevatein.from(this).choose(MimeType.of(MimeType.JPEG)).forResult(REQUEST_CODE_CHOOSE); } } ``` And you'll get the selection result on `Activity#onActivityResult(int, int, Intent)`. ## Features Laevatein provides some APIs to customize selector behaviour for your spec. ### Selectable count limitation Set selectable count with `count(int, int)`. Default is `0 <= count <= 1`. ```java Laevatein.from(this) .choose(MimeType.of(MimeType.JPEG)) .count(0, 10) // minimum = 0, max = 10, so 0 <= count <= 10; .forResult(REQUEST_CODE_CHOOSE); ``` ### Selectable photo quality limitation Set selectable photo quality by pixel count with `quality(int, int)`. Default is `0 <= pixels <= Integer.MAX_VALUE`. ```java Laevatein.from(this) .choose(MimeType.of(MimeType.JPEG)) .quality(30000, Integer.MAX_VALUE) // minimum = 30000px, max = Integer.MAX_VALUEpx, so 30000px <= count <= Integer.MAX_VALUEpx; .forResult(REQUEST_CODE_CHOOSE); ``` ### Selectable photo size limitation Set selectable photo size by pixel with `size(int, int)`. Default is `0 <= pixels <= Integer.MAX_VALUE`. ```java Laevatein.from(this) .choose(MimeType.of(MimeType.JPEG)) .size(300, 400) // minimum width = 300px, minimum height = 400px; .forResult(REQUEST_CODE_CHOOSE); ``` ### Selectable photo size limitation Set selectable photo size by pixel with `size(int, int, int, int)`. Default is `0 <= pixels <= Integer.MAX_VALUE`. ```java Laevatein.from(this) .choose(MimeType.of(MimeType.JPEG)) .size(300, 400, Integer.MAX_VALUE, Integer.MAX_VALUE) // minimum width = 300px, minimum height = 400px, max width = Integer.MAX_VALUEpx, max height = Integer.MAX_VALUEpx; .forResult(REQUEST_CODE_CHOOSE); ``` ### Use custom cell layout Set your layout and ids for the image cell with `bindEachImageWith(int, int, int)`. ```java Laevatein.from(this) .choose(MimeType.of(MimeType.JPEG)) .bindEachImageWith(R.layout.my_cell, R.id.my_cell_image_view, R.id.my_cell_check_box) .forResult(REQUEST_CODE_CHOOSE); ``` ### Resume selection with previously selected photos Set defaultly selected URIs with `resume(List)`. ```java List mSelectedList; Laevatein.from(this) .choose(MimeType.of(MimeType.JPEG)) .resume(mSelectedList) .forResult(REQUEST_CODE_CHOOSE); ``` ### Call camera from the selection activity Set flag to enable camera capture from the selection activity with `capture(boolean)`. ```java Laevatein.from(this) .choose(MimeType.of(MimeType.JPEG)) .capture(true) .forResult(REQUEST_CODE_CHOOSE); ``` ### Customize theme Change Laevatein's theme with `theme(int)`. ```java Laevatein.from(this) .theme(R.style.OriginalTheme) .choose(MimeType.of(MimeType.JPEG)) .forResult(REQUEST_CODE_CHOOSE); ``` Theme must implement these parameter at least. ```xml ``` ## Sample App Sample application is available [here](https://deploygate.com/distributions/b43dc74fc4025bbb8587f179f5b8464418cca559). [Try it on your device via DeployGate](https://dply.me/orf0t9#install) ## Download Via Gradle for Android Studio ~2.x ```groovy repositories { mavenCentral() } android { dependencies { compile 'jp.co.nohana:Laevatein:2.3.2@aar' } } ``` for Android Studio 3.x~ ```groovy repositories { mavenCentral() } android { dependencies { implementation 'jp.co.nohana:Laevatein:2.3.2' } } ``` ## Acknowledgement This library depends on the following libraries. 1. [Picasso](https://github.com/square/picasso) by Square Inc. 2. [ImageViewZoom](https://github.com/sephiroth74/ImageViewZoom) by Alessandro Crugnola 3. [Amalgam](https://github.com/nohana/Amalgam) by nohana, Inc. 4. [CompoundContainers](https://github.com/KeithYokoma/CompoundContainers) by KeithYokoma 5. [AndroidDeviceCompatibility](https://github.com/mixi-inc/Android-Device-Compatibility) by mixi, Inc. 6. [PermissionsDispatcher](https://github.com/hotchemi/PermissionsDispatcher) by Shintaro Katafuchi, Marcel Schnelle, Yoshinori Isogai ## License This library is licensed under Apache License v2. ``` Copyright (C) 2014 nohana, Inc. All rights reserved. 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. ``` [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/nohana/laevatein/trend.png)](https://bitdeli.com/free "Bitdeli Badge") ================================================ FILE: build.gradle ================================================ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.5.3' } } allprojects { repositories { google() jcenter() } } ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ #Thu Nov 30 10:11:36 JST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip ================================================ FILE: gradle.properties ================================================ # Project-wide Gradle settings. # IDE (e.g. Android Studio) users: # Settings specified in this file will override any Gradle settings # configured through the IDE. # For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. # Default value: -Xmx10248m -XX:MaxPermSize=256m # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true VERSION_NAME=3.1.0 VERSION_CODE=26 GROUP=jp.co.nohana POM_DESCRIPTION=Photo image selection activity set library. Currently under development. POM_URL=https://github.com/nohana/Laevatein POM_SCM_URL=https://github.com/nohana/Laevatein POM_SCM_CONNECTION=scm:git@github.com:nohana/Laevatein.git POM_SCM_DEV_CONNECTION=scm:git@github.com:nohana/Laevatein.git POM_LICENCE_NAME=The Apache Software License, Version 2.0 POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt POM_LICENCE_DIST=repo POM_DEVELOPER_ID=nohana POM_DEVELOPER_NAME=nohana,Inc. ANDROID_BUILD_TARGET_SDK_VERSION=29 ANDROID_BUILD_TOOLS_VERSION=29.0.3 ANDROID_BUILD_SDK_VERSION=29 android.useAndroidX=true android.enableJetifier=true ================================================ FILE: gradlew ================================================ #!/usr/bin/env bash ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn ( ) { echo "$*" } die ( ) { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; esac # For Cygwin, ensure paths are in UNIX format before anything is touched. if $cygwin ; then [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` fi # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >&- APP_HOME="`pwd -P`" cd "$SAVED" >&- CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules function splitJvmOpts() { JVM_OPTS=("$@") } eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" ================================================ FILE: gradlew.bat ================================================ @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windowz variants if not "%OS%" == "Windows_NT" goto win9xME_args if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* goto execute :4NT_args @rem Get arguments from the 4NT Shell from JP Software set CMD_LINE_ARGS=%$ :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: laevatein/.gitignore ================================================ /build ================================================ FILE: laevatein/build.gradle ================================================ apply plugin: 'com.android.library' android { compileSdkVersion 29 defaultConfig { minSdkVersion 14 targetSdkVersion 29 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } compileOptions { sourceCompatibility JavaVersion.VERSION_1_7 targetCompatibility JavaVersion.VERSION_1_7 } lintOptions { abortOnError false } testOptions.unitTests.includeAndroidResources true useLibrary 'android.test.mock' } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.annotation:annotation:1.1.0' implementation 'com.google.android.material:material:1.1.0' implementation 'androidx.exifinterface:exifinterface:1.1.0' implementation("com.github.bumptech.glide:glide:4.11.0") { exclude group: 'com.android.support', module: 'support-fragment' } annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0' implementation 'it.sephiroth.android.library.imagezoom:library:1.0.4' implementation 'jp.co.nohana:Amalgam:0.4.0@aar' // Robolectric testImplementation 'junit:junit:4.13' testImplementation 'androidx.test:core:1.2.0' testImplementation 'androidx.test:runner:1.2.0' testImplementation 'androidx.test:rules:1.2.0' testImplementation 'androidx.test.ext:junit:1.1.1' testImplementation 'junit:junit:4.13' testImplementation 'org.robolectric:robolectric:4.3' testImplementation 'org.mockito:mockito-all:1.10.19' androidTestImplementation 'androidx.test:runner:1.2.0' } apply from: 'https://raw.github.com/chrisbanes/gradle-mvn-push/master/gradle-mvn-push.gradle' afterEvaluate { androidJavadocs.failOnError false androidJavadocs.classpath += project.android.libraryVariants.toList().first().javaCompile.classpath } apply plugin: 'idea' idea { module { testOutputDir = file('build/test-classes/debug') } } ================================================ FILE: laevatein/gradle.properties ================================================ POM_NAME=Laevatein POM_ARTIFACT_ID=Laevatein POM_PACKAGING=aar ================================================ FILE: laevatein/proguard-rules.txt ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in /Library/android-sdk-macosx/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the ProGuard # include property in project.properties. # # 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.okhttp.** ================================================ FILE: laevatein/src/androidTest/java/com/laevatein/MimeTypeTest.java ================================================ package com.laevatein; import android.net.Uri; import android.test.mock.MockContentResolver; import org.junit.Test; import org.junit.runner.RunWith; import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @RunWith(AndroidJUnit4ClassRunner.class) public class MimeTypeTest { @Test public void typeCheck() throws Exception { MockContentResolver resolver = new MockContentResolver(); { assertTrue(MimeType.JPEG.checkType(resolver, Uri.parse("file://hogehoge/fugafuga/image.jpg"))); assertTrue(MimeType.JPEG.checkType(resolver, Uri.parse("file://hogehoge/fugafuga/image.jpeg"))); assertFalse(MimeType.JPEG.checkType(resolver, Uri.parse("file://hogehoge/fugafuga/image.png"))); assertFalse(MimeType.JPEG.checkType(resolver, Uri.parse("file://hogehoge/fugafuga/image.gif"))); assertFalse(MimeType.JPEG.checkType(resolver, null)); } { assertFalse(MimeType.PNG.checkType(resolver, Uri.parse("file://hogehoge/fugafuga/image.jpg"))); assertFalse(MimeType.PNG.checkType(resolver, Uri.parse("file://hogehoge/fugafuga/image.jpeg"))); assertTrue(MimeType.PNG.checkType(resolver, Uri.parse("file://hogehoge/fugafuga/image.png"))); assertFalse(MimeType.PNG.checkType(resolver, Uri.parse("file://hogehoge/fugafuga/image.gif"))); assertFalse(MimeType.PNG.checkType(resolver, null)); } { assertFalse(MimeType.GIF.checkType(resolver, Uri.parse("file://hogehoge/fugafuga/image.jpg"))); assertFalse(MimeType.GIF.checkType(resolver, Uri.parse("file://hogehoge/fugafuga/image.jpeg"))); assertFalse(MimeType.GIF.checkType(resolver, Uri.parse("file://hogehoge/fugafuga/image.png"))); assertTrue(MimeType.GIF.checkType(resolver, Uri.parse("file://hogehoge/fugafuga/image.gif"))); assertFalse(MimeType.GIF.checkType(resolver, null)); } } } ================================================ FILE: laevatein/src/main/AndroidManifest.xml ================================================ ================================================ FILE: laevatein/src/main/java/com/laevatein/Laevatein.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein; import android.app.Activity; import android.content.Intent; import android.net.Uri; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import com.laevatein.ui.PhotoSelectionActivity; import java.lang.ref.WeakReference; import java.util.List; import java.util.Set; /** * Photo selection activity set provider. * Use {@link #from(android.app.Activity)} to start selection activity with the specification you'd like. * And the result will be delivered to {@link Activity#onActivityResult(int, int, android.content.Intent)}, * and use {@link #obtainResult(android.content.Intent)} for the convenience of receiving the selection result. * * @author KeithYokoma * @version 1.0.0 * @since 2014/03/19 */ @SuppressWarnings("unused") // public APIs public final class Laevatein { public static final String TAG = Laevatein.class.getSimpleName(); private final WeakReference mContext; private final WeakReference mFragment; protected Laevatein(Activity context) { mContext = new WeakReference<>(context); mFragment = null; } protected Laevatein(Activity activity, Fragment fragment) { mContext = new WeakReference<>(activity); mFragment = new WeakReference<>(fragment); } /** * Starts selection from the specified {@link android.app.Activity}. * * @param activity to start. * @return the requester context wrapper. */ public static Laevatein from(Activity activity) { return new Laevatein(activity); } public static Laevatein from(Fragment fragment) { return new Laevatein(fragment.getActivity(), fragment); } /** * Obtains the selection result passed to your {@link Activity#onActivityResult(int, int, android.content.Intent)}. * * @param data the data. * @return the selected {@link android.net.Uri}s. */ public static List obtainResult(Intent data) { return data.getParcelableArrayListExtra(PhotoSelectionActivity.EXTRA_RESULT_SELECTION); } /** * Specifies the MIME Type to select. * * @param mimeType the mime type of the photo you would like to choose. * @return the specification builder context. */ public SelectionSpecBuilder choose(Set mimeType) { return new SelectionSpecBuilder(this, mimeType); } /** * @return the actual requester context. */ /* package */ @Nullable Activity getActivity() { return mContext.get(); } /** * @return the fragment that is responsible for result handling. */ /* package */ @Nullable Fragment getFragment() { return mFragment != null ? mFragment.get() : null; } } ================================================ FILE: laevatein/src/main/java/com/laevatein/MimeType.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein; import com.laevatein.internal.utils.PhotoMetadataUtils; import android.content.ContentResolver; import android.net.Uri; import android.webkit.MimeTypeMap; import java.util.EnumSet; import java.util.HashSet; import java.util.Locale; import java.util.Set; /** * MIME Type enumeration to restrict selectable photo on the selection activity. * @author KeithYokoma * @since 2014/03/19 * @version 1.0.0 */ @SuppressWarnings("unused") // public APIs public enum MimeType { JPEG("image/jpeg", new HashSet() {{ add("jpg"); add("jpeg"); }}), PNG("image/png", new HashSet() {{ add("png"); }}), GIF("image/gif", new HashSet() {{ add("gif"); }}); private final String mMimeTypeName; private final Set mExtensions; MimeType(String mimeTypeName, Set extensions) { mMimeTypeName = mimeTypeName; mExtensions = extensions; } public static Set allOf() { return EnumSet.allOf(MimeType.class); } public static Set of(MimeType type) { return EnumSet.of(type); } public static Set of(MimeType type, MimeType... rest) { return EnumSet.of(type, rest); } @Override public String toString() { return mMimeTypeName; } public boolean checkType(ContentResolver resolver, Uri uri) { MimeTypeMap map = MimeTypeMap.getSingleton(); if (uri == null) { return false; } String type = map.getExtensionFromMimeType(resolver.getType(uri)); for (String extension : mExtensions) { if (extension.equals(type)) { return true; } String path = PhotoMetadataUtils.getPath(resolver, uri); if (path != null && path.toLowerCase(Locale.US).endsWith(extension)) { return true; } } return false; } } ================================================ FILE: laevatein/src/main/java/com/laevatein/SelectionSpecBuilder.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein; import android.app.Activity; import android.content.Intent; import android.net.Uri; import androidx.annotation.StringRes; import androidx.annotation.StyleRes; import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityOptionsCompat; import androidx.fragment.app.Fragment; import android.view.View; import com.laevatein.internal.entity.CaptureResources; import com.laevatein.internal.entity.CounterViewResources; import com.laevatein.internal.entity.DialogResources; import com.laevatein.internal.entity.ErrorViewResources; import com.laevatein.internal.entity.ErrorViewSpec; import com.laevatein.internal.entity.ItemViewResources; import com.laevatein.internal.entity.PreviewViewResources; import com.laevatein.internal.entity.SelectionSpec; import com.laevatein.internal.entity.ViewResourceSpec; import com.laevatein.ui.ImagePreviewActivity; import com.laevatein.ui.PhotoSelectionActivity; import java.util.ArrayList; import java.util.List; import java.util.Set; /** * Fluent API for building photo select specification. * * @author KeithYokoma * @version 1.0.0 * @since 2014/03/19 */ @SuppressWarnings("unused") // public APIs public final class SelectionSpecBuilder { public static final String TAG = SelectionSpecBuilder.class.getSimpleName(); private final Laevatein mLaevatein; private final Set mMimeType; private final SelectionSpec mSelectionSpec; @StyleRes private int mActivityTheme; private ItemViewResources mItemViewResources; private CounterViewResources mCounterViewResources; private PreviewViewResources mPreviewViewResources; private ErrorViewResources mCountUnderErrorSpec; private ErrorViewResources mCountOverErrorSpec; private ErrorViewResources mUnderQualityErrorSpec; private ErrorViewResources mOverQualityErrorSpec; private ErrorViewResources mUnderSizeErrorSpec; private ErrorViewResources mOverSizeErrorSpec; private ErrorViewResources mTypeErrorSpec; private DialogResources mConfirmDialogSpec; private boolean mOpenDrawer = true; private String mFileProviderAuthorities; private boolean mEnableSelectedView; private int mActivityOrientation; private List mResumeList; private Class mPhotoSelectionActivityClass; private Class mPreviewActivityClass = ImagePreviewActivity.class; /** * Constructs a new specification builder on the context. * @param laevatein a requester context wrapper. * @param mimeType MimeType set to select. */ /* package */ SelectionSpecBuilder(Laevatein laevatein, Set mimeType) { mLaevatein = laevatein; mMimeType = mimeType; mSelectionSpec = new SelectionSpec(); mResumeList = new ArrayList<>(); mActivityOrientation = -1; } /** * Sets the theme of activities. * * @param theme theme of activity * @return the specification builder context. */ public SelectionSpecBuilder theme(@StyleRes int theme) { mActivityTheme = theme; return this; } /** * Sets the binding cell of the grid view with the specified layout resource for photo list. * @param layoutId a layout resource id. * @param imageViewId an id for the image view. * @param checkBoxId an id for the check box. * @param spanCount an span count for GridLayout. */ public SelectionSpecBuilder bindEachImageWith(int layoutId, int imageViewId, int checkBoxId, int spanCount) { mItemViewResources = new ItemViewResources(layoutId, imageViewId, checkBoxId, spanCount); return this; } /** * Sets counter view style of photo selection. * * @param viewPosition the position where counter view layouted * @param textId counter text id. * @return the specification builder context. */ public SelectionSpecBuilder counterView(@CounterViewResources.VIEW_POSITION int viewPosition, @StringRes int textId) { mCounterViewResources = new CounterViewResources(viewPosition, textId); return this; } /** * Sets the binding page of the ViewPager with the specified layout resource for photo list. * * @param layoutId a layout resource id. * @param imageViewId an id for the image view. * @return the specification builder context. */ public SelectionSpecBuilder bindPreviewImageWith(int layoutId, int imageViewId) { mPreviewViewResources = new PreviewViewResources(layoutId, imageViewId); return this; } /** * Sets the limitation of a selectable count within the specified range. * @param min minimum value to select. * @param max maximum value to select. * @return the specification builder context. */ public SelectionSpecBuilder count(int min, int max) { mSelectionSpec.setMinSelectable(min); mSelectionSpec.setMaxSelectable(max); return this; } /** * Sets the error view specification for the error of count over. * @param type error view type. * @param errorMessageId an error message resource id. * @return the specification builder context. */ public SelectionSpecBuilder countUnder(ErrorViewResources.ViewType type, int errorMessageId) { return countUnder(type, -1, errorMessageId); } /** * Sets the error view specification for the error of count over. * @param type error view type. * @param errorTitleId an error title resource id. If type is not {@see ErrorViewResources.ViewType.DIALOG}, this parameter is ignored. * @param errorMessageId an error message resource id. * @return the specification builder context. */ public SelectionSpecBuilder countUnder(ErrorViewResources.ViewType type, int errorTitleId, int errorMessageId) { mCountUnderErrorSpec = type.createSpec(errorTitleId, errorMessageId); return this; } /** * Sets the error view specification for the error of count over. * @param type error view type. * @param errorMessageId an error message resource id. * @return the specification builder context. */ public SelectionSpecBuilder countOver(ErrorViewResources.ViewType type, int errorMessageId) { return countOver(type, -1, errorMessageId); } /** * Sets the error view specification for the error of count over. * @param type error view type. * @param errorTitleId an error title resource id. If type is not {@see ErrorViewResources.ViewType.DIALOG}, this parameter is ignored. * @param errorMessageId an error message resource id. * @return the specification builder context. */ public SelectionSpecBuilder countOver(ErrorViewResources.ViewType type, int errorTitleId, int errorMessageId) { mCountOverErrorSpec = type.createSpec(errorTitleId, errorMessageId); return this; } /** * Sets the flag to determine whether the list of which image has been selected should be shown or not. * The flag is set as false by default. * @param enableSelectedView the flag of visibility. * @return the specification builder context. */ public SelectionSpecBuilder enableSelectedView(boolean enableSelectedView) { mEnableSelectedView = enableSelectedView; return this; } /** * Sets the error view specification for the error of quality un-satisfaction. * @param type error view type. * @param errorMessageId an error message resource id. * @return the specification builder context. */ public SelectionSpecBuilder underQuality(ErrorViewResources.ViewType type, int errorMessageId) { return underQuality(type, -1, errorMessageId); } /** * Sets the error view specification for the error of quality un-satisfaction. * @param type error view type. * @param errorTitleId an error title resource id. If type is not {@see ErrorViewResources.ViewType.DIALOG}, this parameter is ignored. * @param errorMessageId an error message resource id. * @return the specification builder context. */ public SelectionSpecBuilder underQuality(ErrorViewResources.ViewType type, int errorTitleId, int errorMessageId) { mUnderQualityErrorSpec = type.createSpec(errorTitleId, errorMessageId); return this; } /** * Sets the error view specification for the error of quality un-satisfaction.. * @param type error view type. * @param errorMessageId an error message resource id. * @return the specification builder context. */ public SelectionSpecBuilder overQuality(ErrorViewResources.ViewType type, int errorMessageId) { return overQuality(type, -1, errorMessageId); } /** * Sets the error view specification for the error of quality un-satisfaction.. * @param type error view type. * @param errorTitleId an error title resource id. If type is not {@see ErrorViewResources.ViewType.DIALOG}, this parameter is ignored. * @param errorMessageId an error message resource id. * @return the specification builder context. */ public SelectionSpecBuilder overQuality(ErrorViewResources.ViewType type, int errorTitleId, int errorMessageId) { mOverQualityErrorSpec = type.createSpec(errorTitleId, errorMessageId); return this; } /** * Sets the error view specification for the error of size un-satisfaction. * @param type error view type. * @param errorMessageId an error message resource id. * @return the specification builder context. */ public SelectionSpecBuilder underSize(ErrorViewResources.ViewType type, int errorMessageId) { return underSize(type, -1, errorMessageId); } /** * Sets the error view specification for the error of size un-satisfaction. * @param type error view type. * @param errorTitleId an error title resource id. If type is not {@see ErrorViewResources.ViewType.DIALOG}, this parameter is ignored. * @param errorMessageId an error message resource id. * @return the specification builder context. */ public SelectionSpecBuilder underSize(ErrorViewResources.ViewType type, int errorTitleId, int errorMessageId) { mUnderSizeErrorSpec = type.createSpec(errorTitleId, errorMessageId); return this; } /** * Sets the error view specification for the error of size un-satisfaction.. * @param type error view type. * @param errorMessageId an error message resource id. * @return the specification builder context. */ public SelectionSpecBuilder overSize(ErrorViewResources.ViewType type, int errorMessageId) { return overSize(type, -1, errorMessageId); } /** * Sets the error view specification for the error of size un-satisfaction.. * @param type error view type. * @param errorTitleId an error title resource id. If type is not {@see ErrorViewResources.ViewType.DIALOG}, this parameter is ignored. * @param errorMessageId an error message resource id. * @return the specification builder context. */ public SelectionSpecBuilder overSize(ErrorViewResources.ViewType type, int errorTitleId, int errorMessageId) { mOverSizeErrorSpec = type.createSpec(errorTitleId, errorMessageId); return this; } /** * Sets the error view specification for the error of type validation. * @param type error view type. * @param errorMessageId an error message resource id. * @return the specification builder context. */ public SelectionSpecBuilder invalidType(ErrorViewResources.ViewType type, int errorMessageId) { return invalidType(type, -1, errorMessageId); } /** * Sets the error view specification for the error of type validation. * @param type error view type. * @param errorTitleId an error title resource id. If type is not {@see ErrorViewResources.ViewType.DIALOG}, this parameter is ignored. * @param errorMessageId an error message resource id. * @return the specification builder context. */ public SelectionSpecBuilder invalidType(ErrorViewResources.ViewType type, int errorTitleId, int errorMessageId) { mTypeErrorSpec = type.createSpec(errorTitleId, errorMessageId); return this; } /** * Sets the error view specification for the press back without finish. * @param errorMessageId an error message resource id. * @return the specification builder context. */ public SelectionSpecBuilder confirmDialog(int errorMessageId) { return confirmDialog(-1, errorMessageId); } /** * Sets the error view specification for the press back without finish. * @param errorTitleId an error title resource id. If type is not {@see ErrorViewResources.ViewType.DIALOG}, this parameter is ignored. * @param errorMessageId an error message resource id. * @return the specification builder context. */ public SelectionSpecBuilder confirmDialog(int errorTitleId, int errorMessageId) { mConfirmDialogSpec = new DialogResources(errorTitleId, errorMessageId); return this; } /** * Sets the limitation of a selectable image quality by pixel count within the specified range. * @param minPixel minimum value to select. * @param maxPixel maximum value to select. * @return the specification builder context. */ public SelectionSpecBuilder quality(int minPixel, int maxPixel) { mSelectionSpec.setMinPixels(minPixel); mSelectionSpec.setMaxPixels(maxPixel); return this; } /** * Sets the limitation of a selectable image size by pixel count minimum. * * @param minWidth minimum width value to select. * @param minHeight minimum width value to select. * @return the specification builder context. */ public SelectionSpecBuilder size(int minWidth, int minHeight) { size(minWidth, minHeight, Integer.MAX_VALUE, Integer.MAX_VALUE); return this; } /** * Sets the limitation of a selectable image size by pixel count minimum and maximum. * * @param minWidth minimum width value to select. * @param minHeight minimum width value to select. * @param maxWidth maximum height value to select. * @param maxHeight maximum height value to select. * @return the specification builder context. */ public SelectionSpecBuilder size(int minWidth, int minHeight, int maxWidth, int maxHeight) { mSelectionSpec.setMinSize(minWidth, minHeight); mSelectionSpec.setMaxSize(maxWidth, maxHeight); return this; } /** * Sets the default selected album. * * @param albumId default selected album. * @return the specification builder context. */ public SelectionSpecBuilder albumId(String albumId) { mSelectionSpec.setDefaultAlbumId(albumId); return this; } /** * Sets the Activity instead of PhotoSelectionActivity * @param photoSelectionActivityClass an Activity called on photo selecting * @return the specification builder context. */ public SelectionSpecBuilder photoSelectionActivityClass(Class photoSelectionActivityClass) { mPhotoSelectionActivityClass = photoSelectionActivityClass; return this; } /** * Sets the Activity instead of ImagePreviewActivity * * @param previewActivityClass an Activity called on photo preview * @return the specification builder context. */ public SelectionSpecBuilder previewActivityClass(Class previewActivityClass) { mPreviewActivityClass = previewActivityClass; return this; } /** * Sets the default selection to resume photo picking activity. * @param uriList to set selected as default. * @return the specification builder context. */ public SelectionSpecBuilder resume(List uriList) { if (uriList == null) { // nothing to do. return this; } mResumeList.addAll(uriList); return this; } /** * Determines whether opening NavigationDrawer on launch or not. * This flag is true by default. * * @param open whether to open drawer or not. * @return the specification builder context. */ public SelectionSpecBuilder openDrawer(boolean open) { mOpenDrawer = open; return this; } /** * Determines whether the photo capturing is enabled or not on the camera photo grid view. * This flag is false by default. * * @param fileProviderAuthorities your FileProvider authorities name. * @return the specification builder context. */ public SelectionSpecBuilder enableCapture(String fileProviderAuthorities) { mFileProviderAuthorities = fileProviderAuthorities; return this; } /** * Determines whether the photo capturing is enabled or not on the camera photo grid view. * This flag is false by default. * * @return the specification builder context. */ public SelectionSpecBuilder disableCapture() { mFileProviderAuthorities = null; return this; } public SelectionSpecBuilder restrictOrientation(int activityOrientation) { mActivityOrientation = activityOrientation; return this; } /** * Start to select photo. * @param requestCode identity of the requester activity. */ public void forResult(int requestCode) { forResultWithTransition(requestCode, null); } /** * Start to select photo. * * @param requestCode identity of the requester activity. * @param view transition source view */ public void forResultWithTransition(int requestCode, View view) { Activity activity = mLaevatein.getActivity(); if (activity == null) { return; // cannot continue; } mSelectionSpec.setMimeTypeSet(mMimeType); if (mActivityTheme == 0) { mActivityTheme = R.style.L_DefaultTheme; } ViewResourceSpec viewSpec = new ViewResourceSpec.Builder() .setTheme(mActivityTheme) .setPreviewClass(mPreviewActivityClass) .setItemViewResources(mItemViewResources) .setCounterViewResources(mCounterViewResources) .setPreviewViewResources(mPreviewViewResources) .setOpenDrawer(mOpenDrawer) .setCaptureResources(new CaptureResources(mFileProviderAuthorities)) .setEnableSelectedView(mEnableSelectedView) .setActivityOrientation(mActivityOrientation) .create(); ErrorViewSpec errorSpec = new ErrorViewSpec.Builder() .setCountUnderSpec(mCountUnderErrorSpec) .setCountOverSpec(mCountOverErrorSpec) .setOverQualitySpec(mOverQualityErrorSpec) .setUnderQualitySpec(mUnderQualityErrorSpec) .setOverSizeSpec(mOverSizeErrorSpec) .setUnderSizeSpec(mUnderSizeErrorSpec) .setTypeSpec(mTypeErrorSpec) .setConfirmSpec(mConfirmDialogSpec) .create(); if (mPhotoSelectionActivityClass == null) { mPhotoSelectionActivityClass = PhotoSelectionActivity.class; } Intent intent = new Intent(activity, mPhotoSelectionActivityClass); intent.putExtra(PhotoSelectionActivity.EXTRA_VIEW_SPEC, viewSpec); intent.putExtra(PhotoSelectionActivity.EXTRA_ERROR_SPEC, errorSpec); intent.putExtra(PhotoSelectionActivity.EXTRA_SELECTION_SPEC, mSelectionSpec); intent.putParcelableArrayListExtra(PhotoSelectionActivity.EXTRA_RESUME_LIST, (ArrayList) mResumeList); Fragment fragment = mLaevatein.getFragment(); if (fragment != null) { fragment.startActivityForResult(intent, requestCode); } else if (view != null) { ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(activity, view, activity.getString(R.string.l_content_transition_name)); ActivityCompat.startActivityForResult(activity, intent, requestCode, options.toBundle()); } else { activity.startActivityForResult(intent, requestCode); } } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/entity/Album.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.entity; import com.laevatein.R; import android.content.Context; import android.database.Cursor; import android.os.Parcel; import android.os.Parcelable; import android.provider.MediaStore; import androidx.annotation.Nullable; /** * @author KeithYokoma * @since 2014/03/20 * @version 1.0.0 * @hide */ public class Album implements Parcelable { public static final Creator CREATOR = new Creator() { @Nullable @Override public Album createFromParcel(Parcel source) { return new Album(source); } @Override public Album[] newArray(int size) { return new Album[size]; } }; public static final String ALBUM_ID_ALL = String.valueOf(-1); public static final String ALBUM_NAME_ALL = "All"; public static final String ALBUM_NAME_CAMERA = "Camera"; public static final String ALBUM_NAME_DOWNLOAD = "Download"; public static final String ALBUM_NAME_SCREEN_SHOT = "Screenshots"; private final String mId; private final long mCoverId; private final String mDisplayName; /* package */ Album(String id, long coverId, String albumName) { mId = id; mCoverId = coverId; mDisplayName = albumName; } /* package */ Album(Parcel source) { mId = source.readString(); mCoverId = source.readLong(); mDisplayName = source.readString(); } /** * Constructs a new {@link com.laevatein.internal.entity.Album} entity from the {@link android.database.Cursor}. * This method is not responsible for managing cursor resource, such as close, iterate, and so on. * @param cursor to be converted. * @return a new {@link com.laevatein.internal.entity.Album}. */ public static Album valueOf(Cursor cursor) { return new Album( cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_ID)), cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media._ID)), cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_DISPLAY_NAME))); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mId); dest.writeLong(mCoverId); dest.writeString(mDisplayName); } public String getId() { return mId; } public long getCoverId() { return mCoverId; } public String getDisplayName(Context context) { if (isAll()) { return context.getString(R.string.l_album_name_all); } if (isCamera()) { return context.getString(R.string.l_album_name_camera); } if (ALBUM_NAME_DOWNLOAD.equals(mDisplayName)) { return context.getString(R.string.l_album_name_download); } if (ALBUM_NAME_SCREEN_SHOT.equals(mDisplayName)) { return context.getString(R.string.l_album_name_screen_shot); } return mDisplayName; } public boolean isAll() { return ALBUM_ID_ALL.equals(mId); } public boolean isCamera() { return ALBUM_NAME_CAMERA.equals(mDisplayName); } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/entity/CaptureResources.java ================================================ package com.laevatein.internal.entity; import android.os.Parcel; import android.os.Parcelable; import androidx.annotation.Nullable; public class CaptureResources implements Parcelable { public static final Creator CREATOR = new Creator() { @Override public CaptureResources createFromParcel(Parcel in) { return new CaptureResources(in); } @Override public CaptureResources[] newArray(int size) { return new CaptureResources[size]; } }; @Nullable private final String mFileProviderAuthorities; public CaptureResources(@Nullable String fileProviderAuthorities) { mFileProviderAuthorities = fileProviderAuthorities; } private CaptureResources(Parcel in) { mFileProviderAuthorities = in.readString(); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mFileProviderAuthorities); } @Override public int describeContents() { return 0; } public boolean enabled() { return mFileProviderAuthorities != null; } @Nullable public String getFileProviderAuthorities() { return mFileProviderAuthorities; } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/entity/CounterViewResources.java ================================================ package com.laevatein.internal.entity; import android.os.Parcel; import android.os.Parcelable; import androidx.annotation.IntDef; import androidx.annotation.StringRes; import com.laevatein.R; /** * @author keishin.yokomaku * @since 2014/04/07 */ public class CounterViewResources implements Parcelable { public static final Creator CREATOR = new Creator() { @Override public CounterViewResources createFromParcel(Parcel source) { return new CounterViewResources(source); } @Override public CounterViewResources[] newArray(int size) { return new CounterViewResources[size]; } }; @IntDef({LAYOUT_APP_BAR, LAYOUT_BOTTOM}) public @interface VIEW_POSITION { } public static final int LAYOUT_APP_BAR = 1; public static final int LAYOUT_BOTTOM = 2; private static volatile CounterViewResources sDefault; @VIEW_POSITION private final int mViewPosition; @StringRes private final int mTextId; /* package */ CounterViewResources(Parcel source) { mViewPosition = readViewPosition(source); mTextId = source.readInt(); } public CounterViewResources(@VIEW_POSITION int viewPosition, @StringRes int textId) { mViewPosition = viewPosition; mTextId = textId; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mViewPosition); dest.writeInt(mTextId); } @VIEW_POSITION public int getViewType() { return mViewPosition; } @StringRes public int getTextRes(){ return mTextId; } public static CounterViewResources getDefault() { if (sDefault == null) { sDefault = new CounterViewResources(LAYOUT_BOTTOM, R.string.l_format_selection_count); } return sDefault; } @VIEW_POSITION private int readViewPosition(Parcel source) { int result = source.readInt(); switch (result) { case LAYOUT_APP_BAR: return LAYOUT_APP_BAR; case LAYOUT_BOTTOM: return LAYOUT_BOTTOM; default: throw new IllegalArgumentException(); } } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/entity/DialogResources.java ================================================ package com.laevatein.internal.entity; import android.os.Parcel; import android.os.Parcelable; /** * @author hiroyuki.setp * @since 2017/01/11 */ public class DialogResources implements Parcelable { public static final Creator CREATOR = new Creator() { @Override public DialogResources createFromParcel(Parcel source) { return new DialogResources(source); } @Override public DialogResources[] newArray(int size) { return new DialogResources[size]; } }; private final int mTitleId; private final int mMessageId; /* package */ DialogResources(Parcel source) { mTitleId = source.readInt(); mMessageId = source.readInt(); } public DialogResources(int messageId) { this(-1, messageId); } public DialogResources(int titleId, int messageId) { mTitleId = titleId; mMessageId = messageId; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mTitleId); dest.writeInt(mMessageId); } public int getTitleId() { return mTitleId; } public int getMessageId() { return mMessageId; } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/entity/ErrorViewResources.java ================================================ package com.laevatein.internal.entity; import android.os.Parcel; import android.os.Parcelable; /** * @author keishin.yokomaku * @since 2014/04/07 */ public class ErrorViewResources implements Parcelable { public static final Creator CREATOR = new Creator() { @Override public ErrorViewResources createFromParcel(Parcel source) { return new ErrorViewResources(source); } @Override public ErrorViewResources[] newArray(int size) { return new ErrorViewResources[size]; } }; private final ViewType mViewType; private final int mTitleId; private final int mMessageId; /* package */ ErrorViewResources(Parcel source) { mViewType = (ViewType) source.readSerializable(); mTitleId = source.readInt(); mMessageId = source.readInt(); } /* package */ ErrorViewResources(ViewType viewType, int messageId) { this(viewType, -1, messageId); } /* package */ ErrorViewResources(ViewType viewType, int titleId, int messageId) { mViewType = viewType; mTitleId = titleId; mMessageId = messageId; } /* package */ static ErrorViewResources asNoView() { return new ErrorViewResources(ViewType.NONE, 0); } /* package */ static ErrorViewResources asAlertDialog(int titleId, int messageId) { return new ErrorViewResources(ViewType.DIALOG, titleId, messageId); } /* package */ static ErrorViewResources asToast(int messageId) { return new ErrorViewResources(ViewType.TOAST, messageId); } /* package */ static ErrorViewResources asSnackbar(int messageId) { return new ErrorViewResources(ViewType.SNACKBAR, messageId); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeSerializable(mViewType); dest.writeInt(mTitleId); dest.writeInt(mMessageId); } public ViewType getViewType() { return mViewType; } public int getTitleId() { return mTitleId; } public int getMessageId() { return mMessageId; } public boolean isNoView() { return mViewType == ViewType.NONE; } public enum ViewType { TOAST { @Override public ErrorViewResources createSpec(int titleId, int messageId) { return ErrorViewResources.asToast(messageId); } }, SNACKBAR { @Override public ErrorViewResources createSpec(int titleId, int messageId) { return ErrorViewResources.asSnackbar(messageId); } }, DIALOG { @Override public ErrorViewResources createSpec(int titleId, int messageId) { return ErrorViewResources.asAlertDialog(titleId, messageId); } }, NONE { @Override public ErrorViewResources createSpec(int titleId, int messageId) { return ErrorViewResources.asNoView(); } }; public abstract ErrorViewResources createSpec(int titleId, int messageId); } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/entity/ErrorViewSpec.java ================================================ package com.laevatein.internal.entity; import android.os.Parcel; import android.os.Parcelable; import androidx.annotation.Nullable; import com.laevatein.R; /** * @author Keithyokoma * @since 2014/04/07 * @version 1.0.0 * @hide */ public class ErrorViewSpec implements Parcelable { public static final Creator CREATOR = new Creator() { @Override @Nullable public ErrorViewSpec createFromParcel(Parcel source) { return new ErrorViewSpec(source); } @Override public ErrorViewSpec[] newArray(int size) { return new ErrorViewSpec[size]; } }; private final ErrorViewResources mCountUnderErrorSpec; private final ErrorViewResources mCountOverErrorSpec; private final ErrorViewResources mUnderQualitySpec; private final ErrorViewResources mOverQualitySpec; private final ErrorViewResources mUnderSizeSpec; private final ErrorViewResources mOverSizeSpec; private final ErrorViewResources mTypeErrorSpec; private final DialogResources mBackConfirmSpec; /* package */ ErrorViewSpec(Parcel source) { mCountUnderErrorSpec = source.readParcelable(ErrorViewResources.class.getClassLoader()); mCountOverErrorSpec = source.readParcelable(ErrorViewResources.class.getClassLoader()); mUnderQualitySpec = source.readParcelable(ErrorViewResources.class.getClassLoader()); mOverQualitySpec = source.readParcelable(ErrorViewResources.class.getClassLoader()); mUnderSizeSpec = source.readParcelable(ErrorViewResources.class.getClassLoader()); mOverSizeSpec = source.readParcelable(ErrorViewResources.class.getClassLoader()); mTypeErrorSpec = source.readParcelable(ErrorViewResources.class.getClassLoader()); mBackConfirmSpec = source.readParcelable(DialogResources.class.getClassLoader()); } /* package */ ErrorViewSpec(ErrorViewResources countUnderSpec, ErrorViewResources countOverSpec, ErrorViewResources underQualitySpec, ErrorViewResources overQualitySpec, ErrorViewResources underSizeSpec, ErrorViewResources overSizeSpec, ErrorViewResources typeErrorSpec, DialogResources backConfirmSpec) { mCountUnderErrorSpec = countUnderSpec; mCountOverErrorSpec = countOverSpec; mUnderQualitySpec = underQualitySpec; mOverQualitySpec = overQualitySpec; mUnderSizeSpec = underSizeSpec; mOverSizeSpec = overSizeSpec; mTypeErrorSpec = typeErrorSpec; mBackConfirmSpec = backConfirmSpec; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeParcelable(mCountUnderErrorSpec, flags); dest.writeParcelable(mCountOverErrorSpec, flags); dest.writeParcelable(mUnderQualitySpec, flags); dest.writeParcelable(mOverQualitySpec, flags); dest.writeParcelable(mUnderSizeSpec, flags); dest.writeParcelable(mOverSizeSpec, flags); dest.writeParcelable(mTypeErrorSpec, flags); dest.writeParcelable(mBackConfirmSpec, flags); } public ErrorViewResources getCountUnderErrorSpec() { return mCountUnderErrorSpec; } public ErrorViewResources getCountOverErrorSpec() { return mCountOverErrorSpec; } public ErrorViewResources getUnderQualitySpec() { return mUnderQualitySpec; } public ErrorViewResources getOverQualitySpec() { return mOverQualitySpec; } public ErrorViewResources getUnderSizeSpec() { return mUnderSizeSpec; } public ErrorViewResources getOverSizeSpec() { return mOverSizeSpec; } public ErrorViewResources getTypeErrorSpec() { return mTypeErrorSpec; } public DialogResources getBackConfirmSpec() { return mBackConfirmSpec; } public static class Builder { private ErrorViewResources mCountUnderSpec; private ErrorViewResources mCountOverSpec; private ErrorViewResources mUnderQualitySpec; private ErrorViewResources mOverQualitySpec; private ErrorViewResources mUnderSizeSpec; private ErrorViewResources mOverSizeSpec; private ErrorViewResources mTypeSpec; private DialogResources mBackSpec; public Builder setCountUnderSpec(ErrorViewResources spec) { mCountUnderSpec = spec; return this; } public Builder setCountOverSpec(ErrorViewResources spec) { mCountOverSpec = spec; return this; } public Builder setUnderQualitySpec(ErrorViewResources spec) { mUnderQualitySpec = spec; return this; } public Builder setOverQualitySpec(ErrorViewResources spec) { mOverQualitySpec = spec; return this; } public Builder setUnderSizeSpec(ErrorViewResources spec) { mUnderSizeSpec = spec; return this; } public Builder setOverSizeSpec(ErrorViewResources spec) { mOverSizeSpec = spec; return this; } public Builder setTypeSpec(ErrorViewResources spec) { mTypeSpec = spec; return this; } public Builder setConfirmSpec(DialogResources spec) { mBackSpec = spec; return this; } public ErrorViewSpec create() { if (mCountUnderSpec == null) { mCountUnderSpec = ErrorViewResources.ViewType.NONE.createSpec(0, 0); } if (mCountOverSpec == null) { mCountOverSpec = ErrorViewResources.ViewType.NONE.createSpec(0, 0); } if (mUnderQualitySpec == null) { mUnderQualitySpec = ErrorViewResources.ViewType.DIALOG.createSpec(-1, R.string.l_error_quality); } if (mOverQualitySpec == null) { mOverQualitySpec = ErrorViewResources.ViewType.DIALOG.createSpec(-1, R.string.l_error_quality); } if (mUnderSizeSpec == null) { mUnderSizeSpec = ErrorViewResources.ViewType.DIALOG.createSpec(-1, R.string.l_error_size); } if (mOverSizeSpec == null) { mOverSizeSpec = ErrorViewResources.ViewType.DIALOG.createSpec(-1, R.string.l_error_size); } if (mTypeSpec == null) { mTypeSpec = ErrorViewResources.ViewType.DIALOG.createSpec(-1, R.string.l_error_invalid_format); } if (mBackSpec == null) { mBackSpec = new DialogResources(R.string.l_confirm_dialog_title, R.string.l_confirm_dialog_message); } return new ErrorViewSpec(mCountUnderSpec, mCountOverSpec, mUnderQualitySpec, mOverQualitySpec, mUnderSizeSpec, mOverSizeSpec, mTypeSpec, mBackSpec); } } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/entity/Item.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.entity; import android.content.ContentUris; import android.database.Cursor; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; import android.provider.MediaStore; import androidx.annotation.Nullable; /** * @author KeithYokoma * @since 2014/03/24 * @version 1.0.0 * @hide */ public class Item implements Parcelable { public static final Creator CREATOR = new Creator() { @Override @Nullable public Item createFromParcel(Parcel source) { return new Item(source); } @Override public Item[] newArray(int size) { return new Item[size]; } }; public static final long ITEM_ID_CAPTURE = -1; public static final String ITEM_DISPLAY_NAME_CAPTURE = "Capture"; private final long mId; /* package */ Item(long id) { mId = id; } /* package */ Item(Parcel source) { mId = source.readLong(); } public static Item valueOf(Cursor cursor) { return new Item(cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media._ID))); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeLong(mId); } public long getId() { return mId; } public Uri buildContentUri() { return ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mId); } public boolean isCapture() { return mId == ITEM_ID_CAPTURE; } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/entity/ItemViewResources.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.entity; import android.os.Parcel; import android.os.Parcelable; import com.laevatein.R; /** * @author KeithYokoma * @version 1.0.0 * @hide * @since 2014/03/20 */ public final class ItemViewResources implements Parcelable { private static final int DEFAULT_SPAN_COUNT = 4; public static final Creator CREATOR = new Creator() { @Override public ItemViewResources createFromParcel(Parcel source) { return new ItemViewResources(source); } @Override public ItemViewResources[] newArray(int size) { return new ItemViewResources[size]; } }; private static volatile ItemViewResources sDefault; private final int mLayoutId; private final int mImageViewId; private final int mCheckBoxId; private final int mSpanCount; public ItemViewResources(int layoutId, int imageViewId, int checkBoxId, int spanCount) { mLayoutId = layoutId; mImageViewId = imageViewId; mCheckBoxId = checkBoxId; mSpanCount = spanCount; } /* package */ ItemViewResources(Parcel source) { mLayoutId = source.readInt(); mImageViewId = source.readInt(); mCheckBoxId = source.readInt(); mSpanCount = source.readInt(); } public static ItemViewResources getDefault() { if (sDefault == null) { sDefault = new ItemViewResources(R.layout.l_grid_item_default_photo, R.id.l_default_grid_image, R.id.l_default_check_box, DEFAULT_SPAN_COUNT); } return sDefault; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mLayoutId); dest.writeInt(mImageViewId); dest.writeInt(mCheckBoxId); dest.writeInt(mSpanCount); } public int getLayoutId() { return mLayoutId; } public int getImageViewId() { return mImageViewId; } public int getCheckBoxId() { return mCheckBoxId; } public int getSpanCount() { return mSpanCount; } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/entity/PreviewViewResources.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.entity; import android.os.Parcel; import android.os.Parcelable; import com.laevatein.R; /** * @author HiroyukiSeto * @version 2.0.0 * @hide * @since 2016/11/25 */ public final class PreviewViewResources implements Parcelable { public static final Creator CREATOR = new Creator() { @Override public PreviewViewResources createFromParcel(Parcel source) { return new PreviewViewResources(source); } @Override public PreviewViewResources[] newArray(int size) { return new PreviewViewResources[size]; } }; private static volatile PreviewViewResources sDefault; private final int mLayoutId; private final int mImageViewId; public PreviewViewResources(int layoutId, int imageViewId) { mLayoutId = layoutId; mImageViewId = imageViewId; } /* package */ PreviewViewResources(Parcel source) { mLayoutId = source.readInt(); mImageViewId = source.readInt(); } public static PreviewViewResources getDefault() { if (sDefault == null) { sDefault = new PreviewViewResources(R.layout.l_fragment_default_preview, R.id.l_default_image_zoom_view); } return sDefault; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mLayoutId); dest.writeInt(mImageViewId); } public int getLayoutId() { return mLayoutId; } public int getImageViewId() { return mImageViewId; } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/entity/SelectionSpec.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.entity; import android.os.Parcel; import android.os.Parcelable; import com.laevatein.MimeType; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; import java.util.Set; /** * @author KeithYokoma * @since 2014/03/20 * @version 1.0.0 * @hide */ public final class SelectionSpec implements Parcelable { public static final Creator CREATOR = new Creator() { @Override public SelectionSpec createFromParcel(Parcel source) { return new SelectionSpec(source); } @Override public SelectionSpec[] newArray(int size) { return new SelectionSpec[size]; } }; private int mMaxSelectable; private int mMinSelectable; private long mMinPixels; private long mMaxPixels; private int mMinWidthPixels; private int mMinHeightPixels; private int mMaxWidthPixels; private int mMaxHeightPixels; private Set mMimeTypeSet; private String mDefaultAlbumId; public SelectionSpec() { mMinSelectable = 0; mMaxSelectable = 1; mMinPixels = 0L; mMaxPixels = Long.MAX_VALUE; mMinWidthPixels = 0; mMinHeightPixels = 0; mMaxWidthPixels = Integer.MAX_VALUE; mMaxHeightPixels = Integer.MAX_VALUE; mDefaultAlbumId = Album.ALBUM_ID_ALL; } /* package */ SelectionSpec(Parcel source) { mMinSelectable = source.readInt(); mMaxSelectable = source.readInt(); mMinPixels = source.readLong(); mMaxPixels = source.readLong(); mMinWidthPixels = source.readInt(); mMinHeightPixels = source.readInt(); mMaxWidthPixels = source.readInt(); mMaxHeightPixels = source.readInt(); List list = new ArrayList<>(); source.readList(list, MimeType.class.getClassLoader()); mMimeTypeSet = EnumSet.copyOf(list); mDefaultAlbumId = source.readString(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mMinSelectable); dest.writeInt(mMaxSelectable); dest.writeLong(mMinPixels); dest.writeLong(mMaxPixels); dest.writeInt(mMinWidthPixels); dest.writeInt(mMinHeightPixels); dest.writeInt(mMaxWidthPixels); dest.writeInt(mMaxHeightPixels); dest.writeList(new ArrayList<>(mMimeTypeSet)); dest.writeString(mDefaultAlbumId); } public void setMaxSelectable(int maxSelectable) { mMaxSelectable = maxSelectable; } public void setMinSelectable(int minSelectable) { mMinSelectable = minSelectable; } public void setMinPixels(long minPixels) { mMinPixels = minPixels; } public void setMaxPixels(long maxPixels) { mMaxPixels = maxPixels; } public void setMinSize(int minWidthPixels, int minHeightPixels) { this.mMinWidthPixels = minWidthPixels; this.mMinHeightPixels = minHeightPixels; } public void setMaxSize(int maxWidthPixels, int maxHeightPixels) { this.mMaxWidthPixels = maxWidthPixels; this.mMaxHeightPixels = maxHeightPixels; } public void setMimeTypeSet(Set set) { mMimeTypeSet = set; } public void setDefaultAlbumId(String albumId) { mDefaultAlbumId = albumId; } public int getMinSelectable() { return mMinSelectable; } public int getMaxSelectable() { return mMaxSelectable; } public long getMinPixels() { return mMinPixels; } public long getMaxPixels() { return mMaxPixels; } public int getMinWidthPixels() { return mMinWidthPixels; } public int getMinHeightPixels() { return mMinHeightPixels; } public int getMaxWidthPixels() { return mMaxWidthPixels; } public int getMaxHeightPixels() { return mMaxHeightPixels; } public Set getMimeTypeSet() { return mMimeTypeSet; } public String getDefaultAlbumId() { return mDefaultAlbumId; } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/entity/UncapableCause.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.entity; /** * @author KeithYokoma * @since 2014/03/25 * @version 1.0.0 * @hide */ public enum UncapableCause { OVER_COUNT { @Override public ErrorViewResources getErrorResources(ErrorViewSpec spec) { return spec.getCountOverErrorSpec(); } }, UNDER_QUALITY { @Override public ErrorViewResources getErrorResources(ErrorViewSpec spec) { return spec.getUnderQualitySpec(); } }, OVER_QUALITY { @Override public ErrorViewResources getErrorResources(ErrorViewSpec spec) { return spec.getOverQualitySpec(); } }, UNDER_SIZE { @Override public ErrorViewResources getErrorResources(ErrorViewSpec spec) { return spec.getUnderSizeSpec(); } }, OVER_SIZE { @Override public ErrorViewResources getErrorResources(ErrorViewSpec spec) { return spec.getOverSizeSpec(); } }, FILE_TYPE { @Override public ErrorViewResources getErrorResources(ErrorViewSpec spec) { return spec.getTypeErrorSpec(); } }; public abstract ErrorViewResources getErrorResources(ErrorViewSpec spec); } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/entity/ViewResourceSpec.java ================================================ package com.laevatein.internal.entity; import android.content.pm.ActivityInfo; import android.os.Parcel; import android.os.Parcelable; import androidx.annotation.Nullable; import androidx.annotation.StyleRes; import com.amalgam.os.ParcelUtils; import com.laevatein.ui.ImagePreviewActivity; /** * @author keishin.yokomaku * @since 2014/04/07 */ public class ViewResourceSpec implements Parcelable { public static final Creator CREATOR = new Creator() { @Override @Nullable public ViewResourceSpec createFromParcel(Parcel source) { return new ViewResourceSpec(source); } @Override public ViewResourceSpec[] newArray(int size) { return new ViewResourceSpec[size]; } }; public static final int DEFAULT_SCREEN_ORIENTATION = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; // no restriction @StyleRes private int mTheme; private final Class mPreviewActivityClass; private final ItemViewResources mItemViewResources; private final CounterViewResources mCounterViewResources; private final PreviewViewResources mPreviewViewResources; private final boolean mOpenDrawer; private final CaptureResources mCaptureResources; private final boolean mEnableSelectedView; private final int mActivityOrientation; /* package */ ViewResourceSpec(Parcel source) { mTheme = source.readInt(); mPreviewActivityClass = (Class) source.readSerializable(); mItemViewResources = source.readParcelable(ItemViewResources.class.getClassLoader()); mCounterViewResources = source.readParcelable(CounterViewResources.class.getClassLoader()); mPreviewViewResources = source.readParcelable(PreviewViewResources.class.getClassLoader()); mOpenDrawer = ParcelUtils.readBoolean(source); mCaptureResources = source.readParcelable(CaptureResources.class.getClassLoader()); mEnableSelectedView = ParcelUtils.readBoolean(source); mActivityOrientation = source.readInt(); } /* package */ ViewResourceSpec( @StyleRes int theme, Class previewActivityClass, ItemViewResources itemViewResources, CounterViewResources counterViewResources, PreviewViewResources previewViewResources, boolean openDrawer, CaptureResources captureResources, boolean enableSelectedView, int activityOrientation) { mTheme = theme; mPreviewActivityClass = previewActivityClass; mItemViewResources = itemViewResources; mCounterViewResources = counterViewResources; mPreviewViewResources = previewViewResources; mOpenDrawer = openDrawer; mCaptureResources = captureResources; mEnableSelectedView = enableSelectedView; mActivityOrientation = activityOrientation; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mTheme); dest.writeSerializable(mPreviewActivityClass); dest.writeParcelable(mItemViewResources, flags); dest.writeParcelable(mCounterViewResources, flags); dest.writeParcelable(mPreviewViewResources, flags); ParcelUtils.writeBoolean(dest, mOpenDrawer); dest.writeParcelable(mCaptureResources, flags); ParcelUtils.writeBoolean(dest, mEnableSelectedView); dest.writeInt(mActivityOrientation); } public static class Builder { @StyleRes private int mTheme; private Class mPreviewActivityClass; private ItemViewResources mItemViewResources; private CounterViewResources mCounterViewResources; private PreviewViewResources mPreviewViewResources; private boolean mOpenDrawer; private CaptureResources mCaptureResources; private boolean mEnableSelectedView; private int mActivityOrientation; public Builder setTheme(@StyleRes int theme) { mTheme = theme; return this; } public Builder setPreviewClass(Class previewActivityClass) { mPreviewActivityClass = previewActivityClass; return this; } public Builder setItemViewResources(ItemViewResources itemViewResources) { mItemViewResources = itemViewResources; return this; } public Builder setCounterViewResources(CounterViewResources counterViewResources) { mCounterViewResources = counterViewResources; return this; } public Builder setPreviewViewResources(PreviewViewResources previewViewResources) { mPreviewViewResources = previewViewResources; return this; } public Builder setOpenDrawer(boolean openDrawer) { mOpenDrawer = openDrawer; return this; } public Builder setCaptureResources(CaptureResources captureResources) { mCaptureResources = captureResources; return this; } public Builder setEnableSelectedView(boolean enableSelectedView) { mEnableSelectedView = enableSelectedView; return this; } public Builder setActivityOrientation(int activityOrientation) { mActivityOrientation = activityOrientation; return this; } public ViewResourceSpec create() { if (mItemViewResources == null) { mItemViewResources = ItemViewResources.getDefault(); } if (mCounterViewResources == null) { mCounterViewResources = CounterViewResources.getDefault(); } if (mPreviewViewResources == null) { mPreviewViewResources = PreviewViewResources.getDefault(); } return new ViewResourceSpec(mTheme, mPreviewActivityClass, mItemViewResources, mCounterViewResources, mPreviewViewResources, mOpenDrawer, mCaptureResources, mEnableSelectedView, mActivityOrientation); } } public int getTheme() { return mTheme; } public Class getPreviewActivityClass() { return mPreviewActivityClass; } public ItemViewResources getItemViewResources() { return mItemViewResources; } public CounterViewResources getCounterViewResources() { return mCounterViewResources; } public PreviewViewResources getPreviewViewResources() { return mPreviewViewResources; } public boolean openDrawer() { return mOpenDrawer; } public CaptureResources getCaptureResource() { return mCaptureResources; } public boolean isEnableSelectedView() { return mEnableSelectedView; } public boolean needActivityOrientationRestriction() { return mActivityOrientation != DEFAULT_SCREEN_ORIENTATION; } public int getActivityOrientation() { return mActivityOrientation; } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/loader/AlbumLoader.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.loader; import android.content.Context; import android.database.Cursor; import android.database.MatrixCursor; import android.provider.MediaStore; import androidx.loader.content.CursorLoader; import com.laevatein.internal.entity.Album; import java.util.LinkedHashMap; import java.util.Map; /** * Wrapper for {@link CursorLoader} to merge custom cursors. * @author KeithYokoma * @since 2014/03/26 * @version 1.0.0 * @hide */ public class AlbumLoader extends CursorLoader { public static final String TAG = AlbumLoader.class.getSimpleName(); private static final String[] PROJECTION = { MediaStore.Images.Media.BUCKET_ID, MediaStore.Images.Media.BUCKET_DISPLAY_NAME, MediaStore.Images.Media._ID }; private static final String BUCKET_ORDER_BY = "datetaken DESC"; private static final String MEDIA_ID_DUMMY = String.valueOf(-1); public AlbumLoader(Context context) { super(context, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, PROJECTION, null, null, BUCKET_ORDER_BY); } @Override public Cursor loadInBackground() { Cursor albums = super.loadInBackground(); Map buckets = new LinkedHashMap<>(); if (albums != null) { try { while (albums.moveToNext()) { String bucketId = albums.getString(albums.getColumnIndex(MediaStore.Images.Media.BUCKET_ID)); if (!buckets.containsKey(bucketId)) { buckets.put(bucketId, new BucketEntry( albums.getString(albums.getColumnIndex(MediaStore.Images.Media.BUCKET_DISPLAY_NAME)), albums.getString(albums.getColumnIndex(MediaStore.Images.Media._ID)) )); } } } finally { albums.close(); } } MatrixCursor allAlbum = new MatrixCursor(PROJECTION); allAlbum.addRow(new String[]{Album.ALBUM_ID_ALL, Album.ALBUM_NAME_ALL, MEDIA_ID_DUMMY}); for (Map.Entry entry : buckets.entrySet()) { BucketEntry bucket = entry.getValue(); allAlbum.addRow(new String[]{entry.getKey(), bucket.displayName, bucket.mediaId}); } return allAlbum; } private static class BucketEntry { private final String displayName; private final String mediaId; public BucketEntry(String displayName, String mediaId) { this.displayName = displayName; this.mediaId = mediaId; } } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/loader/AlbumPhotoLoader.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.loader; import android.content.Context; import android.database.Cursor; import android.database.MatrixCursor; import android.database.MergeCursor; import android.net.Uri; import android.provider.MediaStore; import androidx.loader.content.CursorLoader; import com.laevatein.internal.entity.Album; import com.laevatein.internal.entity.Item; import com.laevatein.internal.utils.MediaStoreUtils; /** * @author KeithYokoma * @version 1.0.0 * @hide * @since 2014/03/27 */ public class AlbumPhotoLoader extends CursorLoader { public static final String TAG = AlbumPhotoLoader.class.getSimpleName(); private static final String[] PROJECTION = {MediaStore.Images.Media._ID, MediaStore.Images.Media.DISPLAY_NAME}; private static final String ORDER_BY = MediaStore.Images.Media._ID + " DESC"; private final boolean mEnableCapture; public AlbumPhotoLoader(Context context, Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, boolean capture) { super(context, uri, projection, selection, selectionArgs, sortOrder); mEnableCapture = capture; } public static CursorLoader newInstance(Context context, Album album) { if (album.isAll()) { return new AlbumPhotoLoader(context, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, PROJECTION, null, null, ORDER_BY, false); } return newInstance(context, album, false); } public static CursorLoader newInstance(Context context, Album album, boolean capture) { return new AlbumPhotoLoader(context, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, PROJECTION, MediaStore.Images.Media.BUCKET_ID + " = ?", new String[]{album.getId()}, ORDER_BY, capture); } @Override public Cursor loadInBackground() { Cursor result = super.loadInBackground(); if (!mEnableCapture || !MediaStoreUtils.hasCameraFeature(getContext())) { return result; } MatrixCursor dummy = new MatrixCursor(PROJECTION); dummy.addRow(new Object[]{Item.ITEM_ID_CAPTURE, Item.ITEM_DISPLAY_NAME_CAPTURE}); return new MergeCursor(new Cursor[]{dummy, result}); } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/misc/ui/ConfirmationDialogFragment.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.misc.ui; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; import androidx.annotation.NonNull; import androidx.fragment.app.DialogFragment; import androidx.appcompat.app.AlertDialog; import com.amalgam.os.BundleUtils; import com.laevatein.R; /** * @author KeithYokoma * @since 2014/03/25 * @version 1.0.0 * @hide */ public class ConfirmationDialogFragment extends DialogFragment { public static final String TAG = ConfirmationDialogFragment.class.getSimpleName(); private static final String ARGS_TITLE = BundleUtils.buildKey(ConfirmationDialogFragment.class, "ARGS_TITLE"); private static final String ARGS_MESSAGE = BundleUtils.buildKey(ConfirmationDialogFragment.class, "ARGS_MESSAGE"); private ConfirmationSelectionListener mListener; public ConfirmationDialogFragment() {} public static ConfirmationDialogFragment newInstance(int message) { ConfirmationDialogFragment fragment = new ConfirmationDialogFragment(); Bundle args = new Bundle(); args.putInt(ARGS_MESSAGE, message); fragment.setArguments(args); return fragment; } public static ConfirmationDialogFragment newInstance(int title, int message) { ConfirmationDialogFragment fragment = new ConfirmationDialogFragment(); Bundle args = new Bundle(); args.putInt(ARGS_TITLE, title); args.putInt(ARGS_MESSAGE, message); fragment.setArguments(args); return fragment; } @Override public void onAttach(Context context) { super.onAttach(context); try { mListener = (ConfirmationSelectionListener) context; } catch (ClassCastException e) { throw new IllegalArgumentException("the host activity should implement ConfirmationSelectionListener"); } } @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { int title = getArguments().getInt(ARGS_TITLE, -1); if (title == -1) { title = R.string.l_confirm_dialog_title; } int message = getArguments().getInt(ARGS_MESSAGE); AlertDialog.Builder dialog = new AlertDialog.Builder(getActivity()) .setTitle(title) .setMessage(message) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mListener.onPositive(); dialog.dismiss(); } }) .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mListener.onNegative(); dialog.dismiss(); } }); return dialog.create(); } public interface ConfirmationSelectionListener { void onPositive(); void onNegative(); } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/misc/ui/FragmentUtils.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.misc.ui; import android.app.Activity; import android.content.Intent; import android.os.Parcelable; import androidx.fragment.app.Fragment; import android.view.View; /** * @author KeithYokoma * @since 2014/03/20 * @version 1.0.0 * @hide */ public final class FragmentUtils { private FragmentUtils() {} public static T getIntentParcelableExtra(Fragment fragment, String key) { Activity activity = fragment.getActivity(); Intent intent = activity.getIntent(); return intent.getParcelableExtra(key); } public static boolean getIntentBooleanExtra(Fragment fragment, String key, boolean fallback) { Activity activity = fragment.getActivity(); Intent intent = activity.getIntent(); return intent.getBooleanExtra(key, fallback); } public static View findViewById(Fragment fragment, int id) { View view = fragment.getView().findViewById(id); if(view == null){ view = fragment.getView(); } return view; } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/misc/ui/helper/options/OptionsMenu.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.misc.ui.helper.options; import android.app.Activity; /** * @author KeithYokoma * @since 2014/03/20 * @version 1.0.0 * @hide */ public interface OptionsMenu> { int getMenuId(); H getHandler(); } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/misc/ui/helper/options/OptionsMenuHandler.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.misc.ui.helper.options; import android.app.Activity; /** * @author KeithYokoma * @since 2014/03/20 * @version 1.0.0 * @hide */ public interface OptionsMenuHandler { boolean handle(A activity, E extra); } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/misc/widget/SquareGridViewItemRelativeLayout.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.misc.widget; import android.content.Context; import android.util.AttributeSet; import android.widget.RelativeLayout; /** * @author KeithYokoma * @since 2014/03/24 * @version 1.0.0 * @hide */ public class SquareGridViewItemRelativeLayout extends RelativeLayout { public SquareGridViewItemRelativeLayout(Context context) { this(context, null); } public SquareGridViewItemRelativeLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public SquareGridViewItemRelativeLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, widthMeasureSpec); } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/model/AlbumPhotoCollection.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.model; import android.content.Context; import android.database.Cursor; import android.os.Bundle; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.FragmentActivity; import androidx.loader.app.LoaderManager; import androidx.loader.content.Loader; import com.amalgam.os.BundleUtils; import com.laevatein.internal.entity.Album; import com.laevatein.internal.loader.AlbumPhotoLoader; import java.lang.ref.WeakReference; /** * @author KeithYokoma * @since 2014/03/24 * @version 1.0.0 * @hide */ public class AlbumPhotoCollection implements LoaderManager.LoaderCallbacks { private static final int LOADER_ID = 2; private static final String ARGS_ALBUM = BundleUtils.buildKey(AlbumPhotoCollection.class, "ARGS_ALBUM"); private static final String ARGS_ENABLE_CAPTURE = BundleUtils.buildKey(AlbumPhotoCollection.class, "ARGS_ENABLE_CAPTURE"); private WeakReference mContext; private LoaderManager mLoaderManager; private AlbumPhotoCallbacks mCallbacks; @Override public Loader onCreateLoader(int id, Bundle args) { Context context = mContext.get(); if (context == null) { return null; } Album album = args.getParcelable(ARGS_ALBUM); if (album == null) { return null; } if (album.isCamera()) { boolean capture = args.getBoolean(ARGS_ENABLE_CAPTURE, false); return AlbumPhotoLoader.newInstance(context, album, capture); } return AlbumPhotoLoader.newInstance(context, album); } @Override public void onLoadFinished(Loader loader, Cursor data) { Context context = mContext.get(); if (context == null) { return; } mCallbacks.onLoad(data); } @Override public void onLoaderReset(Loader loader) { Context context = mContext.get(); if (context == null) { return; } mCallbacks.onReset(); } public void onCreate(@NonNull FragmentActivity context, @NonNull AlbumPhotoCallbacks callbacks) { mContext = new WeakReference(context); mLoaderManager = context.getSupportLoaderManager(); mCallbacks = callbacks; } public void onDestroy() { mLoaderManager.destroyLoader(LOADER_ID); mCallbacks = null; } public void load(@Nullable Album target) { load(target, false); } public void load(@Nullable Album target, boolean enableCapture) { Bundle args = new Bundle(); args.putParcelable(ARGS_ALBUM, target); args.putBoolean(ARGS_ENABLE_CAPTURE, enableCapture); mLoaderManager.initLoader(LOADER_ID, args, this); } public interface AlbumPhotoCallbacks { void onLoad(Cursor cursor); void onReset(); } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/model/DevicePhotoAlbumCollection.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.model; import com.amalgam.os.BundleUtils; import com.laevatein.internal.loader.AlbumLoader; import android.content.Context; import android.database.Cursor; import android.os.Bundle; import androidx.fragment.app.FragmentActivity; import androidx.loader.app.LoaderManager; import androidx.loader.content.Loader; import java.lang.ref.WeakReference; /** * @author KeithYokoma * @since 2014/03/24 * @version 1.0.0 * @hide */ public class DevicePhotoAlbumCollection implements LoaderManager.LoaderCallbacks { private static final int LOADER_ID = 1; private static final String STATE_CURRENT_SELECTION = BundleUtils.buildKey(DevicePhotoAlbumCollection.class, "STATE_CURRENT_SELECTION"); private WeakReference mContext; private LoaderManager mLoaderManager; private DevicePhotoAlbumCallbacks mCallbacks; private int mCurrentSelection; @Override public Loader onCreateLoader(int id, Bundle args) { Context context = mContext.get(); if (context == null) { return null; } return new AlbumLoader(context); } @Override public void onLoadFinished(Loader loader, Cursor data) { Context context = mContext.get(); if (context == null) { return; } mCallbacks.onLoad(data); } @Override public void onLoaderReset(Loader loader) { Context context = mContext.get(); if (context == null) { return; } mCallbacks.onReset(); } public void onCreate(FragmentActivity activity, DevicePhotoAlbumCallbacks callbacks) { mContext = new WeakReference(activity); mLoaderManager = activity.getSupportLoaderManager(); mCallbacks = callbacks; } public void onRestoreInstanceState(Bundle savedInstanceState) { if (savedInstanceState == null) { return; } mCurrentSelection = savedInstanceState.getInt(STATE_CURRENT_SELECTION); } public void onSaveInstanceState(Bundle outState) { outState.putInt(STATE_CURRENT_SELECTION, mCurrentSelection); } public void onDestroy() { mLoaderManager.destroyLoader(LOADER_ID); mCallbacks = null; } public void loadAlbums() { mLoaderManager.initLoader(LOADER_ID, null, this); } public int getCurrentSelection() { return mCurrentSelection; } public void setStateCurrentSelection(int currentSelection) { mCurrentSelection = currentSelection; } public interface DevicePhotoAlbumCallbacks { void onLoad(Cursor cursor); void onReset(); } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/model/PreviewStateHolder.java ================================================ package com.laevatein.internal.model; import android.app.Activity; import android.net.Uri; import android.os.Bundle; import com.amalgam.os.BundleUtils; import com.laevatein.ui.ImagePreviewActivity; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; /** * @author keishin.yokomaku * @since 2014/04/03 */ public class PreviewStateHolder { private static final String STATE_CHECKED = BundleUtils.buildKey(PreviewStateHolder.class, "STATE_CHECKED"); private ArrayList mChecked; private WeakReference mContext; public PreviewStateHolder(Activity activity) { mContext = new WeakReference<>(activity); } public void onCreate() { Activity activity = mContext.get(); if (activity == null) { return; } mChecked = activity.getIntent().getParcelableArrayListExtra(ImagePreviewActivity.EXTRA_DEFAULT_CHECKED); } public void onRestoreInstanceState(Bundle savedInstanceState) { if (savedInstanceState == null) { return; } mChecked = savedInstanceState.getParcelableArrayList(STATE_CHECKED); } public void onSaveInstanceState(Bundle outState) { outState.putParcelableArrayList(STATE_CHECKED, mChecked); } public boolean isChecked(Uri uri) { return mChecked.contains(uri); } public void setChecked(Uri uri, boolean checked) { if (checked && !mChecked.contains(uri)) { mChecked.add(uri); } else if (!checked) { mChecked.remove(uri); } } public int getCheckedCount() { return mChecked.size(); } public List getAllChecked() { return new ArrayList<>(mChecked); } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/model/SelectedUriCollection.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.model; import android.content.Context; import android.net.Uri; import android.os.Bundle; import com.amalgam.os.BundleUtils; import com.laevatein.internal.entity.SelectionSpec; import com.laevatein.internal.entity.UncapableCause; import com.laevatein.internal.utils.PhotoMetadataUtils; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; /** * @author KeithYokoma * @version 1.0.0 * @hide * @since 2014/03/20 */ public class SelectedUriCollection { private static final String STATE_SELECTION = BundleUtils.buildKey(SelectedUriCollection.class, "STATE_SELECTION"); private final WeakReference mContext; private Set mUris; private SelectionSpec mSpec; public SelectedUriCollection(Context context) { mContext = new WeakReference<>(context); } public void onCreate(Bundle savedInstanceState) { if (savedInstanceState == null) { mUris = new LinkedHashSet<>(); } else { List saved = savedInstanceState.getParcelableArrayList(STATE_SELECTION); mUris = new LinkedHashSet<>(saved); } } public void prepareSelectionSpec(SelectionSpec spec) { mSpec = spec; } public void setDefaultSelection(List uris) { mUris.addAll(uris); } public void onSaveInstanceState(Bundle outState) { outState.putParcelableArrayList(STATE_SELECTION, new ArrayList<>(mUris)); } public boolean add(Uri uri) { return mUris.add(uri); } public boolean remove(Uri uri) { return mUris.remove(uri); } public void overwrite(ArrayList uriLists) { mUris.clear(); mUris.addAll(uriLists); } public List asList() { return new ArrayList<>(mUris); } public boolean isEmpty() { return mUris == null || mUris.isEmpty(); } public boolean isSelected(Uri uri) { return mUris.contains(uri); } public UncapableCause isAcceptable(Uri uri) { return PhotoMetadataUtils.isAcceptable(mContext.get(), mSpec, uri); } public boolean isCountInRange() { return mSpec.getMinSelectable() <= mUris.size() && !isCountOver(); } public boolean isCountOver() { return mUris.size() > mSpec.getMaxSelectable(); } public int count() { return mUris.size(); } public int maxCount() { return mSpec.getMaxSelectable(); } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/package-info.java ================================================ /** * This package contains various internal-use classes. * @author KeithYokoma * @since 2014/03/20 * @version 1.0.0 * @hide */ package com.laevatein.internal; ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/ui/AlbumListFragment.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.ui; import android.content.Context; import android.database.Cursor; import android.os.Bundle; import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ListView; import com.amalgam.os.BundleUtils; import com.laevatein.R; import com.laevatein.internal.entity.Album; import com.laevatein.internal.misc.ui.FragmentUtils; import com.laevatein.internal.model.DevicePhotoAlbumCollection; import com.laevatein.internal.ui.helper.AlbumListViewHelper; /** * @author KeithYokoma * @version 1.0.0 * @hide * @since 2014/03/20 */ public class AlbumListFragment extends Fragment implements AdapterView.OnItemClickListener, DevicePhotoAlbumCollection.DevicePhotoAlbumCallbacks { private static final String ARGS_ALBUM_ID = BundleUtils.buildKey(AlbumListFragment.class, "ARGS_ALBUM_ID"); private final DevicePhotoAlbumCollection mCollection = new DevicePhotoAlbumCollection(); private OnDirectorySelectListener mListener; private String defaultAlbum; public static AlbumListFragment newInstance(String defaultAlbumId) { AlbumListFragment fragment = new AlbumListFragment(); Bundle args = new Bundle(); args.putString(ARGS_ALBUM_ID, defaultAlbumId); fragment.setArguments(args); return fragment; } @Override public void onAttach(Context context) { super.onAttach(context); try { mListener = (OnDirectorySelectListener) context; } catch (ClassCastException e) { throw new IllegalStateException("the host activity should implement OnDirectorySelectListener.", e); } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.l_fragment_list_album, container, false); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); AlbumListViewHelper.setUpHeader(this); AlbumListViewHelper.setUpListView(this, this); defaultAlbum = getArguments().getString(ARGS_ALBUM_ID); mCollection.onCreate(getActivity(), this); mCollection.onRestoreInstanceState(savedInstanceState); ListView list = (ListView) FragmentUtils.findViewById(this, R.id.l_list_album); if (list.getHeaderViewsCount() > 0 && mCollection.getCurrentSelection() == 0) { mCollection.setStateCurrentSelection(1); } AlbumListViewHelper.setCheckedState(this, mCollection.getCurrentSelection()); mCollection.loadAlbums(); } @Override public void onSaveInstanceState(Bundle outState) { mCollection.onSaveInstanceState(outState); super.onSaveInstanceState(outState); } @Override public void onDestroyView() { mCollection.onDestroy(); super.onDestroyView(); } @Override public void onItemClick(AdapterView parent, View view, int position, long id) { Cursor cursor = (Cursor) parent.getItemAtPosition(position); if (cursor == null) { //avoid moving selection to header AlbumListViewHelper.setCheckedState(this, mCollection.getCurrentSelection()); return; } AlbumListViewHelper.callOnSelect(mListener, cursor); AlbumListViewHelper.setCheckedState(this, position); mCollection.setStateCurrentSelection(position); } @Override public void onLoad(final Cursor cursor) { AlbumListViewHelper.setCursor(this, cursor); AlbumListViewHelper.callOnDefaultSelect(this, mListener, cursor, defaultAlbum, mCollection); } @Override public void onReset() { AlbumListViewHelper.setCursor(this, null); } public interface OnDirectorySelectListener { void onSelect(Album album); } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/ui/PhotoGridFragment.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.ui; import android.database.Cursor; import android.os.Bundle; import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.amalgam.os.BundleUtils; import com.laevatein.R; import com.laevatein.internal.entity.Album; import com.laevatein.internal.entity.ViewResourceSpec; import com.laevatein.internal.misc.ui.FragmentUtils; import com.laevatein.internal.model.AlbumPhotoCollection; import com.laevatein.internal.ui.adapter.AlbumPhotoAdapter; import com.laevatein.internal.ui.helper.PhotoGridViewHelper; import com.laevatein.ui.PhotoSelectionActivity; /** * @author KeithYokoma * @version 1.0.0 * @hide * @since 2014/03/20 */ public class PhotoGridFragment extends Fragment implements AlbumPhotoCollection.AlbumPhotoCallbacks, AlbumPhotoAdapter.CheckStateListener { public static final String TAG = PhotoGridFragment.class.getSimpleName(); public static final String ARGS_ALBUM = BundleUtils.buildKey(PhotoGridFragment.class, "ARGS_ALBUM"); private final AlbumPhotoCollection mPhotoCollection = new AlbumPhotoCollection(); public static PhotoGridFragment newInstance(Album album) { PhotoGridFragment fragment = new PhotoGridFragment(); Bundle args = new Bundle(); args.putParcelable(ARGS_ALBUM, album); fragment.setArguments(args); return fragment; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.l_fragment_grid_photo, container, false); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); ViewResourceSpec resources = FragmentUtils.getIntentParcelableExtra(this, PhotoSelectionActivity.EXTRA_VIEW_SPEC); Album album = getArguments().getParcelable(ARGS_ALBUM); AlbumPhotoAdapter.BindViewListener listener; try { listener = (AlbumPhotoAdapter.BindViewListener) getActivity(); } catch (ClassCastException e) { throw new IllegalStateException("the host activity should implement BindViewListener."); } PhotoGridViewHelper.setUpGridView(this, resources, PhotoGridViewHelper.getSelectedPhotoSet(this), listener); mPhotoCollection.onCreate(getActivity(), this); mPhotoCollection.load(album, resources.getCaptureResource().enabled()); getActivity().setTitle(album.getDisplayName(getActivity())); } @Override public void onDestroyView() { PhotoGridViewHelper.tearDownGridView(this); mPhotoCollection.onDestroy(); super.onDestroyView(); } @Override public void onLoad(Cursor cursor) { PhotoGridViewHelper.setCursor(this, cursor); } @Override public void onReset() { PhotoGridViewHelper.setCursor(this, null); } @Override public void onUpdate() { getActivity().invalidateOptionsMenu(); } public void refreshGrid() { PhotoGridViewHelper.refreshView(this); } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/ui/PreviewFragment.java ================================================ package com.laevatein.internal.ui; import android.net.Uri; import android.os.Bundle; import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.amalgam.os.BundleUtils; import com.bumptech.glide.Glide; import com.bumptech.glide.Priority; import com.bumptech.glide.request.RequestOptions; import com.laevatein.internal.entity.PreviewViewResources; import it.sephiroth.android.library.imagezoom.ImageViewTouch; import it.sephiroth.android.library.imagezoom.ImageViewTouchBase; /** * Created by hiroyuki.seto on 15/06/16. * * @hide */ public class PreviewFragment extends Fragment { private static final String ARGS_URI = BundleUtils.buildKey(PhotoGridFragment.class, "ARGS_URI"); private static final String ARGS_RESOURCES = BundleUtils.buildKey(PhotoGridFragment.class, "ARGS_RESOURCES"); private PreviewViewResources mViewResources; public static PreviewFragment newInstance(Uri uri, PreviewViewResources resources) { PreviewFragment fragment = new PreviewFragment(); Bundle bundle = new Bundle(); bundle.putParcelable(ARGS_URI, uri); bundle.putParcelable(ARGS_RESOURCES, resources); fragment.setArguments(bundle); return fragment; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mViewResources = getArguments().getParcelable(ARGS_RESOURCES); return inflater.inflate(mViewResources.getLayoutId(), container, false); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); ImageViewTouch image = getView().findViewById(mViewResources.getImageViewId()); image.setDisplayType(ImageViewTouchBase.DisplayType.FIT_TO_SCREEN); Uri uri = getArguments().getParcelable(ARGS_URI); Glide.with(getActivity()).load(uri) .apply(new RequestOptions().priority(Priority.HIGH).fitCenter().centerInside()) .into(image); } public void resetView() { ((ImageViewTouch) getView()).resetMatrix(); } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/ui/SelectedCountFragment.java ================================================ package com.laevatein.internal.ui; import com.laevatein.R; import com.laevatein.internal.ui.helper.SelectedCountViewHelper; import com.laevatein.ui.PhotoSelectionActivity; import android.content.Context; import android.os.Bundle; import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.Menu; import android.view.View; import android.view.ViewGroup; /** * @author KeithYokoma * @since 2014/04/03 * @version 1.0.0 * @hide */ public class SelectedCountFragment extends Fragment { private OnShowSelectedClickListener mListener; @Override public void onAttach(Context context) { super.onAttach(context); try { mListener = (OnShowSelectedClickListener) context; } catch (ClassCastException e) { throw new IllegalStateException("the host activity should implement OnShowSelectedClickListener."); } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.l_fragment_selected_count, container, false); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); SelectedCountViewHelper.setUpCountView(this); SelectedCountViewHelper.updateCountView((PhotoSelectionActivity) getActivity(), this); } @Override public void onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); SelectedCountViewHelper.updateCountView((PhotoSelectionActivity) getActivity(), this); // bit hacky } public OnShowSelectedClickListener getListener() { return mListener; } public interface OnShowSelectedClickListener { void onClickSelectedView(); } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/ui/SelectedPhotoGridFragment.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.ui; import android.os.Bundle; import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.laevatein.R; import com.laevatein.internal.entity.ViewResourceSpec; import com.laevatein.internal.misc.ui.FragmentUtils; import com.laevatein.internal.ui.adapter.SelectedPhotoAdapter; import com.laevatein.internal.ui.helper.SelectedGridViewHelper; import com.laevatein.ui.PhotoSelectionActivity; /** * @author KeithYokoma * @since 2014/03/27 * @version 1.0.0 * @hide */ public class SelectedPhotoGridFragment extends Fragment implements SelectedPhotoAdapter.CheckStateListener { public static final String TAG = SelectedPhotoGridFragment.class.getSimpleName(); public SelectedPhotoGridFragment() { } public static SelectedPhotoGridFragment newInstance() { return new SelectedPhotoGridFragment(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.l_fragment_grid_photo, container, false); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); ViewResourceSpec resources = FragmentUtils.getIntentParcelableExtra(this, PhotoSelectionActivity.EXTRA_VIEW_SPEC); getActivity().setTitle(R.string.l_album_name_selected); SelectedGridViewHelper.setUpGridView(this, resources.getItemViewResources(), SelectedGridViewHelper.getSelectedPhotoSet(this)); } @Override public void onDestroyView() { super.onDestroyView(); } @Override public void onUpdate() { getActivity().invalidateOptionsMenu(); } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/ui/adapter/AlbumPhotoAdapter.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.ui.adapter; import android.content.Context; import android.database.Cursor; import android.net.Uri; import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.CheckBox; import android.widget.ImageView; import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; import com.laevatein.R; import com.laevatein.internal.entity.CaptureResources; import com.laevatein.internal.entity.Item; import com.laevatein.internal.entity.ItemViewResources; import com.laevatein.internal.entity.ViewResourceSpec; import com.laevatein.internal.model.SelectedUriCollection; import com.laevatein.internal.ui.helper.PhotoGridViewHelper; /** * @author KeithYokoma * @version 1.0.0 * @hide * @since 2014/03/24 */ public class AlbumPhotoAdapter extends RecyclerViewCursorAdapter { private final Context mContext; private final ItemViewResources mResources; private final CaptureResources mCaptureResources; private final SelectedUriCollection mCollection; private CheckStateListener mListener; private BindViewListener mBindViewListener; public AlbumPhotoAdapter(Context context, ViewResourceSpec resources, SelectedUriCollection collection, BindViewListener bindViewListener) { super(null); mContext = context; mResources = resources.getItemViewResources(); mCaptureResources = resources.getCaptureResource(); mCollection = collection; mBindViewListener = bindViewListener; } @Override public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { View v = LayoutInflater.from(viewGroup.getContext()).inflate(mResources.getLayoutId(), viewGroup, false); return new ViewHolder(v, mResources); } @Override protected void onBindViewHolder(ViewHolder holder, Cursor cursor) { final Item item = Item.valueOf(cursor); holder.thumbnail.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (item.isCapture()) { if (mCaptureResources.getFileProviderAuthorities() == null) { throw new IllegalStateException(); } PhotoGridViewHelper.callCamera(mContext, mCaptureResources.getFileProviderAuthorities()); } else { PhotoGridViewHelper.callPreview(mContext, item, mCollection.asList()); } } }); holder.checkBox.setVisibility(item.isCapture() ? View.GONE : View.VISIBLE); holder.checkBox.setChecked(mCollection.isSelected(item.buildContentUri())); holder.checkBox.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { PhotoGridViewHelper.syncCheckState(mContext, mCollection, item, (CheckBox) v); PhotoGridViewHelper.callCheckStateListener(mListener); } }); if (item.isCapture()) { holder.thumbnail.setImageResource(R.drawable.l_ic_capture); } else { Glide.with(mContext).load(item.buildContentUri()) .apply(new RequestOptions().fitCenter().centerCrop()) .into(holder.thumbnail); } mBindViewListener.onBindView(mContext, holder.itemView, item.buildContentUri()); } public void registerCheckStateListener(CheckStateListener listener) { mListener = listener; } public void unregisterCheckStateListener() { mListener = null; } public interface CheckStateListener { void onUpdate(); } public interface BindViewListener { /** * Called when view is bound to data. * * @param context context of photo selection activity * @param view view of grid * @param uri uri of image in grid */ void onBindView(Context context, View view, Uri uri); } static class ViewHolder extends RecyclerView.ViewHolder { private final ImageView thumbnail; private final CheckBox checkBox; ViewHolder(View v, ItemViewResources resources) { super(v); thumbnail = v.findViewById(resources.getImageViewId()); checkBox = v.findViewById(resources.getCheckBoxId()); } } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/ui/adapter/DevicePhotoAlbumAdapter.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.ui.adapter; import android.content.Context; import android.database.Cursor; import androidx.cursoradapter.widget.CursorAdapter; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import com.amalgam.content.ContextUtils; import com.laevatein.R; import com.laevatein.internal.entity.Album; /** * @author KeithYokoma * @version 1.0.0 * @hide * @since 2014/03/24 */ public class DevicePhotoAlbumAdapter extends CursorAdapter { public DevicePhotoAlbumAdapter(Context context, Cursor c) { super(context, c, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER); } @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { LayoutInflater inflater = ContextUtils.getLayoutInflater(context); return inflater.inflate(R.layout.l_list_item_default_album, parent, false); } @Override public void bindView(View view, Context context, Cursor cursor) { Album album = Album.valueOf(cursor); TextView textView = view.findViewById(R.id.l_default_directory_label); textView.setText(album.getDisplayName(context)); } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/ui/adapter/PreviewPagerAdapter.java ================================================ package com.laevatein.internal.ui.adapter; import android.net.Uri; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentPagerAdapter; import android.view.ViewGroup; import com.laevatein.internal.entity.PreviewViewResources; import com.laevatein.internal.ui.PreviewFragment; import java.util.ArrayList; import java.util.List; /** * Created by hiroyuki.seto on 15/06/16. * * @hide */ public class PreviewPagerAdapter extends FragmentPagerAdapter { private ArrayList mUris = new ArrayList<>(); private PreviewViewResources mPreviewViewResources; private OnPrimaryItemSetListener mListener; public PreviewPagerAdapter(FragmentManager manager, PreviewViewResources previewViewResources, OnPrimaryItemSetListener listener) { super(manager); mPreviewViewResources = previewViewResources; mListener = listener; } @Override public Fragment getItem(int position) { return PreviewFragment.newInstance(mUris.get(position), mPreviewViewResources); } @Override public int getCount() { return mUris.size(); } @Override public void setPrimaryItem(ViewGroup container, int position, Object object) { super.setPrimaryItem(container, position, object); mListener.onPrimaryItemSet(position); } public Uri getUri(int position) { return mUris.get(position); } public void addAll(List uris) { mUris.addAll(uris); } public interface OnPrimaryItemSetListener { void onPrimaryItemSet(int position); } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/ui/adapter/RecyclerViewCursorAdapter.java ================================================ /* * This file is based on the code of `RecyclerViewCursorAdapter.java` in `Matisse`. * * https://github.com/zhihu/Matisse/blob/master/matisse/src/main/java/com/zhihu/matisse/internal/ui/adapter/RecyclerViewCursorAdapter.java */ package com.laevatein.internal.ui.adapter; import android.database.Cursor; import android.provider.MediaStore; import androidx.recyclerview.widget.RecyclerView; public abstract class RecyclerViewCursorAdapter extends RecyclerView.Adapter { private Cursor mCursor; private int mRowIDColumn; protected abstract void onBindViewHolder(VH holder, Cursor cursor); RecyclerViewCursorAdapter(Cursor c) { setHasStableIds(true); swapCursor(c); } @Override public void onBindViewHolder(VH holder, int position) { if (!isDataValid(mCursor)) { throw new IllegalStateException("Cannot bind view holder when cursor is in invalid state."); } if (!mCursor.moveToPosition(position)) { throw new IllegalStateException("Could not move cursor to position " + position + " when trying to bind view holder"); } onBindViewHolder(holder, mCursor); } @Override public int getItemCount() { if (isDataValid(mCursor)) { return mCursor.getCount(); } else { return 0; } } @Override public long getItemId(int position) { if (!isDataValid(mCursor)) { throw new IllegalStateException("Cannot lookup item id when cursor is in invalid state."); } if (!mCursor.moveToPosition(position)) { throw new IllegalStateException("Could not move cursor to position " + position + " when trying to get an item id"); } return mCursor.getLong(mRowIDColumn); } public void swapCursor(Cursor newCursor) { if (newCursor == mCursor) { return; } if (newCursor != null) { mCursor = newCursor; mRowIDColumn = mCursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID); notifyDataSetChanged(); } else { notifyItemRangeRemoved(0, getItemCount()); mCursor = null; mRowIDColumn = -1; } } public Cursor getCursor() { return mCursor; } private boolean isDataValid(Cursor cursor) { return cursor != null && !cursor.isClosed(); } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/ui/adapter/SelectedPhotoAdapter.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.ui.adapter; import android.content.Context; import android.net.Uri; import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.ImageView; import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; import com.laevatein.internal.entity.ItemViewResources; import com.laevatein.internal.model.SelectedUriCollection; import com.laevatein.internal.ui.helper.SelectedGridViewHelper; import java.util.List; /** * @author KeithYokoma * @version 1.0.0 * @hide * @since 2014/03/27 */ public class SelectedPhotoAdapter extends RecyclerView.Adapter { public static final String TAG = SelectedPhotoAdapter.class.getSimpleName(); private final Context mContext; private final ItemViewResources mResources; private final SelectedUriCollection mCollection; private CheckStateListener mListener; private List mUris; public SelectedPhotoAdapter(Context context, ItemViewResources resources, SelectedUriCollection collection) { mContext = context; mResources = resources; mCollection = collection; mUris = collection.asList(); } @Override public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { View v = LayoutInflater.from(viewGroup.getContext()).inflate(mResources.getLayoutId(), viewGroup, false); return new ViewHolder(v, mResources); } @Override public void onBindViewHolder(ViewHolder holder, final int position) { final Uri uri = mUris.get(position); holder.checkBox.setChecked(mCollection.isSelected(uri)); holder.checkBox.setText(null); holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { SelectedGridViewHelper.syncCollection(mCollection, uri, isChecked); SelectedGridViewHelper.callCheckStateListener(mListener); } }); Glide.with(mContext).load(uri) .apply(new RequestOptions().fitCenter().centerCrop()) .into(holder.thumbnail); } @Override public int getItemCount() { return mUris.size(); } public void registerCheckStateListener(CheckStateListener listener) { mListener = listener; } public void unregisterCheckStateListener() { mListener = null; } public interface CheckStateListener { void onUpdate(); } static class ViewHolder extends RecyclerView.ViewHolder { private final ImageView thumbnail; private final CheckBox checkBox; ViewHolder(View v, ItemViewResources resources) { super(v); thumbnail = v.findViewById(resources.getImageViewId()); checkBox = v.findViewById(resources.getCheckBoxId()); } } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/ui/helper/AlbumListViewHelper.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.ui.helper; import android.database.Cursor; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import androidx.cursoradapter.widget.CursorAdapter; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; import android.widget.AdapterView; import android.widget.HeaderViewListAdapter; import android.widget.ListView; import com.amalgam.os.HandlerUtils; import com.laevatein.R; import com.laevatein.internal.entity.Album; import com.laevatein.internal.misc.ui.FragmentUtils; import com.laevatein.internal.model.DevicePhotoAlbumCollection; import com.laevatein.internal.ui.AlbumListFragment; import com.laevatein.internal.ui.adapter.DevicePhotoAlbumAdapter; /** * @author KeithYokoma * @version 1.0.0 * @hide * @since 2014/03/20 */ public final class AlbumListViewHelper { private AlbumListViewHelper() { throw new AssertionError("oops! the utility class is about to be instantiated..."); } public static void setUpHeader(Fragment fragment) { TypedValue value = new TypedValue(); fragment.getContext().getTheme().resolveAttribute(R.attr.l_drawerHeaderLayout, value, true); if (value.resourceId == 0) { return; } ListView listView = (ListView) FragmentUtils.findViewById(fragment, R.id.l_list_album); LayoutInflater inflater = LayoutInflater.from(fragment.getContext()); View header = inflater.inflate(value.resourceId, listView, false); listView.addHeaderView(header); } public static void setUpListView(Fragment fragment, AdapterView.OnItemClickListener listener) { ListView listView = (ListView) FragmentUtils.findViewById(fragment, R.id.l_list_album); listView.setOnItemClickListener(listener); listView.setAdapter(new DevicePhotoAlbumAdapter(fragment.getActivity(), null)); } public static void setCursor(Fragment fragment, Cursor cursor) { ListView listView = (ListView) FragmentUtils.findViewById(fragment, R.id.l_list_album); CursorAdapter adapter; if (listView.getHeaderViewsCount() > 0) { adapter = (CursorAdapter) ((HeaderViewListAdapter) listView.getAdapter()).getWrappedAdapter(); } else { adapter = (CursorAdapter) listView.getAdapter(); } adapter.swapCursor(cursor); } public static void callOnSelect(AlbumListFragment.OnDirectorySelectListener listener, Cursor cursor) { Album album = Album.valueOf(cursor); listener.onSelect(album); } public static void callOnDefaultSelect(final Fragment fragment, final AlbumListFragment.OnDirectorySelectListener listener, final Cursor cursor, final String albumId, final DevicePhotoAlbumCollection collection) { HandlerUtils.getMainHandler().post(new Runnable() { @Override public void run() { FragmentActivity fragmentActivity = fragment.getActivity(); if (fragmentActivity == null) { return; } FragmentManager manager = fragmentActivity.getSupportFragmentManager(); Fragment f = manager.findFragmentById(R.id.l_container_grid_fragment); if (f != null) { return; } cursor.moveToFirst(); do { Album item = Album.valueOf(cursor); if (item.getId().equals(albumId)) { ListView listView = (ListView) FragmentUtils.findViewById(fragment, R.id.l_list_album); int position = cursor.getPosition() + listView.getHeaderViewsCount(); collection.setStateCurrentSelection(position); setCheckedState(fragment, position); listener.onSelect(item); return; } } while (cursor.moveToNext()); cursor.moveToFirst(); callOnSelect(listener, cursor); ListView listView = (ListView) FragmentUtils.findViewById(fragment, R.id.l_list_album); setCheckedState(fragment, listView.getHeaderViewsCount()); } }); } public static void setCheckedState(Fragment fragment, int position) { ListView listView = (ListView) FragmentUtils.findViewById(fragment, R.id.l_list_album); listView.setItemChecked(position, true); } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/ui/helper/PhotoGridViewHelper.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.ui.helper; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; import android.view.View; import android.widget.CheckBox; import android.widget.TextView; import com.laevatein.R; import com.laevatein.internal.entity.Album; import com.laevatein.internal.entity.ErrorViewResources; import com.laevatein.internal.entity.ErrorViewSpec; import com.laevatein.internal.entity.Item; import com.laevatein.internal.entity.UncapableCause; import com.laevatein.internal.entity.ViewResourceSpec; import com.laevatein.internal.model.SelectedUriCollection; import com.laevatein.internal.ui.PhotoGridFragment; import com.laevatein.internal.ui.adapter.AlbumPhotoAdapter; import com.laevatein.internal.ui.adapter.RecyclerViewCursorAdapter; import com.laevatein.internal.ui.widget.PhotoDecoration; import com.laevatein.internal.utils.ErrorViewUtils; import com.laevatein.internal.utils.MediaStoreUtils; import com.laevatein.ui.ImagePreviewActivity; import com.laevatein.ui.PhotoSelectionActivity; import java.util.ArrayList; import java.util.List; /** * @author KeithYokoma * @version 1.0.0 * @hide * @since 2014/03/24 */ public final class PhotoGridViewHelper { private PhotoGridViewHelper() { throw new AssertionError("oops! the utility class is about to be instantiated..."); } public static SelectedUriCollection getSelectedPhotoSet(Fragment fragment) { return ((PhotoSelectionActivity) fragment.getActivity()).getCollection(); } public static void setUpGridView(Fragment fragment, ViewResourceSpec resources, SelectedUriCollection collection, AlbumPhotoAdapter.BindViewListener listener) { RecyclerView recyclerView = fragment.getView().findViewById(R.id.l_recyclerview); int spanCount = resources.getItemViewResources().getSpanCount(); recyclerView.setLayoutManager(new GridLayoutManager(fragment.getContext(), resources.getItemViewResources().getSpanCount())); recyclerView.setHasFixedSize(true); recyclerView.setDrawingCacheEnabled(true); recyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_LOW); AlbumPhotoAdapter adapter = new AlbumPhotoAdapter(fragment.getActivity(), resources, collection, listener); adapter.registerCheckStateListener((AlbumPhotoAdapter.CheckStateListener) fragment); int spacing = fragment.getResources().getDimensionPixelSize(R.dimen.grid_spacing); recyclerView.addItemDecoration(new PhotoDecoration(spanCount, spacing, false)); recyclerView.setAdapter(adapter); TextView emptyMessage = fragment.getView().findViewById(R.id.l_label_empty); if (adapter.getItemCount() == 0) { emptyMessage.setVisibility(View.VISIBLE); emptyMessage.setText(R.string.l_empty_photo); } else { emptyMessage.setVisibility(View.GONE); } } public static void tearDownGridView(Fragment fragment) { RecyclerView recyclerView = fragment.getView().findViewById(R.id.l_recyclerview); AlbumPhotoAdapter adapter = (AlbumPhotoAdapter) recyclerView.getAdapter(); adapter.unregisterCheckStateListener(); } public static void setCursor(Fragment fragment, Cursor cursor) { RecyclerView recyclerView = fragment.getView().findViewById(R.id.l_recyclerview); RecyclerViewCursorAdapter adapter = (RecyclerViewCursorAdapter) recyclerView.getAdapter(); adapter.swapCursor(cursor); TextView emptyMessage = fragment.getView().findViewById(R.id.l_label_empty); if (adapter.getItemCount() == 0) { emptyMessage.setVisibility(View.VISIBLE); emptyMessage.setText(R.string.l_empty_photo); } else { emptyMessage.setVisibility(View.GONE); } } public static void refreshView(Fragment fragment) { RecyclerView recyclerView = fragment.getView().findViewById(R.id.l_recyclerview); RecyclerViewCursorAdapter adapter = (RecyclerViewCursorAdapter) recyclerView.getAdapter(); adapter.notifyDataSetChanged(); } public static void callPreview(Context context, Item item, List checked) { PhotoSelectionActivity activity = (PhotoSelectionActivity) context; ViewResourceSpec resources = activity.getIntent().getParcelableExtra(PhotoSelectionActivity.EXTRA_VIEW_SPEC); Intent intent = new Intent(context, resources.getPreviewActivityClass()); Fragment fragment = activity.getSupportFragmentManager().findFragmentById(R.id.l_container_grid_fragment); Album album = fragment.getArguments().getParcelable(PhotoGridFragment.ARGS_ALBUM); intent.putExtra(ImagePreviewActivity.EXTRA_ALBUM, album); intent.putExtra(ImagePreviewActivity.EXTRA_ITEM, item); intent.putExtra(ImagePreviewActivity.EXTRA_ERROR_SPEC, activity.getIntent().getParcelableExtra(PhotoSelectionActivity.EXTRA_ERROR_SPEC)); intent.putExtra(ImagePreviewActivity.EXTRA_SELECTION_SPEC, activity.getIntent().getParcelableExtra(PhotoSelectionActivity.EXTRA_SELECTION_SPEC)); intent.putExtra(ImagePreviewActivity.EXTRA_VIEW_SPEC, activity.getIntent().getParcelableExtra(PhotoSelectionActivity.EXTRA_VIEW_SPEC)); intent.putParcelableArrayListExtra(ImagePreviewActivity.EXTRA_DEFAULT_CHECKED, (ArrayList) checked); activity.startActivityForResult(intent, PhotoSelectionActivity.REQUEST_CODE_PREVIEW); } public static void callCamera(Context context, String fileProviderAuthorities) { PhotoSelectionActivity activity = (PhotoSelectionActivity) context; MediaStoreUtils utils = activity.getMediaStoreUtils(); String preparedUri = utils.invokeCameraCapture(activity, fileProviderAuthorities, PhotoSelectionActivity.REQUEST_CODE_CAPTURE); activity.prepareCapture(preparedUri); } public static void syncCheckState(Context context, SelectedUriCollection collection, Item item, CheckBox checkBox) { Uri uri = item.buildContentUri(); if (collection.isSelected(uri)) { removeSelection(collection, uri, checkBox); } else { addSelection(context, collection, uri, checkBox); } } public static void removeSelection(SelectedUriCollection collection, Uri uri, CheckBox checkBox) { collection.remove(uri); checkBox.setChecked(false); } public static void addSelection(Context context, SelectedUriCollection collection, Uri uri, CheckBox checkBox) { UncapableCause cause = collection.isAcceptable(uri); FragmentActivity activity = (FragmentActivity) context; ErrorViewSpec spec = activity.getIntent().getParcelableExtra(PhotoSelectionActivity.EXTRA_ERROR_SPEC); if (cause == null) { ErrorViewResources countSpec = spec.getCountOverErrorSpec(); collection.add(uri); if (collection.isCountOver() && !countSpec.isNoView()) { ErrorViewUtils.showErrorView(activity, countSpec); collection.remove(uri); checkBox.setChecked(false); return; } checkBox.setChecked(true); } else { checkBox.setChecked(false); ErrorViewUtils.showErrorView(activity, cause.getErrorResources(spec)); } } public static void callCheckStateListener(AlbumPhotoAdapter.CheckStateListener listener) { if (listener == null) { return; } listener.onUpdate(); } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/ui/helper/PhotoSelectionActivityDrawerToggle.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.ui.helper; import android.app.Activity; import androidx.fragment.app.FragmentActivity; import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.widget.Toolbar; import android.view.View; import com.laevatein.R; /** * @author KeithYokoma * @since 2014/03/24 * @version 1.0.0 * @hide */ public class PhotoSelectionActivityDrawerToggle extends ActionBarDrawerToggle { private FragmentActivity mActivity; /** * Construct a new ActionBarDrawerToggle. * *

The given {@link android.app.Activity} will be linked to the specified {@link DrawerLayout}. * The provided drawer indicator drawable will animate slightly off-screen as the drawer * is opened, indicating that in the open state the drawer will move off-screen when pressed * and in the closed state the drawer will move on-screen when pressed.

* *

String resources must be provided to describe the open/close drawer actions for * accessibility services.

* * @param activity The Activity hosting the drawer * @param drawerLayout The DrawerLayout to link to the given Activity's ActionBar */ public PhotoSelectionActivityDrawerToggle(Activity activity, DrawerLayout drawerLayout) { super(activity, drawerLayout, R.string.l_content_desc_open_drawer, R.string.l_content_desc_close_drawer); mActivity = (FragmentActivity) activity; drawerLayout.setDrawerShadow(R.drawable.l_drawer_shadow, GravityCompat.START); } public PhotoSelectionActivityDrawerToggle(Activity activity, DrawerLayout drawerLayout,Toolbar toolbar) { super(activity, drawerLayout, toolbar, R.string.l_content_desc_open_drawer, R.string.l_content_desc_close_drawer); mActivity = (FragmentActivity) activity; drawerLayout.setDrawerShadow(R.drawable.l_drawer_shadow, GravityCompat.START); } public void setUpActionBar(ActionBar actionBar) { if (actionBar == null) { return; // FIXME for now just check null or not to avoid NPE, consider compatibility layer to deal with tool bar later on. } actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setHomeButtonEnabled(true); } @Override public void onDrawerClosed(View drawerView) { super.onDrawerClosed(drawerView); mActivity.invalidateOptionsMenu(); } @Override public void onDrawerOpened(View drawerView) { super.onDrawerOpened(drawerView); mActivity.invalidateOptionsMenu(); } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/ui/helper/PhotoSelectionViewHelper.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.ui.helper; import com.google.android.material.appbar.AppBarLayout; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.RelativeLayout; import com.laevatein.R; import com.laevatein.internal.entity.Album; import com.laevatein.internal.entity.CounterViewResources; import com.laevatein.internal.entity.ErrorViewResources; import com.laevatein.internal.entity.ErrorViewSpec; import com.laevatein.internal.entity.ViewResourceSpec; import com.laevatein.internal.model.SelectedUriCollection; import com.laevatein.internal.ui.AlbumListFragment; import com.laevatein.internal.ui.PhotoGridFragment; import com.laevatein.internal.ui.SelectedPhotoGridFragment; import com.laevatein.ui.PhotoSelectionActivity; /** * @author KeithYokoma * @since 2014/03/25 * @version 1.0.0 * @hide */ public final class PhotoSelectionViewHelper { private PhotoSelectionViewHelper() { throw new AssertionError("oops! the utility class is about to be instantiated..."); } public static void setUpActivity(PhotoSelectionActivity activity) { ViewResourceSpec spec = activity.getIntent().getParcelableExtra(PhotoSelectionActivity.EXTRA_VIEW_SPEC); if (spec != null && spec.needActivityOrientationRestriction()) { activity.setRequestedOrientation(spec.getActivityOrientation()); } } public static void setUpCounter(PhotoSelectionActivity activity) { ViewResourceSpec spec = activity.getIntent().getParcelableExtra(PhotoSelectionActivity.EXTRA_VIEW_SPEC); if (spec.getCounterViewResources().getViewType() == CounterViewResources.LAYOUT_APP_BAR) { AppBarLayout appBar = activity.findViewById(R.id.l_app_bar); LayoutInflater.from(activity).inflate(R.layout.l_view_counter, appBar); } else { RelativeLayout content = activity.findViewById(R.id.l_content); View counter = LayoutInflater.from(activity).inflate(R.layout.l_view_counter, content, false); RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); content.addView(counter, params); View view = activity.findViewById(R.id.l_container_grid_fragment); RelativeLayout.LayoutParams params2 = (RelativeLayout.LayoutParams) view.getLayoutParams(); params2.addRule(RelativeLayout.ABOVE, R.id.l_fragment_selected_count); } } public static void refreshOptionsMenuState(PhotoSelectionActivity activity, SelectedUriCollection collection, Menu menu) { if (collection == null) { return; } ErrorViewSpec spec = activity.getIntent().getParcelableExtra(PhotoSelectionActivity.EXTRA_ERROR_SPEC); ErrorViewResources res = spec.getCountUnderErrorSpec(); MenuItem select = menu.findItem(R.id.action_finish_select); updateSelectMenuState(select, res, collection, activity.isDrawerOpen()); } public static void updateSelectMenuState(MenuItem item, ErrorViewResources res, SelectedUriCollection collection, boolean drawerOpen) { if (item == null) { return; } item.setVisible(!drawerOpen); if (res.isNoView()) { item.setEnabled(!collection.isEmpty() && collection.isCountInRange()); } else { item.setEnabled(!collection.isEmpty()); } } public static void setPhotoGridFragment(FragmentActivity activity, DrawerLayout drawer, Album album) { Fragment fragment = PhotoGridFragment.newInstance(album); FragmentManager manager = activity.getSupportFragmentManager(); manager.beginTransaction() .replace(R.id.l_container_grid_fragment, fragment, PhotoGridFragment.TAG) .commit(); if (((PhotoSelectionActivity) activity).isDrawerOpen()) { drawer.closeDrawers(); } else { ViewResourceSpec spec = activity.getIntent().getParcelableExtra(PhotoSelectionActivity.EXTRA_VIEW_SPEC); if (spec.openDrawer()) { drawer.openDrawer(GravityCompat.START); } } } public static void setAlbumList(FragmentActivity activity, String albumId) { Fragment fragment = AlbumListFragment.newInstance(albumId); FragmentManager manager = activity.getSupportFragmentManager(); manager.beginTransaction() .add(R.id.l_container_album_list, fragment, PhotoGridFragment.TAG) .commit(); } public static void setSelectedGridFragment(FragmentActivity activity) { Fragment fragment = SelectedPhotoGridFragment.newInstance(); FragmentManager manager = activity.getSupportFragmentManager(); manager.beginTransaction() .replace(R.id.l_container_grid_fragment, fragment, PhotoGridFragment.TAG) .commit(); } public static void refreshGridView(FragmentActivity activity) { FragmentManager manager = activity.getSupportFragmentManager(); Fragment fragment = manager.findFragmentByTag(PhotoGridFragment.TAG); if (fragment instanceof PhotoGridFragment) { ((PhotoGridFragment) fragment).refreshGrid(); } } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/ui/helper/PreviewHelper.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.ui.helper; import android.app.Activity; import android.content.Intent; import android.net.Uri; import androidx.viewpager.widget.ViewPager; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import android.view.Menu; import android.view.MenuItem; import android.widget.CheckBox; import android.widget.CompoundButton; import com.laevatein.R; import com.laevatein.internal.entity.ErrorViewResources; import com.laevatein.internal.entity.ErrorViewSpec; import com.laevatein.internal.entity.Item; import com.laevatein.internal.entity.PreviewViewResources; import com.laevatein.internal.entity.SelectionSpec; import com.laevatein.internal.entity.UncapableCause; import com.laevatein.internal.entity.ViewResourceSpec; import com.laevatein.ui.ImagePreviewActivity; import com.laevatein.internal.ui.adapter.PreviewPagerAdapter; import com.laevatein.internal.utils.ErrorViewUtils; import com.laevatein.internal.utils.PhotoMetadataUtils; import java.util.ArrayList; import java.util.List; /** * @author KeithYokoma * @version 1.0.0 * @hide * @since 2014/03/24 */ public final class PreviewHelper { private PreviewHelper() { throw new AssertionError("oops! the utility class is about to be instantiated..."); } public static void setUpActivity(ImagePreviewActivity activity) { ViewResourceSpec spec = activity.getIntent().getParcelableExtra(ImagePreviewActivity.EXTRA_VIEW_SPEC); if (spec != null && spec.needActivityOrientationRestriction()) { activity.setRequestedOrientation(spec.getActivityOrientation()); } PreviewViewResources previewViewResources; if (spec != null && spec.getPreviewViewResources() != null) { previewViewResources = spec.getPreviewViewResources(); } else { previewViewResources = PreviewViewResources.getDefault(); } PreviewPagerAdapter adapter = new PreviewPagerAdapter(activity.getSupportFragmentManager(), previewViewResources, activity); ViewPager pager = activity.findViewById(R.id.l_pager); pager.setAdapter(adapter); } public static void setUpActionBar(AppCompatActivity activity) { Toolbar toolbar = activity.findViewById(R.id.l_toolbar); toolbar.setTitle(activity.getApplicationContext().getString(R.string.l_detail_photo_title)); activity.setSupportActionBar(toolbar); ActionBar actionBar = activity.getSupportActionBar(); actionBar.setHomeButtonEnabled(true); actionBar.setDisplayHomeAsUpEnabled(true); } public static void setUpActionItem(final ImagePreviewActivity activity, Menu menu) { final MenuItem item = menu.findItem(R.id.l_action_selection_state); if (item == null) { return; } final Item photo = activity.getIntent().getParcelableExtra(ImagePreviewActivity.EXTRA_ITEM); final SelectionSpec spec = activity.getIntent().getParcelableExtra(ImagePreviewActivity.EXTRA_SELECTION_SPEC); final ErrorViewSpec errorSpec = activity.getIntent().getParcelableExtra(ImagePreviewActivity.EXTRA_ERROR_SPEC); item.setActionView(R.layout.l_action_layout_checkbox); final CheckBox checkBox = item.getActionView().findViewById(R.id.l_default_check_box); checkBox.setChecked(activity.getStateHolder().isChecked(photo.buildContentUri())); checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { ViewPager pager = activity.findViewById(R.id.l_pager); Uri currentUri = ((PreviewPagerAdapter) pager.getAdapter()).getUri(pager.getCurrentItem()); if (!isChecked) { activity.getStateHolder().setChecked(currentUri, false); return; } UncapableCause cause = PhotoMetadataUtils .isAcceptable(activity, spec, currentUri); int currentCount = activity.getStateHolder().getCheckedCount(); if (!activity.getStateHolder().isChecked(currentUri) && currentCount + 1 > spec.getMaxSelectable()) { cause = UncapableCause.OVER_COUNT; } if (cause == null) { activity.getStateHolder().setChecked(currentUri, true); return; } ErrorViewResources error = cause.getErrorResources(errorSpec); ErrorViewUtils.showErrorView(activity, error); checkBox.setChecked(false); activity.getStateHolder().setChecked(currentUri, false); } }); } public static void sendBackResult(ImagePreviewActivity activity) { Intent intent = new Intent(); List checked = activity.getStateHolder().getAllChecked(); intent.putParcelableArrayListExtra(ImagePreviewActivity.EXTRA_RESULT_CHECKED, (ArrayList) checked); activity.setResult(Activity.RESULT_OK, intent); } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/ui/helper/SelectedCountViewHelper.java ================================================ package com.laevatein.internal.ui.helper; import android.view.View; import android.widget.TextView; import com.laevatein.R; import com.laevatein.internal.entity.ViewResourceSpec; import com.laevatein.internal.misc.ui.FragmentUtils; import com.laevatein.internal.ui.SelectedCountFragment; import com.laevatein.ui.PhotoSelectionActivity; /** * @author KeithYokoma * @version 1.0.0 * @hide * @since 2014/04/04 */ public final class SelectedCountViewHelper { private SelectedCountViewHelper() { throw new AssertionError("oops! the utility class is about to be instantiated..."); } public static void setUpCountView(final SelectedCountFragment fragment) { TextView counter = (TextView) FragmentUtils.findViewById(fragment, R.id.l_container_count_view); ViewResourceSpec spec = FragmentUtils.getIntentParcelableExtra(fragment, PhotoSelectionActivity.EXTRA_VIEW_SPEC); counter.setEnabled(spec.isEnableSelectedView()); counter.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { fragment.getListener().onClickSelectedView(); } }); } public static void updateCountView(PhotoSelectionActivity activity, SelectedCountFragment fragment) { if (activity == null || fragment == null || activity.getCollection() == null || fragment.getView() == null) { return; } ViewResourceSpec spec = FragmentUtils.getIntentParcelableExtra(fragment, PhotoSelectionActivity.EXTRA_VIEW_SPEC); TextView label = (TextView) FragmentUtils.findViewById(fragment, R.id.l_container_count_view); int max = activity.getCollection().maxCount(); int current = activity.getCollection().count(); label.setText(activity.getString(spec.getCounterViewResources().getTextRes(), current, max)); } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/ui/helper/SelectedGridViewHelper.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.ui.helper; import android.net.Uri; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; import android.view.View; import android.widget.TextView; import com.laevatein.R; import com.laevatein.internal.entity.ItemViewResources; import com.laevatein.internal.model.SelectedUriCollection; import com.laevatein.internal.ui.adapter.SelectedPhotoAdapter; import com.laevatein.internal.ui.widget.PhotoDecoration; import com.laevatein.ui.PhotoSelectionActivity; /** * @author keishin.yokomaku * @version 1.0.0 * @hide * @since 2014/03/27 */ public final class SelectedGridViewHelper { private SelectedGridViewHelper() { throw new AssertionError("oops! the utility class is about to be instantiated..."); } public static SelectedUriCollection getSelectedPhotoSet(Fragment fragment) { return ((PhotoSelectionActivity) fragment.getActivity()).getCollection(); } public static void setUpGridView(Fragment fragment, ItemViewResources resources, SelectedUriCollection collection) { RecyclerView recyclerView = fragment.getView().findViewById(R.id.l_recyclerview); int spanCount = resources.getSpanCount(); recyclerView.setLayoutManager(new GridLayoutManager(fragment.getContext(), resources.getSpanCount())); recyclerView.setHasFixedSize(true); recyclerView.setDrawingCacheEnabled(true); recyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_LOW); SelectedPhotoAdapter adapter = new SelectedPhotoAdapter(fragment.getActivity(), resources, collection); adapter.registerCheckStateListener((SelectedPhotoAdapter.CheckStateListener) fragment); int spacing = fragment.getResources().getDimensionPixelSize(R.dimen.grid_spacing); recyclerView.addItemDecoration(new PhotoDecoration(spanCount, spacing, false)); recyclerView.setAdapter(adapter); TextView emptyMessage = fragment.getView().findViewById(R.id.l_label_empty); if (collection.asList().size() == 0) { emptyMessage.setVisibility(View.VISIBLE); emptyMessage.setText(R.string.l_empty_selection); } else { emptyMessage.setVisibility(View.GONE); } } public static void tearDownGridView(Fragment fragment) { RecyclerView recyclerView = fragment.getView().findViewById(R.id.l_recyclerview); SelectedPhotoAdapter adapter = (SelectedPhotoAdapter) recyclerView.getAdapter(); adapter.unregisterCheckStateListener(); } public static void syncCollection(SelectedUriCollection collection, Uri uri, boolean checked) { if (checked) { collection.add(uri); } else { collection.remove(uri); } } public static void callCheckStateListener(SelectedPhotoAdapter.CheckStateListener listener) { if (listener == null) { return; } listener.onUpdate(); } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/ui/helper/options/FinishSelectMenuHandler.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.ui.helper.options; import android.app.Activity; import android.content.Intent; import android.os.Parcelable; import com.laevatein.internal.entity.ErrorViewResources; import com.laevatein.internal.entity.ErrorViewSpec; import com.laevatein.internal.model.SelectedUriCollection; import com.laevatein.internal.utils.ErrorViewUtils; import com.laevatein.ui.PhotoSelectionActivity; import java.util.ArrayList; /** * @author KeithYokoma * @since 2014/03/25 */ public class FinishSelectMenuHandler implements PhotoSelectionOptionsMenuHandler { @Override public boolean handle(PhotoSelectionActivity activity, Void extra) { SelectedUriCollection collection = activity.getCollection(); if (collection.isCountInRange()) { Intent intent = new Intent(); intent.putParcelableArrayListExtra(PhotoSelectionActivity.EXTRA_RESULT_SELECTION, (ArrayList) collection.asList()); activity.setResult(Activity.RESULT_OK, intent); activity.finish(); } else { ErrorViewSpec spec = activity.getIntent().getParcelableExtra(PhotoSelectionActivity.EXTRA_ERROR_SPEC); ErrorViewResources res = spec.getCountUnderErrorSpec(); ErrorViewUtils.showErrorView(activity, res); } return true; } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/ui/helper/options/PhotoSelectionOptionsMenu.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.ui.helper.options; import android.view.MenuItem; import com.laevatein.R; import com.laevatein.internal.misc.ui.helper.options.OptionsMenu; import com.laevatein.ui.PhotoSelectionActivity; /** * @author KeithYokoma * @version 1.0.0 * @hide * @since 2014/03/20 */ public enum PhotoSelectionOptionsMenu implements OptionsMenu { SELECT(R.id.action_finish_select, new FinishSelectMenuHandler()), UNKNOWN(-1, new UnknownMenuHandler()); // null object pattern private final int mMenuId; private final PhotoSelectionOptionsMenuHandler mHandler; PhotoSelectionOptionsMenu(int menuId, PhotoSelectionOptionsMenuHandler handler) { mMenuId = menuId; mHandler = handler; } public static PhotoSelectionOptionsMenu valueOf(MenuItem item) { for (PhotoSelectionOptionsMenu menu : values()) { if (menu.getMenuId() == item.getItemId()) { return menu; } } return UNKNOWN; } @Override public int getMenuId() { return mMenuId; } @Override public PhotoSelectionOptionsMenuHandler getHandler() { return mHandler; } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/ui/helper/options/PhotoSelectionOptionsMenuHandler.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.ui.helper.options; import com.laevatein.internal.misc.ui.helper.options.OptionsMenuHandler; import com.laevatein.ui.PhotoSelectionActivity; /** * @author KeithYokoma * @since 2014/03/20 * @version 1.0.0 * @hide */ public interface PhotoSelectionOptionsMenuHandler extends OptionsMenuHandler {} ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/ui/helper/options/UnknownMenuHandler.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.ui.helper.options; import com.laevatein.ui.PhotoSelectionActivity; /** * @author KeithYokoma * @since 2014/03/25 */ public class UnknownMenuHandler implements PhotoSelectionOptionsMenuHandler { @Override public boolean handle(PhotoSelectionActivity activity, Void extra) { return false; } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/ui/widget/PhotoDecoration.java ================================================ package com.laevatein.internal.ui.widget; import android.graphics.Rect; import androidx.recyclerview.widget.RecyclerView; import android.view.View; public class PhotoDecoration extends RecyclerView.ItemDecoration { private int mSpanCount; private int mSpacing; private boolean mIncludeEdge; public PhotoDecoration(int spanCount, int spacing, boolean includeEdge) { this.mSpanCount = spanCount; this.mSpacing = spacing; this.mIncludeEdge = includeEdge; } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { int position = parent.getChildAdapterPosition(view); int column = position % mSpanCount; if (mIncludeEdge) { outRect.left = mSpacing - column * mSpacing / mSpanCount; outRect.right = (column + 1) * mSpacing / mSpanCount; if (position < mSpanCount) { outRect.top = mSpacing; } outRect.bottom = mSpacing; } else { outRect.left = column * mSpacing / mSpanCount; outRect.right = mSpacing - (column + 1) * mSpacing / mSpanCount; if (position >= mSpanCount) { outRect.top = mSpacing; } } } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/utils/ErrorViewUtils.java ================================================ package com.laevatein.internal.utils; import com.google.android.material.snackbar.Snackbar; import androidx.fragment.app.FragmentActivity; import android.widget.Toast; import com.amalgam.app.SupportSimpleAlertDialogFragment; import com.laevatein.internal.entity.DialogResources; import com.laevatein.internal.entity.ErrorViewResources; import com.laevatein.internal.misc.ui.ConfirmationDialogFragment; /** * @author KeithYokoma * @version 1.0.0 * @hide * @since 2014/04/07 */ public final class ErrorViewUtils { private ErrorViewUtils() { throw new AssertionError("oops! the utility class is about to be instantiated..."); } public static void showErrorView(FragmentActivity activity, ErrorViewResources resources) { if (resources.isNoView()) { return; } if (resources.getViewType() == ErrorViewResources.ViewType.DIALOG) { if (resources.getTitleId() == -1) { SupportSimpleAlertDialogFragment.newInstance(resources.getMessageId()) .show(activity.getSupportFragmentManager(), SupportSimpleAlertDialogFragment.TAG); } else { SupportSimpleAlertDialogFragment.newInstance(resources.getTitleId(), resources.getMessageId()) .show(activity.getSupportFragmentManager(), SupportSimpleAlertDialogFragment.TAG); } } else if (resources.getViewType() == ErrorViewResources.ViewType.TOAST) { Toast.makeText(activity.getApplicationContext(), resources.getMessageId(), Toast.LENGTH_LONG).show(); } else if (resources.getViewType() == ErrorViewResources.ViewType.SNACKBAR) { Snackbar.make(activity.findViewById(android.R.id.content), resources.getMessageId(), Snackbar.LENGTH_LONG).show(); } } public static void showConfirmDialog(FragmentActivity activity, DialogResources resources) { if (resources.getTitleId() == -1) { ConfirmationDialogFragment.newInstance(resources.getMessageId()) .show(activity.getSupportFragmentManager(), ConfirmationDialogFragment.TAG); } else { ConfirmationDialogFragment.newInstance(resources.getTitleId(), resources.getMessageId()) .show(activity.getSupportFragmentManager(), ConfirmationDialogFragment.TAG); } } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/utils/ExifInterfaceUtils.java ================================================ package com.laevatein.internal.utils; import androidx.exifinterface.media.ExifInterface; import android.text.TextUtils; import android.util.Log; import java.io.IOException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimeZone; /** * This class is based on this source code. * https://github.com/mixi-inc/Android-Device-Compatibility/blob/master/AndroidDeviceCompatibility/src/main/java/jp/mixi/compatibility/android/media/ExifInterfaceCompat.java *

* License * see: https://github.com/mixi-inc/Android-Device-Compatibility#license */ public class ExifInterfaceUtils { public static final String TAG = ExifInterfaceUtils.class.getSimpleName(); /** * Do not instantiate this class. */ private ExifInterfaceUtils() { } private static Date getExifDateTime(String filepath) { ExifInterface exif = null; try { exif = new ExifInterface(filepath); } catch (IOException ex) { Log.e(TAG, "cannot read exif", ex); return null; } String date = exif.getAttribute(ExifInterface.TAG_DATETIME); if (TextUtils.isEmpty(date)) { return null; } try { SimpleDateFormat formatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss"); formatter.setTimeZone(TimeZone.getTimeZone("UTC")); return formatter.parse(date); } catch (ParseException e) { Log.d(TAG, "failed to parse date taken", e); } return null; } /** * Read exif info and get datetime value of the photo. * * @param filepath to get datetime * @return when a photo taken. */ public static long getExifDateTimeInMillis(String filepath) { Date datetime = getExifDateTime(filepath); if (datetime == null) { return -1; } return datetime.getTime(); } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/utils/MediaStoreUtils.java ================================================ package com.laevatein.internal.utils; import android.annotation.TargetApi; import android.app.Activity; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.os.Handler; import android.provider.MediaStore; import androidx.exifinterface.media.ExifInterface; import androidx.core.content.FileProvider; import android.util.Log; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.channels.FileChannel; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; /** * Compatibility class for the issue of various implementations of camera feature. *

* This class is based on this source code. * https://github.com/mixi-inc/Android-Device-Compatibility/blob/master/AndroidDeviceCompatibility/src/main/java/jp/mixi/compatibility/android/provider/MediaStoreCompat.java *

* License * see: https://github.com/mixi-inc/Android-Device-Compatibility#license */ public class MediaStoreUtils { public static final String TAG = MediaStoreUtils.class.getSimpleName(); private static final String MEDIA_FILE_NAME_FORMAT = "yyyyMMdd_HHmmss"; private static final String MEDIA_FILE_EXTENSION = ".jpg"; private static final String MEDIA_FILE_PREFIX = "IMG_"; private final String MEDIA_FILE_DIRECTORY; private Context mContext; private ContentObserver mObserver; private ArrayList mRecentlyUpdatedPhotos; /** * Prepares to deal with various implementations of camera feature and {@link MediaStore}. * You should call {@link MediaStoreUtils#destroy()} on destroying context. * * @param context a context * @param handler a handler */ public MediaStoreUtils(Context context, Handler handler) { mContext = context; MEDIA_FILE_DIRECTORY = context.getPackageName(); mObserver = new ContentObserver(handler) { @Override public void onChange(boolean selfChange) { super.onChange(selfChange); updateLatestPhotos(); } }; mContext.getContentResolver().registerContentObserver( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, mObserver); } /** * Checks whether the device has a camera feature or not. * * @param context a context to check for camera feature. * @return true if the device has a camera feature. false otherwise. */ public static final boolean hasCameraFeature(Context context) { PackageManager pm = context.getApplicationContext().getPackageManager(); return pm.hasSystemFeature(PackageManager.FEATURE_CAMERA); } /** * Invokes a camera capture activity. * * @param activity the caller of the camera capture activity. * @param requestCode activity result handling id. * @return a file name to be saved as. */ public String invokeCameraCapture(Activity activity, String fileProviderAuthorities, int requestCode) { if (!hasCameraFeature(mContext)) return null; File toSave = getOutputFile(); if (toSave == null) return null; Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.putExtra(MediaStore.EXTRA_OUTPUT, FileProvider.getUriForFile(activity, fileProviderAuthorities, toSave)); intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1); activity.startActivityForResult(intent, requestCode); return toSave.toString(); } /** * Should be called on destroying context to dereference to the {@link ContentObserver}. */ public void destroy() { mContext.getContentResolver().unregisterContentObserver(mObserver); } /** * @param data the result {@link Intent} of a camera activity. * @param preparedUri a prepared photo uri to fetch photo data. * @return captured photo {@link Uri} */ public Uri getCapturedPhotoUri(Intent data, String preparedUri) { Uri captured = null; if (data != null) { captured = data.getData(); if (captured == null) data.getParcelableExtra(Intent.EXTRA_STREAM); } File prepared = new File(preparedUri.toString()); if (captured == null || captured.equals(Uri.fromFile(prepared))) { captured = findPhotoFromRecentlyTaken(prepared); if (captured == null) { captured = storeImage(prepared); prepared.delete(); } else { // Found. Compare the destination path // and delete app-private one if there is any copy outside of app-private directory. String realPath = getPathFromUri( mContext.getContentResolver(), captured); if (realPath != null) { if (!prepared.equals(new File(realPath))) prepared.delete(); } } } return captured; } /** * Deletes unnecessary files if exist. * * @param uri to delete. */ public void cleanUp(String uri) { File file = new File(uri.toString()); if (file.exists()) file.delete(); } /** * Obtains a captured photo file path from content {@link Uri} of a {@link MediaStore} * * @param resolver a content resolver * @param contentUri a content uri * @return captured photo file path string */ public static String getPathFromUri(ContentResolver resolver, Uri contentUri) { final String dataColumn = MediaStore.MediaColumns.DATA; Cursor cursor = null; try { cursor = resolver.query(contentUri, new String[]{dataColumn}, null, null, null); if (cursor == null || !cursor.moveToFirst()) return null; final int index = cursor.getColumnIndex(dataColumn); return cursor.getString(index); } finally { if (cursor != null) cursor.close(); } } /** * Copies file streams. * * @param is input stream * @param os output stream * @return the number of bytes. * @throws IOException if something wrong with I/O. */ public static long copyFileStream(FileInputStream is, FileOutputStream os) throws IOException { FileChannel srcChannel = null; FileChannel destChannel = null; try { srcChannel = is.getChannel(); destChannel = os.getChannel(); return srcChannel.transferTo(0, srcChannel.size(), destChannel); } finally { if (srcChannel != null) srcChannel.close(); if (destChannel != null) destChannel.close(); } } /** * Obtains file {@link Uri} that refers to a recently taken photo. * * @param file to search * @return photo file uri. */ private Uri findPhotoFromRecentlyTaken(File file) { if (mRecentlyUpdatedPhotos == null) updateLatestPhotos(); long filesize = file.length(); long taken = ExifInterfaceUtils.getExifDateTimeInMillis(file.getAbsolutePath()); int maxPoint = 0; PhotoContent maxItem = null; for (PhotoContent item : mRecentlyUpdatedPhotos) { int point = 0; if (item.size == filesize) point++; if (item.taken == taken) point++; if (point > maxPoint) { maxPoint = point; maxItem = item; } } if (maxItem != null) { generateThumbnails(maxItem.id); return ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, maxItem.id); } return null; } /** * Stores a file to media store with photo orientation and date that taken. * * @param file to store * @return a stored file uri */ private Uri storeImage(File file) { // store image to content provider try { // create parameters for Intent with filename ContentValues values = new ContentValues(); values.put(MediaStore.Images.Media.TITLE, file.getName()); values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); values.put(MediaStore.Images.Media.DESCRIPTION, "mixi Photo"); values.put(MediaStore.Images.Media.ORIENTATION, new ExifInterface(file.getAbsolutePath()).getRotationDegrees()); long date = ExifInterfaceUtils.getExifDateTimeInMillis(file.getAbsolutePath()); if (date != -1) values.put(MediaStore.Images.Media.DATE_TAKEN, date); Uri imageUri = mContext.getContentResolver().insert( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); FileOutputStream fos = (FileOutputStream) mContext.getContentResolver().openOutputStream( imageUri); FileInputStream fis = new FileInputStream(file); copyFileStream(fis, fos); fos.close(); fis.close(); generateThumbnails(ContentUris.parseId(imageUri)); return imageUri; } catch (Exception e) { Log.w(TAG, "cannot insert", e); return null; } } /** * Request to update latest photos onto media store. */ private void updateLatestPhotos() { // retrieve the newest five images Cursor c = MediaStore.Images.Media.query(mContext.getContentResolver(), MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[]{ MediaStore.Images.ImageColumns._ID, MediaStore.Images.ImageColumns.DATE_TAKEN, MediaStore.Images.ImageColumns.SIZE, }, null, null, MediaStore.Images.ImageColumns.DATE_ADDED + " DESC"); if (c != null) { try { int count = 0; mRecentlyUpdatedPhotos = new ArrayList(); while (c.moveToNext()) { PhotoContent item = new PhotoContent(); item.id = c.getLong(0); item.taken = c.getLong(1); item.size = c.getInt(2); mRecentlyUpdatedPhotos.add(item); if (++count > 5) break; } } finally { c.close(); } } } /** * Create thumbnail images for a specified photo image. * * @param imageId referes to a photo image. */ private void generateThumbnails(long imageId) { try { MediaStore.Images.Thumbnails.getThumbnail(mContext.getContentResolver(), imageId, MediaStore.Images.Thumbnails.MINI_KIND, null); } catch (NullPointerException e) { // some MediaStores throws NPE, just ignore the result } } /** * Make output file uri based on a external storage directory. * * @return a file */ @TargetApi(Build.VERSION_CODES.FROYO) private File getOutputFile() { File extDir = new File( Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES), MEDIA_FILE_DIRECTORY); if (!extDir.exists()) { if (!extDir.mkdirs()) return null; } String timeStamp = new SimpleDateFormat(MEDIA_FILE_NAME_FORMAT).format(new Date()); return new File(extDir.getPath() + File.separator + MEDIA_FILE_PREFIX + timeStamp + MEDIA_FILE_EXTENSION); } /** * Entity class for photo content. */ private static class PhotoContent { public long id; public long taken; public int size; } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/utils/PhotoMetadataUtils.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.internal.utils; import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; import android.graphics.BitmapFactory; import android.graphics.Point; import android.net.Uri; import android.provider.MediaStore; import androidx.annotation.Nullable; import androidx.exifinterface.media.ExifInterface; import android.util.Log; import com.amalgam.database.CursorUtils; import com.amalgam.io.CloseableUtils; import com.laevatein.MimeType; import com.laevatein.internal.entity.SelectionSpec; import com.laevatein.internal.entity.UncapableCause; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; /** * @author KeithYokoma * @version 1.0.0 * @hide * @since 2014/03/25 */ public final class PhotoMetadataUtils { public static final String TAG = PhotoMetadataUtils.class.getSimpleName(); private static final String SCHEME_CONTENT = "content"; private PhotoMetadataUtils() { throw new AssertionError("oops! the utility class is about to be instantiated..."); } public static int getPixelsCount(ContentResolver resolver, Uri uri) { Point size = getBitmapBound(resolver, uri); return size.x * size.y; } private static Point getBitmapBound(ContentResolver resolver, Uri uri) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; InputStream is = null; try { is = ContentResolverWrapper.openInputStream(resolver, uri); BitmapFactory.decodeStream(is, null, options); // Argument "InputStream" is nullable, so never crash here. } catch (FileNotFoundException e) { return new Point(0, 0); } finally { CloseableUtils.close(is); } int width = options.outWidth; int height = options.outHeight; return new Point(width, height); } public static String getPath(ContentResolver resolver, Uri uri) { if (uri == null) { return null; } if (SCHEME_CONTENT.equals(uri.getScheme())) { Cursor cursor = null; try { cursor = resolver.query(uri, new String[]{MediaStore.Images.ImageColumns.DATA}, null, null, null); if (cursor == null || !cursor.moveToFirst()) { return null; } return cursor.getString(cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA)); } finally { CursorUtils.close(cursor); } } return uri.getPath(); } public static UncapableCause isAcceptable(Context context, SelectionSpec spec, Uri uri) { if (!isSelectableType(context, spec, uri)) { return UncapableCause.FILE_TYPE; } if (!hasUnderAtMostQuality(context, spec, uri)) { return UncapableCause.OVER_QUALITY; } if (!hasOverAtLeastQuality(context, spec, uri)) { return UncapableCause.UNDER_QUALITY; } if (!isSmallerThanMaxSize(context, spec, uri)) { return UncapableCause.OVER_SIZE; } if (!isLargerThanMinSize(context, spec, uri)) { return UncapableCause.UNDER_SIZE; } return null; } public static boolean hasOverAtLeastQuality(Context context, SelectionSpec spec, Uri uri) { if (context == null) { return false; } int pixels = PhotoMetadataUtils.getPixelsCount(context.getContentResolver(), uri); return spec.getMinPixels() <= pixels; } public static boolean hasUnderAtMostQuality(Context context, SelectionSpec spec, Uri uri) { if (context == null) { return false; } int pixels = PhotoMetadataUtils.getPixelsCount(context.getContentResolver(), uri); return pixels <= spec.getMaxPixels(); } public static boolean isLargerThanMinSize(Context context, SelectionSpec spec, Uri uri) { if (context == null) { return false; } Point p = PhotoMetadataUtils.getBitmapBound(context.getContentResolver(), uri); return p.x >= spec.getMinWidthPixels() && p.y >= spec.getMinHeightPixels(); } public static boolean isSmallerThanMaxSize(Context context, SelectionSpec spec, Uri uri) { if (context == null) { return false; } Point p = PhotoMetadataUtils.getBitmapBound(context.getContentResolver(), uri); return p.x <= spec.getMaxWidthPixels() && p.y <= spec.getMaxHeightPixels(); } public static boolean isSelectableType(Context context, SelectionSpec spec, Uri uri) { if (context == null) { return false; } ContentResolver resolver = context.getContentResolver(); for (MimeType type : spec.getMimeTypeSet()) { if (type.checkType(resolver, uri)) { return true; } } return false; } public static boolean shouldRotate(ContentResolver resolver, Uri uri) { ExifInterface exif; try { exif = new ExifInterface(getPath(resolver, uri)); } catch (IOException e) { Log.e(TAG, "could not read exif info of the image: " + uri); return false; } int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1); return orientation == ExifInterface.ORIENTATION_ROTATE_90 || orientation == ExifInterface.ORIENTATION_ROTATE_270; } static final class ContentResolverWrapper { @Nullable static InputStream openInputStream(ContentResolver resolver, Uri uri) throws FileNotFoundException { try { return resolver.openInputStream(uri); } catch (NullPointerException e) { // NPE may occur when `openInputStream` is called on Kyocera's device. // > Attempt to invoke virtual method 'char[] java.lang.String.toCharArray()' on a null object reference // That was "kyocera original" implement, so we cannot get bounds in this cause. return null; } } } } ================================================ FILE: laevatein/src/main/java/com/laevatein/internal/view/PreviewViewPager.java ================================================ package com.laevatein.internal.view; import android.content.Context; import androidx.viewpager.widget.ViewPager; import android.util.AttributeSet; import android.view.View; import it.sephiroth.android.library.imagezoom.ImageViewTouch; /** * Created by hiroyuki.seto on 15/06/16. * * @hide */ public class PreviewViewPager extends ViewPager { public PreviewViewPager(Context context, AttributeSet attrs) { super(context, attrs); } @Override protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) { if (v instanceof ImageViewTouch) { return ((ImageViewTouch) v).canScroll(dx) || super.canScroll(v, checkV, dx, x, y); } return super.canScroll(v, checkV, dx, x, y); } } ================================================ FILE: laevatein/src/main/java/com/laevatein/ui/ImagePreviewActivity.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.ui; import android.content.ContentUris; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.provider.MediaStore; import androidx.viewpager.widget.ViewPager; import androidx.appcompat.app.AppCompatActivity; import android.view.Menu; import android.view.MenuItem; import android.widget.CheckBox; import com.amalgam.os.BundleUtils; import com.laevatein.R; import com.laevatein.internal.entity.Album; import com.laevatein.internal.entity.Item; import com.laevatein.internal.entity.ViewResourceSpec; import com.laevatein.internal.model.AlbumPhotoCollection; import com.laevatein.internal.model.PreviewStateHolder; import com.laevatein.internal.ui.PreviewFragment; import com.laevatein.internal.ui.adapter.PreviewPagerAdapter; import com.laevatein.internal.ui.helper.PreviewHelper; import java.util.ArrayList; import java.util.List; import static com.laevatein.R.id.l_default_check_box; /** * @author KeithYokoma * @version 1.0.0 * @hide * @since 2014/03/24 */ public class ImagePreviewActivity extends AppCompatActivity implements AlbumPhotoCollection.AlbumPhotoCallbacks, PreviewPagerAdapter.OnPrimaryItemSetListener { public static final String EXTRA_ITEM = BundleUtils.buildKey(ImagePreviewActivity.class, "EXTRA_ITEM"); public static final String EXTRA_ALBUM = BundleUtils.buildKey(ImagePreviewActivity.class, "EXTRA_ALBUM"); public static final String EXTRA_ERROR_SPEC = BundleUtils.buildKey(ImagePreviewActivity.class, "EXTRA_ERROR_SPEC"); public static final String EXTRA_SELECTION_SPEC = BundleUtils.buildKey(ImagePreviewActivity.class, "EXTRA_SELECTION_SPEC"); public static final String EXTRA_VIEW_SPEC = BundleUtils.buildKey(ImagePreviewActivity.class, "EXTRA_VIEW_SPEC"); public static final String EXTRA_DEFAULT_CHECKED = BundleUtils.buildKey(ImagePreviewActivity.class, "EXTRA_DEFAULT_CHECKED"); public static final String EXTRA_RESULT_CHECKED = BundleUtils.buildKey(ImagePreviewActivity.class, "EXTRA_RESULT_CHECKED"); private final PreviewStateHolder mStateHolder = new PreviewStateHolder(this); private ViewPager mPager; private CheckBox mCheckBox; private final AlbumPhotoCollection mCollection = new AlbumPhotoCollection(); private boolean mIsAlreadySetPosition; private int mPreviousPos = -1; @Override protected void onCreate(Bundle savedInstanceState) { ViewResourceSpec spec = getIntent().getParcelableExtra(EXTRA_VIEW_SPEC); setTheme(spec.getTheme()); super.onCreate(savedInstanceState); setContentView(R.layout.l_activity_preview); mStateHolder.onCreate(); mStateHolder.onRestoreInstanceState(savedInstanceState); PreviewHelper.setUpActivity(this); PreviewHelper.setUpActionBar(this); mPager = findViewById(R.id.l_pager); mCollection.onCreate(this, this); Album album = getIntent().getParcelableExtra(EXTRA_ALBUM); mCollection.load(album); } @Override protected void onSaveInstanceState(Bundle outState) { mStateHolder.onSaveInstanceState(outState); super.onSaveInstanceState(outState); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.l_activity_image_preview, menu); PreviewHelper.setUpActionItem(this, menu); final MenuItem item = menu.findItem(R.id.l_action_selection_state); mCheckBox = item.getActionView().findViewById(l_default_check_box); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { if (R.id.home == item.getItemId() || android.R.id.home == item.getItemId()) { PreviewHelper.sendBackResult(this); finish(); return true; } return super.onOptionsItemSelected(item); } @Override public void onBackPressed() { PreviewHelper.sendBackResult(this); super.onBackPressed(); } @Override protected void onDestroy() { super.onDestroy(); mCollection.onDestroy(); } public PreviewStateHolder getStateHolder() { return mStateHolder; } @Override public void onLoad(Cursor cursor) { List uris = new ArrayList<>(); while (cursor.moveToNext()) { final Item item = Item.valueOf(cursor); Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, item.getId()); uris.add(uri); } PreviewPagerAdapter adapter = (PreviewPagerAdapter) mPager.getAdapter(); adapter.addAll(uris); adapter.notifyDataSetChanged(); if (!mIsAlreadySetPosition) { //onLoad is called many times.. mIsAlreadySetPosition = true; Item selected = getIntent().getParcelableExtra(EXTRA_ITEM); int selectedIndex = uris.indexOf(selected.buildContentUri()); mPager.setCurrentItem(selectedIndex, false); } } @Override public void onReset() { } @Override public void onPrimaryItemSet(int position) { if (!mIsAlreadySetPosition) { return; } PreviewPagerAdapter adapter = (PreviewPagerAdapter) mPager.getAdapter(); if (mPreviousPos != -1 && mPreviousPos != position) { ((PreviewFragment) adapter.instantiateItem(mPager, mPreviousPos)).resetView(); Uri uri = adapter.getUri(position); if (mStateHolder.isChecked(uri)) { mCheckBox.setChecked(true); } else { mCheckBox.setChecked(false); } } mPreviousPos = position; } } ================================================ FILE: laevatein/src/main/java/com/laevatein/ui/PhotoSelectionActivity.java ================================================ /* * Copyright (C) 2014 nohana, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.laevatein.ui; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.net.Uri; import android.os.Bundle; import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import android.view.Menu; import android.view.MenuItem; import android.view.View; import com.amalgam.os.BundleUtils; import com.amalgam.os.HandlerUtils; import com.laevatein.R; import com.laevatein.internal.entity.Album; import com.laevatein.internal.entity.ErrorViewSpec; import com.laevatein.internal.entity.SelectionSpec; import com.laevatein.internal.entity.ViewResourceSpec; import com.laevatein.internal.misc.ui.ConfirmationDialogFragment; import com.laevatein.internal.model.SelectedUriCollection; import com.laevatein.internal.ui.AlbumListFragment; import com.laevatein.internal.ui.SelectedCountFragment; import com.laevatein.internal.ui.adapter.AlbumPhotoAdapter; import com.laevatein.internal.ui.helper.PhotoSelectionActivityDrawerToggle; import com.laevatein.internal.ui.helper.PhotoSelectionViewHelper; import com.laevatein.internal.ui.helper.options.PhotoSelectionOptionsMenu; import com.laevatein.internal.utils.ErrorViewUtils; import com.laevatein.internal.utils.MediaStoreUtils; import java.util.ArrayList; /** * @author KeithYokoma * @version 1.0.0 * @since 2014/03/20 */ public class PhotoSelectionActivity extends AppCompatActivity implements AlbumListFragment.OnDirectorySelectListener, ConfirmationDialogFragment.ConfirmationSelectionListener, SelectedCountFragment.OnShowSelectedClickListener, AlbumPhotoAdapter.BindViewListener { public static final String EXTRA_VIEW_SPEC = BundleUtils.buildKey(PhotoSelectionActivity.class, "EXTRA_VIEW_SPEC"); public static final String EXTRA_RESUME_LIST = BundleUtils.buildKey(PhotoSelectionActivity.class, "EXTRA_RESUME_LIST"); public static final String EXTRA_SELECTION_SPEC = BundleUtils.buildKey(PhotoSelectionActivity.class, "EXTRA_SELECTION_SPEC"); public static final String EXTRA_RESULT_SELECTION = BundleUtils.buildKey(PhotoSelectionActivity.class, "EXTRA_RESULT_SELECTION"); public static final String EXTRA_ERROR_SPEC = BundleUtils.buildKey(PhotoSelectionActivity.class, "EXTRA_ERROR_SPEC"); public static final String STATE_CAPTURE_PHOTO_URI = BundleUtils.buildKey(PhotoSelectionActivity.class, "STATE_CAPTURE_PHOTO_URI"); public static final int REQUEST_CODE_CAPTURE = 1; public static final int REQUEST_CODE_PREVIEW = 2; private final SelectedUriCollection mCollection = new SelectedUriCollection(this); private MediaStoreUtils mMediaStoreUtils; private PhotoSelectionActivityDrawerToggle mToggle; private DrawerLayout mDrawer; private String mCapturePhotoUriHolder; @Override protected void onCreate(Bundle savedInstanceState) { ViewResourceSpec spec = getIntent().getParcelableExtra(EXTRA_VIEW_SPEC); setTheme(spec.getTheme()); super.onCreate(savedInstanceState); setContentView(R.layout.l_activity_select_photo); PhotoSelectionViewHelper.setUpActivity(this); SelectionSpec selectionSpec = getIntent().getParcelableExtra(EXTRA_SELECTION_SPEC); PhotoSelectionViewHelper.setAlbumList(this, selectionSpec.getDefaultAlbumId()); PhotoSelectionViewHelper.setUpCounter(this); mMediaStoreUtils = new MediaStoreUtils(this, HandlerUtils.getMainHandler()); mCapturePhotoUriHolder = savedInstanceState != null ? savedInstanceState.getString(STATE_CAPTURE_PHOTO_URI) : ""; mCollection.onCreate(savedInstanceState); mCollection.prepareSelectionSpec(getIntent().getParcelableExtra(EXTRA_SELECTION_SPEC)); mCollection.setDefaultSelection(getIntent().getParcelableArrayListExtra(EXTRA_RESUME_LIST)); mDrawer = findViewById(R.id.l_container_drawer); Toolbar toolbar = findViewById(R.id.l_toolbar); mToggle = new PhotoSelectionActivityDrawerToggle(this, mDrawer, toolbar); setSupportActionBar(toolbar); mToggle.setUpActionBar(getSupportActionBar()); mDrawer.addDrawerListener(mToggle); } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); mToggle.syncState(); } @Override protected void onSaveInstanceState(Bundle outState) { mCollection.onSaveInstanceState(outState); outState.putString(STATE_CAPTURE_PHOTO_URI, mCapturePhotoUriHolder); super.onSaveInstanceState(outState); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); mToggle.onConfigurationChanged(newConfig); } @Override protected void onDestroy() { mMediaStoreUtils.destroy(); super.onDestroy(); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CODE_CAPTURE && resultCode == Activity.RESULT_OK) { Uri captured = mMediaStoreUtils.getCapturedPhotoUri(data, mCapturePhotoUriHolder); if (captured != null) { mCollection.add(captured); mMediaStoreUtils.cleanUp(mCapturePhotoUriHolder); } supportInvalidateOptionsMenu(); } else if (requestCode == REQUEST_CODE_PREVIEW && resultCode == Activity.RESULT_OK) { ArrayList checked = data.getParcelableArrayListExtra(ImagePreviewActivity.EXTRA_RESULT_CHECKED); mCollection.overwrite(checked); PhotoSelectionViewHelper.refreshGridView(this); supportInvalidateOptionsMenu(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.l_activity_options_select_photo, menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onPrepareOptionsMenu(Menu menu) { PhotoSelectionViewHelper.refreshOptionsMenuState(this, mCollection, menu); return super.onPrepareOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { PhotoSelectionOptionsMenu menu = PhotoSelectionOptionsMenu.valueOf(item); return mToggle.onOptionsItemSelected(item) || menu.getHandler().handle(this, null) || super.onOptionsItemSelected(item); } @Override public void onBackPressed() { if (mCollection.isEmpty()) { setResult(Activity.RESULT_CANCELED); super.onBackPressed(); return; } ErrorViewSpec spec = getIntent().getParcelableExtra(PhotoSelectionActivity.EXTRA_ERROR_SPEC); ErrorViewUtils.showConfirmDialog(this, spec.getBackConfirmSpec()); } @Override public void onSelect(Album album) { PhotoSelectionViewHelper.setPhotoGridFragment(this, mDrawer, album); } @Override public final void onClickSelectedView() { PhotoSelectionViewHelper.setSelectedGridFragment(this); } @Override public void onBindView(Context context, View view, Uri uri) { } public final SelectedUriCollection getCollection() { return mCollection; } public final MediaStoreUtils getMediaStoreUtils() { return mMediaStoreUtils; } public final void prepareCapture(String uri) { mCapturePhotoUriHolder = uri; } public final boolean isDrawerOpen() { return mDrawer != null && mDrawer.isDrawerOpen(GravityCompat.START); } @Override public void onPositive() { setResult(Activity.RESULT_CANCELED); finish(); } @Override public void onNegative() { // nothing to do. } } ================================================ FILE: laevatein/src/main/res/drawable/l_bg_drawer_item.xml ================================================ ================================================ FILE: laevatein/src/main/res/drawable/l_ic_capture.xml ================================================ ================================================ FILE: laevatein/src/main/res/drawable/l_ic_capture_normal.xml ================================================ ================================================ FILE: laevatein/src/main/res/drawable/l_ic_capture_pressed.xml ================================================ ================================================ FILE: laevatein/src/main/res/drawable/l_ic_folder.xml ================================================ ================================================ FILE: laevatein/src/main/res/drawable/l_ic_folder_close.xml ================================================ ================================================ FILE: laevatein/src/main/res/drawable/l_ic_folder_open.xml ================================================ ================================================ FILE: laevatein/src/main/res/drawable/l_list_selector_drawer_menu.xml ================================================ ================================================ FILE: laevatein/src/main/res/drawable-v21/l_bg_drawer_item.xml ================================================ ================================================ FILE: laevatein/src/main/res/layout/l_action_layout_checkbox.xml ================================================ ================================================ FILE: laevatein/src/main/res/layout/l_activity_preview.xml ================================================ ================================================ FILE: laevatein/src/main/res/layout/l_activity_select_photo.xml ================================================ ================================================ FILE: laevatein/src/main/res/layout/l_fragment_default_preview.xml ================================================ ================================================ FILE: laevatein/src/main/res/layout/l_fragment_grid_photo.xml ================================================ ================================================ FILE: laevatein/src/main/res/layout/l_fragment_list_album.xml ================================================ ================================================ FILE: laevatein/src/main/res/layout/l_fragment_selected_count.xml ================================================ ================================================ FILE: laevatein/src/main/res/layout/l_grid_item_default_photo.xml ================================================ ================================================ FILE: laevatein/src/main/res/layout/l_list_item_default_album.xml ================================================ ================================================ FILE: laevatein/src/main/res/layout/l_view_counter.xml ================================================ ================================================ FILE: laevatein/src/main/res/menu/l_activity_image_preview.xml ================================================

================================================ FILE: laevatein/src/main/res/menu/l_activity_options_select_photo.xml ================================================ ================================================ FILE: laevatein/src/main/res/values/attrs.xml ================================================ ================================================ FILE: laevatein/src/main/res/values/colors.xml ================================================ #1b000000 ================================================ FILE: laevatein/src/main/res/values/dimens.xml ================================================ 280dp 2dp ================================================ FILE: laevatein/src/main/res/values/strings.xml ================================================ Open menu Close menu Done Cancel Attention Are your sure you want to discard the selection? Selected items(%1$d / %2$d) Not allowed to select this photo due to invalid file format. Not allowed to select this photo due to not enough quality. Not allowed to select this photo due to not suitable size. All Photos Selected Photos Camera Download Screenshot No photo. Nothing selected. 0 Photo Detail ContentsTransition ================================================ FILE: laevatein/src/main/res/values/styles.xml ================================================ ================================================ FILE: laevatein/src/test/java/com/laevatein/LaevateinTest.java ================================================ package com.laevatein; import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Parcelable; import com.laevatein.ui.PhotoSelectionActivity; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import java.util.ArrayList; import java.util.List; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; /** * @author keishin.yokomaku * @since 2014/04/01 */ @RunWith(RobolectricTestRunner.class) public class LaevateinTest { private Activity mActivity; @Before public void setUp() throws Exception { mActivity = new Activity(); } @Test public void from_returnsLaevatein() throws Exception { Laevatein laevatein = Laevatein.from(mActivity); assertNotNull(laevatein.getActivity()); } @Test public void obtainResult_returnsSomeUri() throws Exception { List mock = new ArrayList() {{ add(Uri.parse("content://hogehoge/fugafuga/1")); }}; Intent data = new Intent(); data.putParcelableArrayListExtra(PhotoSelectionActivity.EXTRA_RESULT_SELECTION, (ArrayList) mock); List uris = Laevatein.obtainResult(data); assertNotNull(uris); assertSame(mock, uris); assertEquals(mock, uris); } } ================================================ FILE: laevatein/src/test/java/com/laevatein/MimeTypeTest.java ================================================ package com.laevatein; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import java.util.Set; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; /** * @author keishin.yokomaku * @since 2014/03/31 */ @RunWith(RobolectricTestRunner.class) public class MimeTypeTest { @Test public void oneOf() throws Exception { { Set set = MimeType.of(MimeType.JPEG); assertEquals(1, set.size()); assertTrue(set.contains(MimeType.JPEG)); assertFalse(set.contains(MimeType.PNG)); assertFalse(set.contains(MimeType.GIF)); } { Set set = MimeType.of(MimeType.PNG); assertEquals(1, set.size()); assertFalse(set.contains(MimeType.JPEG)); assertTrue(set.contains(MimeType.PNG)); assertFalse(set.contains(MimeType.GIF)); } { Set set = MimeType.of(MimeType.GIF); assertEquals(1, set.size()); assertFalse(set.contains(MimeType.JPEG)); assertFalse(set.contains(MimeType.PNG)); assertTrue(set.contains(MimeType.GIF)); } } @Test public void someOf() throws Exception { { Set set = MimeType.of(MimeType.JPEG, MimeType.PNG); assertEquals(2, set.size()); assertTrue(set.contains(MimeType.JPEG)); assertTrue(set.contains(MimeType.PNG)); assertFalse(set.contains(MimeType.GIF)); } { Set set = MimeType.of(MimeType.JPEG, MimeType.GIF); assertEquals(2, set.size()); assertTrue(set.contains(MimeType.JPEG)); assertFalse(set.contains(MimeType.PNG)); assertTrue(set.contains(MimeType.GIF)); } { Set set = MimeType.of(MimeType.PNG, MimeType.GIF); assertEquals(2, set.size()); assertFalse(set.contains(MimeType.JPEG)); assertTrue(set.contains(MimeType.PNG)); assertTrue(set.contains(MimeType.GIF)); } { Set set = MimeType.of(MimeType.JPEG, MimeType.PNG, MimeType.GIF); assertEquals(3, set.size()); assertTrue(set.contains(MimeType.JPEG)); assertTrue(set.contains(MimeType.PNG)); assertTrue(set.contains(MimeType.GIF)); } } @Test public void allOf() throws Exception { Set set = MimeType.allOf(); assertEquals(3, set.size()); assertTrue(set.contains(MimeType.JPEG)); assertTrue(set.contains(MimeType.PNG)); assertTrue(set.contains(MimeType.GIF)); } @Test public void stringify() throws Exception { assertEquals("image/jpeg", MimeType.JPEG.toString()); assertEquals("image/png", MimeType.PNG.toString()); assertEquals("image/gif", MimeType.GIF.toString()); } } ================================================ FILE: laevatein/src/test/java/com/laevatein/SelectionSpecBuilderTest.java ================================================ package com.laevatein; import android.app.Activity; import android.content.Intent; import android.net.Uri; import com.laevatein.ui.PhotoSelectionActivity; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import java.util.ArrayList; import java.util.concurrent.CountDownLatch; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; /** * @author keishin.yokomaku * @since 2014/03/31 */ @RunWith(RobolectricTestRunner.class) public class SelectionSpecBuilderTest { private static final int MOCK_REQUEST_CODE = 1; private SelectionSpecBuilder mBuilder; private CountDownLatch mLatch; @Before public void setUp() throws Exception { mLatch = new CountDownLatch(1); Laevatein laevatein = Laevatein.from(new Activity() { @Override public void startActivityForResult(Intent intent, int requestCode) { mLatch.countDown(); assertEquals(MOCK_REQUEST_CODE, requestCode); ensureAllExtrasExist(intent); assertEquals(PhotoSelectionActivity.class.getCanonicalName(), intent.getComponent().getClassName()); } @Override public String getPackageName() { // XXX return "com.laevatein"; } }); assertNotNull(laevatein); mBuilder = laevatein.choose(MimeType.of(MimeType.JPEG)); assertNotNull(mBuilder); } private void ensureAllExtrasExist(Intent intent) { assertNotNull(intent.getParcelableExtra(PhotoSelectionActivity.EXTRA_SELECTION_SPEC)); assertNotNull(intent.getParcelableArrayListExtra(PhotoSelectionActivity.EXTRA_RESUME_LIST)); assertNotNull(intent.getParcelableExtra(PhotoSelectionActivity.EXTRA_VIEW_SPEC)); assertNotNull(intent.getParcelableExtra(PhotoSelectionActivity.EXTRA_ERROR_SPEC)); } @Test public void count() throws Exception { mBuilder = mBuilder.count(0, 10); assertNotNull(mBuilder); mBuilder.forResult(MOCK_REQUEST_CODE); mLatch.await(); } @Test public void enableCapture() throws Exception { mBuilder = mBuilder.enableCapture("com.test.authorities"); assertNotNull(mBuilder); mBuilder.forResult(MOCK_REQUEST_CODE); mLatch.await(); } @Test public void disableCapture() throws Exception { mBuilder = mBuilder.disableCapture(); assertNotNull(mBuilder); mBuilder.forResult(MOCK_REQUEST_CODE); mLatch.await(); } @Test public void quality() throws Exception { mBuilder = mBuilder.quality(0, 100); assertNotNull(mBuilder); mBuilder.forResult(MOCK_REQUEST_CODE); mLatch.await(); } @Test public void size() throws Exception { mBuilder = mBuilder.size(0, 100); assertNotNull(mBuilder); mBuilder.forResult(MOCK_REQUEST_CODE); mLatch.await(); } @Test public void resume() throws Exception { mBuilder = mBuilder.resume(new ArrayList()); assertNotNull(mBuilder); mBuilder.forResult(MOCK_REQUEST_CODE); mLatch.await(); } // FIXME cannot resolve R while using unit testing support for now // public void testBindItemViewSpec() throws Exception { // mBuilder = mBuilder.bindEachImageWith(R.layout.l_grid_item_default_photo, R.id.l_default_grid_image, R.id.l_default_check_box); // assertNotNull(mBuilder); // // mBuilder.forResult(MOCK_REQUEST_CODE); // mLatch.await(); // } // // public void testBindAlbumViewSpec() throws Exception { // mBuilder = mBuilder.bindEachAlbumWith(R.layout.l_list_item_default_album, R.id.l_default_list_image, R.id.l_default_directory_label); // assertNotNull(mBuilder); // // mBuilder.forResult(MOCK_REQUEST_CODE); // mLatch.await(); // } } ================================================ FILE: laevatein/src/test/java/com/laevatein/internal/entity/AlbumTest.java ================================================ package com.laevatein.internal.entity; import android.database.MatrixCursor; import android.provider.MediaStore; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; /** * @author KeithYokoma * @since 2014/04/01 */ @RunWith(RobolectricTestRunner.class) public class AlbumTest { private static final String[] MOCK_PROJECTION = new String[]{MediaStore.Images.Media._ID, MediaStore.Images.Media.BUCKET_ID, MediaStore.Images.Media.BUCKET_DISPLAY_NAME}; @Test public void camera() throws Exception { MatrixCursor cameraRow = new MatrixCursor(MOCK_PROJECTION); cameraRow.addRow(new String[]{"1", "1", "Camera"}); assertTrue(cameraRow.moveToFirst()); Album camera = Album.valueOf(cameraRow); assertNotNull(camera); assertFalse(camera.isAll()); assertTrue(camera.isCamera()); assertEquals(1L, camera.getCoverId()); assertEquals("1", camera.getId()); assertEquals("Camera", camera.getDisplayName(RuntimeEnvironment.application)); } @Test public void all() throws Exception { MatrixCursor allRow = new MatrixCursor(MOCK_PROJECTION); allRow.addRow(new String[]{Album.ALBUM_ID_ALL, "-1", "All"}); assertTrue(allRow.moveToFirst()); Album all = Album.valueOf(allRow); assertNotNull(all); assertTrue(all.isAll()); assertFalse(all.isCamera()); assertEquals(-1L, all.getCoverId()); assertEquals(Album.ALBUM_ID_ALL, all.getId()); assertEquals("All Photos", all.getDisplayName(RuntimeEnvironment.application)); } @Test public void download() throws Exception { MatrixCursor downloadRow = new MatrixCursor(MOCK_PROJECTION); downloadRow.addRow(new String[]{"2", "2", "Download"}); assertTrue(downloadRow.moveToFirst()); Album download = Album.valueOf(downloadRow); assertNotNull(download); assertFalse(download.isAll()); assertFalse(download.isCamera()); assertEquals(2L, download.getCoverId()); assertEquals("2", download.getId()); assertEquals("Download", download.getDisplayName(RuntimeEnvironment.application)); } @Test public void screenshot() throws Exception { MatrixCursor screenShotRow = new MatrixCursor(MOCK_PROJECTION); screenShotRow.addRow(new String[]{"3", "3", "Screenshots"}); assertTrue(screenShotRow.moveToFirst()); Album screenShot = Album.valueOf(screenShotRow); assertNotNull(screenShot); assertFalse(screenShot.isAll()); assertFalse(screenShot.isCamera()); assertEquals(3L, screenShot.getCoverId()); assertEquals("3", screenShot.getId()); assertEquals("Screenshot", screenShot.getDisplayName(RuntimeEnvironment.application)); } } ================================================ FILE: laevatein/src/test/resources/com/laevatein/robolectric.properties ================================================ sdk=23 constants=com.laevatein.BuildConfig ================================================ FILE: repository/com/laevatein/Laevatein/0.10.0/Laevatein-0.10.0.aar.md5 ================================================ 4f643bfe2c7071a32d7ae068d810ed59 ================================================ FILE: repository/com/laevatein/Laevatein/0.10.0/Laevatein-0.10.0.aar.sha1 ================================================ 97376903ff26d05ecd478e82025878bc29a84497 ================================================ FILE: repository/com/laevatein/Laevatein/0.10.0/Laevatein-0.10.0.pom ================================================ 4.0.0 com.laevatein Laevatein 0.10.0 aar jp.yokomark.widget CompoundContainers 0.9.1 compile jp.mixi.compatibility AndroidDeviceCompatibility 0.1.0 compile com.amalgam Amalgam 0.3.2 compile com.google.code.findbugs jsr305 2.0.1 compile com.android.support support-v4 19.0.1 compile com.squareup.picasso picasso 2.1.1 compile it.sephiroth.android.library.imagezoom library + compile com.android.support appcompat-v7 19.0.1 compile ================================================ FILE: repository/com/laevatein/Laevatein/0.10.0/Laevatein-0.10.0.pom.md5 ================================================ 825ca71b37cdc9c0f87ab7523834162b ================================================ FILE: repository/com/laevatein/Laevatein/0.10.0/Laevatein-0.10.0.pom.sha1 ================================================ 8d14590b62ad3372970a7a2483320c966f0fc99b ================================================ FILE: repository/com/laevatein/Laevatein/0.10.1/Laevatein-0.10.1.aar.md5 ================================================ fa0e95703cbf65258eaee726c8d1e722 ================================================ FILE: repository/com/laevatein/Laevatein/0.10.1/Laevatein-0.10.1.aar.sha1 ================================================ 900f14a52904ed3d1c42ee086db60334333ffc22 ================================================ FILE: repository/com/laevatein/Laevatein/0.10.1/Laevatein-0.10.1.pom ================================================ 4.0.0 com.laevatein Laevatein 0.10.1 aar jp.yokomark.widget CompoundContainers 0.9.1 compile jp.mixi.compatibility AndroidDeviceCompatibility 0.1.0 compile com.amalgam Amalgam 0.3.2 compile com.google.code.findbugs jsr305 2.0.1 compile com.android.support support-v4 19.0.1 compile com.squareup.picasso picasso 2.1.1 compile it.sephiroth.android.library.imagezoom library + compile com.android.support appcompat-v7 19.0.1 compile ================================================ FILE: repository/com/laevatein/Laevatein/0.10.1/Laevatein-0.10.1.pom.md5 ================================================ 2ad8acb6b85f1ab1daa75bf7a64e04a8 ================================================ FILE: repository/com/laevatein/Laevatein/0.10.1/Laevatein-0.10.1.pom.sha1 ================================================ 3bb6b97d4d4a8587ff21b02ccb528d3f86d66b25 ================================================ FILE: repository/com/laevatein/Laevatein/0.10.2/Laevatein-0.10.2.aar.md5 ================================================ a504dbb34e1ac26fedc996d8e5ee534b ================================================ FILE: repository/com/laevatein/Laevatein/0.10.2/Laevatein-0.10.2.aar.sha1 ================================================ b6c1070c99ad004fc19ea477caa5794332ecebae ================================================ FILE: repository/com/laevatein/Laevatein/0.10.2/Laevatein-0.10.2.pom ================================================ 4.0.0 com.laevatein Laevatein 0.10.2 aar jp.yokomark.widget CompoundContainers 0.9.1 compile jp.mixi.compatibility AndroidDeviceCompatibility 0.1.0 compile com.amalgam Amalgam 0.3.2 compile com.google.code.findbugs jsr305 2.0.1 compile com.android.support support-v4 19.0.1 compile com.squareup.picasso picasso 2.1.1 compile it.sephiroth.android.library.imagezoom library + compile com.android.support appcompat-v7 19.0.1 compile ================================================ FILE: repository/com/laevatein/Laevatein/0.10.2/Laevatein-0.10.2.pom.md5 ================================================ bd3214d3ed7a172e90167ac59d8be2c9 ================================================ FILE: repository/com/laevatein/Laevatein/0.10.2/Laevatein-0.10.2.pom.sha1 ================================================ 6bb7ce6bf4c8c9b2204a966563c93c3be8ea3033 ================================================ FILE: repository/com/laevatein/Laevatein/0.10.3/Laevatein-0.10.3.aar.md5 ================================================ 3193eaf71ca35b58a2cefd2afcb7baaf ================================================ FILE: repository/com/laevatein/Laevatein/0.10.3/Laevatein-0.10.3.aar.sha1 ================================================ 9522fd2a86aaaa6854a94b6bd770fa4fde805b16 ================================================ FILE: repository/com/laevatein/Laevatein/0.10.3/Laevatein-0.10.3.pom ================================================ 4.0.0 com.laevatein Laevatein 0.10.3 aar jp.yokomark.widget CompoundContainers 0.9.1 compile jp.mixi.compatibility AndroidDeviceCompatibility 0.1.0 compile com.amalgam Amalgam 0.3.2 compile com.google.code.findbugs jsr305 2.0.1 compile com.android.support support-v4 19.0.1 compile com.squareup.picasso picasso 2.1.1 compile it.sephiroth.android.library.imagezoom library + compile com.android.support appcompat-v7 19.0.1 compile ================================================ FILE: repository/com/laevatein/Laevatein/0.10.3/Laevatein-0.10.3.pom.md5 ================================================ 79daa595964fba36734845b2bf435692 ================================================ FILE: repository/com/laevatein/Laevatein/0.10.3/Laevatein-0.10.3.pom.sha1 ================================================ 957c304d0b60782f533ac1e4add3e7ead1501388 ================================================ FILE: repository/com/laevatein/Laevatein/0.10.4/Laevatein-0.10.4.aar.md5 ================================================ 33b10d5b4011546d21e4e8461c471f09 ================================================ FILE: repository/com/laevatein/Laevatein/0.10.4/Laevatein-0.10.4.aar.sha1 ================================================ c48a129ede7172bda5a1d9e99b88d9aa69869748 ================================================ FILE: repository/com/laevatein/Laevatein/0.10.4/Laevatein-0.10.4.pom ================================================ 4.0.0 com.laevatein Laevatein 0.10.4 aar jp.yokomark.widget CompoundContainers 0.9.1 compile jp.mixi.compatibility AndroidDeviceCompatibility 0.1.0 compile com.amalgam Amalgam 0.3.2 compile com.google.code.findbugs jsr305 2.0.1 compile com.android.support support-v4 19.0.1 compile com.squareup.picasso picasso 2.1.1 compile it.sephiroth.android.library.imagezoom library + compile com.android.support appcompat-v7 19.0.1 compile ================================================ FILE: repository/com/laevatein/Laevatein/0.10.4/Laevatein-0.10.4.pom.md5 ================================================ 5cba2a54bfee064e23d4905f50bb2604 ================================================ FILE: repository/com/laevatein/Laevatein/0.10.4/Laevatein-0.10.4.pom.sha1 ================================================ 1017e41a27a8f626cd593605e791e4d5d3bbc792 ================================================ FILE: repository/com/laevatein/Laevatein/0.10.5/Laevatein-0.10.5.aar.md5 ================================================ 085f7a28f8207701d456247ab371f8a1 ================================================ FILE: repository/com/laevatein/Laevatein/0.10.5/Laevatein-0.10.5.aar.sha1 ================================================ db89d6e50b2d04617cda8b715b3b41dc4efcf615 ================================================ FILE: repository/com/laevatein/Laevatein/0.10.5/Laevatein-0.10.5.pom ================================================ 4.0.0 com.laevatein Laevatein 0.10.5 aar jp.yokomark.widget CompoundContainers 0.9.1 compile jp.mixi AndroidDeviceCompatibility 1.0.0 aar compile jp.co.nohana Amalgam 0.3.2 aar compile com.google.code.findbugs jsr305 2.0.1 compile com.android.support support-v4 19.0.1 compile com.squareup.picasso picasso 2.1.1 compile it.sephiroth.android.library.imagezoom library + compile com.android.support appcompat-v7 19.0.1 compile ================================================ FILE: repository/com/laevatein/Laevatein/0.10.5/Laevatein-0.10.5.pom.md5 ================================================ 5bd85aeb3f195ccc239b8c6da1a2335c ================================================ FILE: repository/com/laevatein/Laevatein/0.10.5/Laevatein-0.10.5.pom.sha1 ================================================ 5cb74fbc7b8d3846efed18978b893f206c5f5217 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.0/Laevatein-0.9.0.aar.md5 ================================================ 538eabb88d9ac6142a1623a52b5669f8 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.0/Laevatein-0.9.0.aar.sha1 ================================================ 1a191f64578a93da1f71b7780adde2967273b57e ================================================ FILE: repository/com/laevatein/Laevatein/0.9.0/Laevatein-0.9.0.pom ================================================ 4.0.0 com.laevatein Laevatein 0.9.0 aar com.amalgam Amalgam 0.3.2 compile com.google.code.findbugs jsr305 2.0.1 compile com.android.support support-v4 19.0.1 compile com.squareup.picasso picasso 2.1.1 compile it.sephiroth.android.library.imagezoom library + compile com.android.support appcompat-v7 19.0.1 compile ================================================ FILE: repository/com/laevatein/Laevatein/0.9.0/Laevatein-0.9.0.pom.md5 ================================================ 159f53aa37408e0e5b378203a5a88439 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.0/Laevatein-0.9.0.pom.sha1 ================================================ a92b9b4785cda454085be558885433a6f031ffcf ================================================ FILE: repository/com/laevatein/Laevatein/0.9.1/Laevatein-0.9.1.aar.md5 ================================================ b62f7c07ef552a4c8ebc8abc15d1ba51 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.1/Laevatein-0.9.1.aar.sha1 ================================================ 83927797e3ea2ee46e77478e2940767b7a17606e ================================================ FILE: repository/com/laevatein/Laevatein/0.9.1/Laevatein-0.9.1.pom ================================================ 4.0.0 com.laevatein Laevatein 0.9.1 aar com.amalgam Amalgam 0.3.2 compile com.google.code.findbugs jsr305 2.0.1 compile com.android.support support-v4 19.0.1 compile com.squareup.picasso picasso 2.1.1 compile it.sephiroth.android.library.imagezoom library + compile com.android.support appcompat-v7 19.0.1 compile jp.yokomark.widget CompoundContainers + compile ================================================ FILE: repository/com/laevatein/Laevatein/0.9.1/Laevatein-0.9.1.pom.md5 ================================================ 4a8da8a01b92eee9c258d953d19bb820 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.1/Laevatein-0.9.1.pom.sha1 ================================================ efd1a13f51038ceb16fe17b8265689e09b083bd1 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.10/Laevatein-0.9.10.aar.md5 ================================================ b2e7440b0b791958fc4085d521a79544 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.10/Laevatein-0.9.10.aar.sha1 ================================================ 0a01102070e107577518cbeeab084c394855772d ================================================ FILE: repository/com/laevatein/Laevatein/0.9.10/Laevatein-0.9.10.pom ================================================ 4.0.0 com.laevatein Laevatein 0.9.10 aar jp.yokomark.widget CompoundContainers 0.9.1 compile jp.mixi.compatibility AndroidDeviceCompatibility 0.1.0 compile com.amalgam Amalgam 0.3.2 compile com.google.code.findbugs jsr305 2.0.1 compile com.android.support support-v4 19.0.1 compile com.squareup.picasso picasso 2.1.1 compile it.sephiroth.android.library.imagezoom library + compile com.android.support appcompat-v7 19.0.1 compile ================================================ FILE: repository/com/laevatein/Laevatein/0.9.10/Laevatein-0.9.10.pom.md5 ================================================ a4a3806253a2340e5a72e41aa26c0d1e ================================================ FILE: repository/com/laevatein/Laevatein/0.9.10/Laevatein-0.9.10.pom.sha1 ================================================ bf3991cb0c12ddb94d7c7d89750769cba7084211 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.11/Laevatein-0.9.11.aar.md5 ================================================ 72f711fe29c9f3ecdcf34b9b81b12a6c ================================================ FILE: repository/com/laevatein/Laevatein/0.9.11/Laevatein-0.9.11.aar.sha1 ================================================ edfcba2479320e9b479a773dfe9ef09dc3bc52da ================================================ FILE: repository/com/laevatein/Laevatein/0.9.11/Laevatein-0.9.11.pom ================================================ 4.0.0 com.laevatein Laevatein 0.9.11 aar jp.yokomark.widget CompoundContainers 0.9.1 compile jp.mixi.compatibility AndroidDeviceCompatibility 0.1.0 compile com.amalgam Amalgam 0.3.2 compile com.google.code.findbugs jsr305 2.0.1 compile com.android.support support-v4 19.0.1 compile com.squareup.picasso picasso 2.1.1 compile it.sephiroth.android.library.imagezoom library + compile com.android.support appcompat-v7 19.0.1 compile ================================================ FILE: repository/com/laevatein/Laevatein/0.9.11/Laevatein-0.9.11.pom.md5 ================================================ 1c8905d8a0ae8bf8439d3681be2a9207 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.11/Laevatein-0.9.11.pom.sha1 ================================================ 0dc29c582e4a50413f4fcb3006e9f1aa1d59d2fb ================================================ FILE: repository/com/laevatein/Laevatein/0.9.12/Laevatein-0.9.12.aar.md5 ================================================ 3e02bea93f3b529f1ac6864af477cf84 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.12/Laevatein-0.9.12.aar.sha1 ================================================ 71ef691ba5dbbbbfe3f0c23c7e833f42bdb11675 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.12/Laevatein-0.9.12.pom ================================================ 4.0.0 com.laevatein Laevatein 0.9.12 aar jp.yokomark.widget CompoundContainers 0.9.1 compile jp.mixi.compatibility AndroidDeviceCompatibility 0.1.0 compile com.amalgam Amalgam 0.3.2 compile com.google.code.findbugs jsr305 2.0.1 compile com.android.support support-v4 19.0.1 compile com.squareup.picasso picasso 2.1.1 compile it.sephiroth.android.library.imagezoom library + compile com.android.support appcompat-v7 19.0.1 compile ================================================ FILE: repository/com/laevatein/Laevatein/0.9.12/Laevatein-0.9.12.pom.md5 ================================================ 79b54ebc6fd1c75698343c2bb71357eb ================================================ FILE: repository/com/laevatein/Laevatein/0.9.12/Laevatein-0.9.12.pom.sha1 ================================================ 58ec6c0a97c9f8d155fa113010d65fbf8035419b ================================================ FILE: repository/com/laevatein/Laevatein/0.9.13/Laevatein-0.9.13.aar.md5 ================================================ ff0f41dd31730c29a3e6b5f6e147b876 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.13/Laevatein-0.9.13.aar.sha1 ================================================ baf7e595d4fc1bd16f87c3230274e8bb691941bb ================================================ FILE: repository/com/laevatein/Laevatein/0.9.13/Laevatein-0.9.13.pom ================================================ 4.0.0 com.laevatein Laevatein 0.9.13 aar jp.yokomark.widget CompoundContainers 0.9.1 compile jp.mixi.compatibility AndroidDeviceCompatibility 0.1.0 compile com.amalgam Amalgam 0.3.2 compile com.google.code.findbugs jsr305 2.0.1 compile com.android.support support-v4 19.0.1 compile com.squareup.picasso picasso 2.1.1 compile it.sephiroth.android.library.imagezoom library + compile com.android.support appcompat-v7 19.0.1 compile ================================================ FILE: repository/com/laevatein/Laevatein/0.9.13/Laevatein-0.9.13.pom.md5 ================================================ 6c7a73f07e6cb4d11538c60493651bcd ================================================ FILE: repository/com/laevatein/Laevatein/0.9.13/Laevatein-0.9.13.pom.sha1 ================================================ 0290b91995d07c3a0a46862f4d8f2674de135d78 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.14/Laevatein-0.9.14.aar.md5 ================================================ 29029692874470ef1b394c56a4e33a2c ================================================ FILE: repository/com/laevatein/Laevatein/0.9.14/Laevatein-0.9.14.aar.sha1 ================================================ cdc2e1ebb6dcc81a6bb6dafbc6b73432f0c69970 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.14/Laevatein-0.9.14.pom ================================================ 4.0.0 com.laevatein Laevatein 0.9.14 aar jp.yokomark.widget CompoundContainers 0.9.1 compile jp.mixi.compatibility AndroidDeviceCompatibility 0.1.0 compile com.amalgam Amalgam 0.3.2 compile com.google.code.findbugs jsr305 2.0.1 compile com.android.support support-v4 19.0.1 compile com.squareup.picasso picasso 2.1.1 compile it.sephiroth.android.library.imagezoom library + compile com.android.support appcompat-v7 19.0.1 compile ================================================ FILE: repository/com/laevatein/Laevatein/0.9.14/Laevatein-0.9.14.pom.md5 ================================================ 840c34c3a6b960423e81e077980ad007 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.14/Laevatein-0.9.14.pom.sha1 ================================================ 9a9ccfd0f3b17379e98dfefc2128bd507ffbada4 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.15/Laevatein-0.9.15.aar.md5 ================================================ 0761abe61356770b23494425a2804254 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.15/Laevatein-0.9.15.aar.sha1 ================================================ f8f1b9a4d54332ac3abbcad9a67b72af039af667 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.15/Laevatein-0.9.15.pom ================================================ 4.0.0 com.laevatein Laevatein 0.9.15 aar jp.yokomark.widget CompoundContainers 0.9.1 compile jp.mixi.compatibility AndroidDeviceCompatibility 0.1.0 compile com.amalgam Amalgam 0.3.2 compile com.google.code.findbugs jsr305 2.0.1 compile com.android.support support-v4 19.0.1 compile com.squareup.picasso picasso 2.1.1 compile it.sephiroth.android.library.imagezoom library + compile com.android.support appcompat-v7 19.0.1 compile ================================================ FILE: repository/com/laevatein/Laevatein/0.9.15/Laevatein-0.9.15.pom.md5 ================================================ c56781ec0e92b76282527946b64be211 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.15/Laevatein-0.9.15.pom.sha1 ================================================ 8a9a3720d7134ee15e560ea0135a47c0ae952456 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.16/Laevatein-0.9.16.aar.md5 ================================================ 1dd4a8206305c93d4f9e9913bd871c9d ================================================ FILE: repository/com/laevatein/Laevatein/0.9.16/Laevatein-0.9.16.aar.sha1 ================================================ b462f822d34e4582f4bcec46ce38b5495f2de5a9 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.16/Laevatein-0.9.16.pom ================================================ 4.0.0 com.laevatein Laevatein 0.9.16 aar jp.yokomark.widget CompoundContainers 0.9.1 compile jp.mixi.compatibility AndroidDeviceCompatibility 0.1.0 compile com.amalgam Amalgam 0.3.2 compile com.google.code.findbugs jsr305 2.0.1 compile com.android.support support-v4 19.0.1 compile com.squareup.picasso picasso 2.1.1 compile it.sephiroth.android.library.imagezoom library + compile com.android.support appcompat-v7 19.0.1 compile ================================================ FILE: repository/com/laevatein/Laevatein/0.9.16/Laevatein-0.9.16.pom.md5 ================================================ ae762d26171e6f412ec51dc4127fb77a ================================================ FILE: repository/com/laevatein/Laevatein/0.9.16/Laevatein-0.9.16.pom.sha1 ================================================ ffa26337f0c1955da82e26b42acbef7cf72bfc49 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.2/Laevatein-0.9.2.aar.md5 ================================================ 447b70869a65c5ac7be898fdbba56abb ================================================ FILE: repository/com/laevatein/Laevatein/0.9.2/Laevatein-0.9.2.aar.sha1 ================================================ 87082a04c0bf64321fef111087cde5aba77395bb ================================================ FILE: repository/com/laevatein/Laevatein/0.9.2/Laevatein-0.9.2.pom ================================================ 4.0.0 com.laevatein Laevatein 0.9.2 aar jp.yokomark.widget CompoundContainers 0.9.1 compile com.amalgam Amalgam 0.3.2 compile com.google.code.findbugs jsr305 2.0.1 compile com.android.support support-v4 19.0.1 compile com.squareup.picasso picasso 2.1.1 compile it.sephiroth.android.library.imagezoom library + compile com.android.support appcompat-v7 19.0.1 compile ================================================ FILE: repository/com/laevatein/Laevatein/0.9.2/Laevatein-0.9.2.pom.md5 ================================================ a5f355e50c0db5cada41d488c0d6eb42 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.2/Laevatein-0.9.2.pom.sha1 ================================================ 1df0cf8d0eb9636c52ae345fee0ccd7c86652725 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.3/Laevatein-0.9.3.aar.md5 ================================================ 65cd2931c5ed653cc10fe96d416ecf0b ================================================ FILE: repository/com/laevatein/Laevatein/0.9.3/Laevatein-0.9.3.aar.sha1 ================================================ 8d877fdc66ffcb7d4bc19418302842b79d4a9db9 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.3/Laevatein-0.9.3.pom ================================================ 4.0.0 com.laevatein Laevatein 0.9.3 aar jp.yokomark.widget CompoundContainers 0.9.1 compile com.amalgam Amalgam 0.3.2 compile com.google.code.findbugs jsr305 2.0.1 compile com.android.support support-v4 19.0.1 compile com.squareup.picasso picasso 2.1.1 compile it.sephiroth.android.library.imagezoom library + compile com.android.support appcompat-v7 19.0.1 compile ================================================ FILE: repository/com/laevatein/Laevatein/0.9.3/Laevatein-0.9.3.pom.md5 ================================================ dc40e6a91d027e524593b08c4c5ae81d ================================================ FILE: repository/com/laevatein/Laevatein/0.9.3/Laevatein-0.9.3.pom.sha1 ================================================ 8f1de44ca20e3489571355eb66f0521f3f501e87 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.4/Laevatein-0.9.4.aar.md5 ================================================ 856a9126222b9a197cb2ddf31fc50bdf ================================================ FILE: repository/com/laevatein/Laevatein/0.9.4/Laevatein-0.9.4.aar.sha1 ================================================ 66a5972ba6261b3f56397ceb5aefe08cc77ccad5 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.4/Laevatein-0.9.4.pom ================================================ 4.0.0 com.laevatein Laevatein 0.9.4 aar jp.yokomark.widget CompoundContainers 0.9.1 compile com.amalgam Amalgam 0.3.2 compile com.google.code.findbugs jsr305 2.0.1 compile com.android.support support-v4 19.0.1 compile com.squareup.picasso picasso 2.1.1 compile it.sephiroth.android.library.imagezoom library + compile com.android.support appcompat-v7 19.0.1 compile ================================================ FILE: repository/com/laevatein/Laevatein/0.9.4/Laevatein-0.9.4.pom.md5 ================================================ ef01830482cd3ec14d1236b5d3cc55a5 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.4/Laevatein-0.9.4.pom.sha1 ================================================ 61b6fda575726d8c36c1c68134447f6538f6671c ================================================ FILE: repository/com/laevatein/Laevatein/0.9.5/Laevatein-0.9.5.aar.md5 ================================================ 9e41941bda0831f4c101bdd7c6f08659 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.5/Laevatein-0.9.5.aar.sha1 ================================================ 9f682e76392ca69e64fd32ae3e89618c361e02f6 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.5/Laevatein-0.9.5.pom ================================================ 4.0.0 com.laevatein Laevatein 0.9.5 aar jp.yokomark.widget CompoundContainers 0.9.1 compile jp.mixi.compatibility AndroidDeviceCompatibility 0.1.0 compile com.amalgam Amalgam 0.3.2 compile com.google.code.findbugs jsr305 2.0.1 compile com.android.support support-v4 19.0.1 compile com.squareup.picasso picasso 2.1.1 compile it.sephiroth.android.library.imagezoom library + compile com.android.support appcompat-v7 19.0.1 compile ================================================ FILE: repository/com/laevatein/Laevatein/0.9.5/Laevatein-0.9.5.pom.md5 ================================================ ad874dfbb5d2d08b61f03c370d13a704 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.5/Laevatein-0.9.5.pom.sha1 ================================================ da53069e31988d40888fa0d86dd65eb088cf5e7e ================================================ FILE: repository/com/laevatein/Laevatein/0.9.6/Laevatein-0.9.6.aar.md5 ================================================ d09f4669b90dff71405439c6812cd8b8 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.6/Laevatein-0.9.6.aar.sha1 ================================================ c5bf9f1f520f891758057a35380cc23d3bf025d2 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.6/Laevatein-0.9.6.pom ================================================ 4.0.0 com.laevatein Laevatein 0.9.6 aar jp.yokomark.widget CompoundContainers 0.9.1 compile jp.mixi.compatibility AndroidDeviceCompatibility 0.1.0 compile com.amalgam Amalgam 0.3.2 compile com.google.code.findbugs jsr305 2.0.1 compile com.android.support support-v4 19.0.1 compile com.squareup.picasso picasso 2.1.1 compile it.sephiroth.android.library.imagezoom library + compile com.android.support appcompat-v7 19.0.1 compile ================================================ FILE: repository/com/laevatein/Laevatein/0.9.6/Laevatein-0.9.6.pom.md5 ================================================ 9880bb8c9410a9c5b354bec9b49c8c9a ================================================ FILE: repository/com/laevatein/Laevatein/0.9.6/Laevatein-0.9.6.pom.sha1 ================================================ ed217e31851baa66966944a33259aa717e860b1e ================================================ FILE: repository/com/laevatein/Laevatein/0.9.7/Laevatein-0.9.7.aar.md5 ================================================ e8d2ce95e92c49e4deef3353b27934ad ================================================ FILE: repository/com/laevatein/Laevatein/0.9.7/Laevatein-0.9.7.aar.sha1 ================================================ df9906685c2013d237f0d05ec0133dd12a54dc86 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.7/Laevatein-0.9.7.pom ================================================ 4.0.0 com.laevatein Laevatein 0.9.7 aar jp.yokomark.widget CompoundContainers 0.9.1 compile jp.mixi.compatibility AndroidDeviceCompatibility 0.1.0 compile com.amalgam Amalgam 0.3.2 compile com.google.code.findbugs jsr305 2.0.1 compile com.android.support support-v4 19.0.1 compile com.squareup.picasso picasso 2.1.1 compile it.sephiroth.android.library.imagezoom library + compile com.android.support appcompat-v7 19.0.1 compile ================================================ FILE: repository/com/laevatein/Laevatein/0.9.7/Laevatein-0.9.7.pom.md5 ================================================ 78bb7dca6e333ae6b7b0765706e2fac8 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.7/Laevatein-0.9.7.pom.sha1 ================================================ b097eb76fb597e8dc0a16e963394365f689f6dd0 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.8/Laevatein-0.9.8.aar.md5 ================================================ 5cd25cdd165bbac02dd8d676ac347c19 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.8/Laevatein-0.9.8.aar.sha1 ================================================ 2ca161e117905cd0f14619544b738a9a4cec74df ================================================ FILE: repository/com/laevatein/Laevatein/0.9.8/Laevatein-0.9.8.pom ================================================ 4.0.0 com.laevatein Laevatein 0.9.8 aar jp.yokomark.widget CompoundContainers 0.9.1 compile jp.mixi.compatibility AndroidDeviceCompatibility 0.1.0 compile com.amalgam Amalgam 0.3.2 compile com.google.code.findbugs jsr305 2.0.1 compile com.android.support support-v4 19.0.1 compile com.squareup.picasso picasso 2.1.1 compile it.sephiroth.android.library.imagezoom library + compile com.android.support appcompat-v7 19.0.1 compile ================================================ FILE: repository/com/laevatein/Laevatein/0.9.8/Laevatein-0.9.8.pom.md5 ================================================ 5cefc53f8c036db6f90286293aaab270 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.8/Laevatein-0.9.8.pom.sha1 ================================================ b06ef6f20f6158f45f931fc9c54478ebec980a6f ================================================ FILE: repository/com/laevatein/Laevatein/0.9.9/Laevatein-0.9.9.aar.md5 ================================================ da503c314b4f56659e98778c5b50a503 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.9/Laevatein-0.9.9.aar.sha1 ================================================ 5530d519be99f6e5bbb070c652316a79286c8284 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.9/Laevatein-0.9.9.pom ================================================ 4.0.0 com.laevatein Laevatein 0.9.9 aar jp.yokomark.widget CompoundContainers 0.9.1 compile jp.mixi.compatibility AndroidDeviceCompatibility 0.1.0 compile com.amalgam Amalgam 0.3.2 compile com.google.code.findbugs jsr305 2.0.1 compile com.android.support support-v4 19.0.1 compile com.squareup.picasso picasso 2.1.1 compile it.sephiroth.android.library.imagezoom library + compile com.android.support appcompat-v7 19.0.1 compile ================================================ FILE: repository/com/laevatein/Laevatein/0.9.9/Laevatein-0.9.9.pom.md5 ================================================ 0c01d63f24dcaafdc8db9bf355125164 ================================================ FILE: repository/com/laevatein/Laevatein/0.9.9/Laevatein-0.9.9.pom.sha1 ================================================ 5e4b678718000477660c515dd8b5c42e6260832b ================================================ FILE: repository/com/laevatein/Laevatein/maven-metadata.xml ================================================ jp.co.nohana Laevatein 3.1.0 3.1.0 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.1.0 2.1.1 2.1.2 2.2.0 2.2.1 2.2.2 2.3.0 2.3.1 2.3.2 2.4.0 2.4.1 2.5.0 3.0.0 3.0.1 3.1.0 20200617151000 ================================================ FILE: repository/com/laevatein/Laevatein/maven-metadata.xml.md5 ================================================ 991f2405e62517f4deff7b45b88fc060 ================================================ FILE: repository/com/laevatein/Laevatein/maven-metadata.xml.sha1 ================================================ 10631471e0dc7e792646df181ca7fcdf5cab3e9f ================================================ FILE: sample/.gitignore ================================================ /build ================================================ FILE: sample/build.gradle ================================================ apply plugin: 'com.android.application' android { compileSdkVersion 29 defaultConfig { minSdkVersion 14 targetSdkVersion 29 versionCode 1 versionName "1.0" } compileOptions { sourceCompatibility JavaVersion.VERSION_1_7 targetCompatibility JavaVersion.VERSION_1_7 } lintOptions { abortOnError false } } dependencies { implementation 'androidx.appcompat:appcompat:1.0.2' implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'com.google.android.material:material:1.0.0' implementation project(':laevatein') implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'org.permissionsdispatcher:permissionsdispatcher:4.3.0' annotationProcessor 'org.permissionsdispatcher:permissionsdispatcher-processor:4.3.0' } ================================================ FILE: sample/proguard-rules.txt ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in /Library/android-sdk-macosx/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the ProGuard # include property in project.properties. # # 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.okhttp.** ================================================ FILE: sample/src/main/AndroidManifest.xml ================================================ ================================================ FILE: sample/src/main/java/com/laevatein/sample/LSampleActivity.java ================================================ package com.laevatein.sample; import android.Manifest; import android.content.Intent; import android.content.pm.ActivityInfo; import android.net.Uri; import android.os.Bundle; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.Toast; import com.laevatein.Laevatein; import com.laevatein.MimeType; import com.laevatein.internal.entity.ErrorViewResources; import java.util.List; import permissions.dispatcher.NeedsPermission; import permissions.dispatcher.OnNeverAskAgain; import permissions.dispatcher.OnPermissionDenied; import permissions.dispatcher.RuntimePermissions; /** * @author KeithYokoma * @since 2014/03/20 */ @RuntimePermissions public class LSampleActivity extends AppCompatActivity { public static final String TAG = LSampleActivity.class.getSimpleName(); public static final int REQUEST_CODE_CHOOSE = 1; private List mSelected; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_sample); Toolbar toolbar = findViewById(R.id.l_toolbar); setSupportActionBar(toolbar); Button button = findViewById(R.id.choose); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { LSampleActivityPermissionsDispatcher.startPhotoSelectWithPermissionCheck(LSampleActivity.this); } }); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CODE_CHOOSE && resultCode == RESULT_OK) { mSelected = Laevatein.obtainResult(data); Log.v(TAG, "selected: " + mSelected); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); LSampleActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults); } @NeedsPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) void startPhotoSelect() { String authorities = getString(R.string.file_provider_authorities); Laevatein.from(this) .choose(MimeType.of(MimeType.JPEG)) .count(10, 10) .quality(300000, Integer.MAX_VALUE) .size(300, 300) .resume(mSelected) .enableCapture(authorities) .countUnder(ErrorViewResources.ViewType.SNACKBAR, R.string.error_count_under) .countOver(ErrorViewResources.ViewType.DIALOG, R.string.error_count_over) .enableSelectedView(true) .restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) .forResult(REQUEST_CODE_CHOOSE); } @OnPermissionDenied(Manifest.permission.WRITE_EXTERNAL_STORAGE) void showDeniedForCamera() { Toast.makeText(this, R.string.permission_denied, Toast.LENGTH_SHORT).show(); } @OnNeverAskAgain(Manifest.permission.WRITE_EXTERNAL_STORAGE) void showNeverAskForCamera() { Button button = findViewById(R.id.choose); button.setText(R.string.permission_nerver_ask); } } ================================================ FILE: sample/src/main/res/drawable/selector_background.xml ================================================ ================================================ FILE: sample/src/main/res/layout/activity_sample.xml ================================================