├── .gitignore ├── CHANGELOG.md ├── FAQ.md ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ ├── aidl │ │ └── com │ │ │ └── android │ │ │ └── vending │ │ │ └── billing │ │ │ └── IInAppBillingService.aidl │ ├── assets │ │ └── latest_changelog.md │ ├── java │ │ └── org │ │ │ └── horaapps │ │ │ └── leafpic │ │ │ ├── App.java │ │ │ ├── CardViewStyle.java │ │ │ ├── CustomGlideModule.java │ │ │ ├── LookForMediaJob.java │ │ │ ├── SecretConstants.java │ │ │ ├── SelectAlbumBuilder.java │ │ │ ├── about │ │ │ ├── AboutActivity.java │ │ │ ├── AboutCreator.java │ │ │ ├── AboutLink.java │ │ │ ├── Contact.java │ │ │ ├── ContactButton.java │ │ │ ├── ContactListener.java │ │ │ ├── Contributor.java │ │ │ ├── ContributorViewHolder.java │ │ │ └── ContributorsAdapter.java │ │ │ ├── activities │ │ │ ├── AffixActivity.java │ │ │ ├── BlackWhiteListActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── PaletteActivity.java │ │ │ ├── PlayerActivity.java │ │ │ ├── SecurityActivity.java │ │ │ ├── SettingsActivity.java │ │ │ ├── SingleMediaActivity.java │ │ │ ├── SplashScreen.java │ │ │ └── base │ │ │ │ ├── BaseActivity.java │ │ │ │ └── SharedMediaActivity.java │ │ │ ├── adapters │ │ │ ├── AlbumsAdapter.java │ │ │ ├── BaseAdapter.java │ │ │ ├── MediaAdapter.java │ │ │ └── MediaPagerAdapter.java │ │ │ ├── animations │ │ │ └── DepthPageTransformer.java │ │ │ ├── data │ │ │ ├── Album.java │ │ │ ├── AlbumSettings.java │ │ │ ├── AlbumsHelper.java │ │ │ ├── CursorHandler.java │ │ │ ├── HandlingAlbums.java │ │ │ ├── IAlbum.java │ │ │ ├── Media.java │ │ │ ├── MediaHelper.java │ │ │ ├── StorageHelper.java │ │ │ ├── filter │ │ │ │ ├── FilterMode.java │ │ │ │ ├── FoldersFileFilter.java │ │ │ │ ├── IMediaFilter.java │ │ │ │ ├── ImageFileFilter.java │ │ │ │ ├── MediaFilter.java │ │ │ │ └── NotHiddenFoldersFilter.java │ │ │ ├── metadata │ │ │ │ ├── MediaDetailsMap.java │ │ │ │ ├── MetaDataItem.java │ │ │ │ └── MetadataHelper.java │ │ │ ├── provider │ │ │ │ ├── CPHelper.java │ │ │ │ ├── ContentProviderHelper.java │ │ │ │ ├── Query.java │ │ │ │ └── QueryUtils.java │ │ │ └── sort │ │ │ │ ├── AlbumsComparators.java │ │ │ │ ├── MediaComparators.java │ │ │ │ ├── SortingMode.java │ │ │ │ └── SortingOrder.java │ │ │ ├── fragments │ │ │ ├── AlbumsFragment.java │ │ │ ├── BaseFragment.java │ │ │ ├── BaseMediaFragment.java │ │ │ ├── BaseMediaGridFragment.kt │ │ │ ├── EditModeListener.java │ │ │ ├── GifFragment.java │ │ │ ├── IFragment.java │ │ │ ├── ImageFragment.java │ │ │ ├── NothingToShowListener.java │ │ │ ├── RvMediaFragment.java │ │ │ └── VideoFragment.java │ │ │ ├── interfaces │ │ │ └── MediaClickListener.java │ │ │ ├── items │ │ │ └── ActionsListener.java │ │ │ ├── progress │ │ │ ├── ErrorCause.java │ │ │ ├── ErrorCauseAdapter.java │ │ │ ├── ErrorCauseViewHolder.java │ │ │ ├── ProgressBottomSheet.java │ │ │ └── ProgressException.java │ │ │ ├── settings │ │ │ ├── CardViewStyleSetting.java │ │ │ ├── ColorsSetting.java │ │ │ ├── GeneralSetting.java │ │ │ ├── MapProviderSetting.java │ │ │ ├── SinglePhotoSetting.java │ │ │ └── ThemedSetting.java │ │ │ ├── timeline │ │ │ ├── GroupingMode.java │ │ │ ├── TimelineAdapter.java │ │ │ ├── TimelineFragment.kt │ │ │ ├── ViewHolder.java │ │ │ └── data │ │ │ │ ├── TimelineHeaderModel.java │ │ │ │ └── TimelineItem.java │ │ │ ├── util │ │ │ ├── Affix.java │ │ │ ├── AlertDialogsHelper.java │ │ │ ├── AnimationUtils.java │ │ │ ├── ApplicationUtils.java │ │ │ ├── ArrayUtils.java │ │ │ ├── BitmapUtils.java │ │ │ ├── ChromeCustomTabs.java │ │ │ ├── DeviceUtils.java │ │ │ ├── FingerPrint.java │ │ │ ├── FingerprintHandler.java │ │ │ ├── GLideRotateTransformation.java │ │ │ ├── LegacyCompatFileProvider.java │ │ │ ├── Measure.java │ │ │ ├── MediaUtils.kt │ │ │ ├── MimeTypeUtils.java │ │ │ ├── NumericComparator.java │ │ │ ├── PermissionUtils.java │ │ │ ├── RecyclerItemClickListener.java │ │ │ ├── Security.java │ │ │ ├── ServerConstants.java │ │ │ ├── StaticMapProvider.java │ │ │ ├── StringUtils.java │ │ │ ├── inapppurchase │ │ │ │ ├── IabBroadcastReceiver.java │ │ │ │ ├── IabException.java │ │ │ │ ├── IabHelper.java │ │ │ │ ├── IabResult.java │ │ │ │ ├── Inventory.java │ │ │ │ ├── Purchase.java │ │ │ │ ├── Security.java │ │ │ │ └── SkuDetails.java │ │ │ └── preferences │ │ │ │ ├── Defaults.java │ │ │ │ ├── Keys.java │ │ │ │ ├── Prefs.java │ │ │ │ └── SharedPrefs.java │ │ │ └── views │ │ │ ├── FabScrollBehaviour.java │ │ │ ├── GridSpacingItemDecoration.java │ │ │ ├── HackyViewPager.java │ │ │ ├── PinchImageView.java │ │ │ ├── SettingBasic.java │ │ │ ├── SettingWithSwitchView.java │ │ │ ├── SquareImageView.java │ │ │ ├── SquareRelativeLayout.java │ │ │ ├── ZoomImageView.java │ │ │ ├── navigation_drawer │ │ │ ├── NavigationDrawer.java │ │ │ └── NavigationEntry.java │ │ │ ├── themeable │ │ │ ├── ThemedCardView.java │ │ │ ├── ThemedSettingsCaption.java │ │ │ ├── ThemedSettingsCategory.java │ │ │ ├── ThemedSettingsIcon.java │ │ │ └── ThemedSettingsTitle.java │ │ │ └── videoplayer │ │ │ ├── CustomExoPlayerView.java │ │ │ ├── CustomPlayBackController.java │ │ │ └── TrackSelectionHelper.java │ └── res │ │ ├── anim │ │ ├── fade_in.xml │ │ ├── fade_out.xml │ │ ├── grid_layout_animation.xml │ │ ├── slide_bottom_in.xml │ │ ├── slide_down.xml │ │ ├── slide_fade_card.xml │ │ ├── slide_fade_photos.xml │ │ └── zoom_in.xml │ │ ├── drawable-v19 │ │ └── ripple.xml │ │ ├── drawable-v21 │ │ └── ripple.xml │ │ ├── drawable │ │ ├── calvin_header.webp │ │ ├── calvin_profile.webp │ │ ├── donald_header.webp │ │ ├── donald_profile.webp │ │ ├── gilbert_header.webp │ │ ├── gilbert_profile.webp │ │ ├── ic_close.xml │ │ ├── ic_delete.xml │ │ ├── ic_empty.webp │ │ ├── ic_empty_amoled.webp │ │ ├── ic_empty_white.webp │ │ ├── ic_error.webp │ │ ├── ic_filter.xml │ │ ├── ic_gif_white_18dp.png │ │ ├── ic_group.xml │ │ ├── ic_holder.png │ │ ├── ic_scrollbar.webp │ │ ├── ic_select_all.xml │ │ ├── ic_share.xml │ │ └── leaf_pic.webp │ │ ├── layout │ │ ├── activity_about.xml │ │ ├── activity_affix.xml │ │ ├── activity_black_white_list.xml │ │ ├── activity_donate.xml │ │ ├── activity_main.xml │ │ ├── activity_palette.xml │ │ ├── activity_player.xml │ │ ├── activity_security.xml │ │ ├── activity_settings.xml │ │ ├── activity_single_media.xml │ │ ├── activity_splash.xml │ │ ├── bottom_sheet_progress.xml │ │ ├── card_album_compact.xml │ │ ├── card_album_flat.xml │ │ ├── card_album_material.xml │ │ ├── card_photo.xml │ │ ├── card_track_folder.xml │ │ ├── delete_folder_dialog_item.xml │ │ ├── dialog_affix.xml │ │ ├── dialog_album_card_style.xml │ │ ├── dialog_base_theme.xml │ │ ├── dialog_changelog.xml │ │ ├── dialog_color_picker.xml │ │ ├── dialog_delete_album_progress.xml │ │ ├── dialog_exclude.xml │ │ ├── dialog_insert_text.xml │ │ ├── dialog_list_progress.xml │ │ ├── dialog_map_provider.xml │ │ ├── dialog_media_detail.xml │ │ ├── dialog_media_viewer_theme.xml │ │ ├── dialog_multi_column.xml │ │ ├── dialog_password.xml │ │ ├── dialog_progress.xml │ │ ├── dialog_set_password.xml │ │ ├── dialog_text.xml │ │ ├── exo_media_control.xml │ │ ├── exo_player.xml │ │ ├── fragment_albums.xml │ │ ├── fragment_photo.xml │ │ ├── fragment_rv_media.xml │ │ ├── fragment_timeline.xml │ │ ├── fragment_video.xml │ │ ├── item_error_cause.xml │ │ ├── list_divider.xml │ │ ├── palette_item.xml │ │ ├── select_folder_bottom_sheet.xml │ │ ├── select_folder_bottom_sheet_item.xml │ │ ├── spinner_item_dark.xml │ │ ├── spinner_item_light.xml │ │ ├── spinner_item_with_pic.xml │ │ ├── there_is_nothing_to_show.xml │ │ ├── toolbar.xml │ │ ├── track_selection_dialog.xml │ │ ├── view_about_contributor.xml │ │ ├── view_about_creator.xml │ │ ├── view_about_link.xml │ │ ├── view_navigation_drawer.xml │ │ ├── view_navigation_entry.xml │ │ ├── view_setting_basic.xml │ │ ├── view_setting_switch.xml │ │ └── view_timeline_header.xml │ │ ├── menu │ │ ├── grid_albums.xml │ │ ├── grid_media.xml │ │ ├── menu_albums.xml │ │ ├── menu_black_white_list.xml │ │ ├── menu_timeline.xml │ │ ├── menu_video_player.xml │ │ ├── menu_view_page_slide_on.xml │ │ └── menu_view_pager.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-af-rZA │ │ └── strings.xml │ │ ├── values-ar-rSA │ │ └── strings.xml │ │ ├── values-ca-rES │ │ └── strings.xml │ │ ├── values-cs-rCZ │ │ └── strings.xml │ │ ├── values-de-rDE │ │ └── strings.xml │ │ ├── values-el-rGR │ │ └── strings.xml │ │ ├── values-es-rES │ │ └── strings.xml │ │ ├── values-fi-rFI │ │ └── strings.xml │ │ ├── values-fr-rFR │ │ └── strings.xml │ │ ├── values-gl-rES │ │ └── strings.xml │ │ ├── values-it-rIT │ │ └── strings.xml │ │ ├── values-ja-rJP │ │ └── strings.xml │ │ ├── values-lt-rLT │ │ └── strings.xml │ │ ├── values-nl-rNL │ │ └── strings.xml │ │ ├── values-no-rNO │ │ └── strings.xml │ │ ├── values-pl-rPL │ │ └── strings.xml │ │ ├── values-pt-rBR │ │ └── strings.xml │ │ ├── values-ro-rRO │ │ └── strings.xml │ │ ├── values-ru-rRU │ │ └── strings.xml │ │ ├── values-sk-rSK │ │ └── strings.xml │ │ ├── values-sr-rSP │ │ └── strings.xml │ │ ├── values-sv-rSE │ │ └── strings.xml │ │ ├── values-th-rTH │ │ └── strings.xml │ │ ├── values-tr-rTR │ │ └── strings.xml │ │ ├── values-uk-rUA │ │ └── strings.xml │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values-vi-rVN │ │ └── strings.xml │ │ ├── values-zh-rCN │ │ └── strings.xml │ │ ├── values-zh-rTW │ │ └── strings.xml │ │ ├── values │ │ ├── array.xml │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimen.xml │ │ ├── dimens.xml │ │ ├── icons.xml │ │ ├── preferences-key.xml │ │ ├── shity_strings.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ └── provider_paths.xml │ ├── noGPlay │ └── java │ │ └── org │ │ └── horaapps │ │ └── leafpic │ │ └── activities │ │ └── DonateActivity.java │ └── withGPlay │ └── java │ └── org │ └── horaapps │ └── leafpic │ └── activities │ └── DonateActivity.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── screenshots ├── 1.png ├── 2.png ├── 3.png ├── 4.png └── 5.png ├── scripts └── crowdin.sh └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | # User-specific configurations 3 | /.directory 4 | 5 | .idea/ 6 | *.iml 7 | /local.properties 8 | 9 | # release 10 | /signing.properties 11 | *.jks 12 | 13 | .DS_Store 14 | /build 15 | /captures 16 | /scripts/crowdin.key 17 | /app/src/main/assets/secretconstants.properties 18 | 19 | # debug purpose 20 | inappstoragereader -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | v0.7 2 | ==== 3 | 4 | ### Features 5 | * Color Palette 6 | * FingerPrint unlock #396 #235 7 | * Affix 8 | * Slide Show #266 9 | * Show all Images Feature #416 10 | * Print 11 | * Delete/Move/Copy/Rename with progress dialog, and cancel feature #386 12 | 13 | ### Fixes 14 | 15 | * Fixed crashes resuming from recents #475 #398 16 | * Fixed crashes KitKAT #418 #410 17 | * Improved opening from other apps #417 #403 -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | ## FREQUENTLY ASKED QUESTIONS FOR LEAFPIC 2 | 3 | Q – How do I hide folders/album? 4 | 5 | A – There’re two options available for that 6 | (i) Exclude 7 | (ii) Hide 8 | To hide long press on the folder you wish to hide and click on the **vertical dotted line** at the top right corner at the screen and select Hide/Exclude. 9 | 10 | Q – What is the difference between the Hide and the Exclude function on Leafpic? 11 | 12 | A – Exclude simply removes the specific album from the remaining list of album. While Hide would create a (.nonmedia ) file in the folder and would be inaccessible to other apps but an excluded album is accessible other media apps on your device. 13 | 14 | Q – How do I solve **Leafpics has stopped** while trying to play some videos on Leafpic ? 15 | 16 | A – Go to the video you wish to play and click the **dotted vertical line** at the top right corner of the screen, click **more** and select **open with** and choose from the listed video players on your device. 17 | 18 | Q – How do I set up security for my hidden folders? 19 | 20 | A – Click the **menu button** at the top left corner of the screen, select **settings**, select **security**, Activate **access by password**, insert and confirm your password and select **OK**. 21 | **NOTE** : minimum password length is 4. 22 | 23 | Q – How do I check/restore my Excluded folder/album? 24 | 25 | A – Get to the **settings page** of the app, click **Excluded Items** under the general settings, there you’ll find the excluded folder/album, Click the **X** sign to revert/restore the Excluded folder. 26 | 27 | Q – How do I get back to the main folder/album? 28 | 29 | A – Click the **menu button** at the top left side of the screen and select **local folder**. 30 | 31 | 32 | To Contact us please click on the **about section** and select your preferred communication channel. 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## This project is no longer maintained 2 | 3 | [![Crowdin](https://d322cqt584bo4o.cloudfront.net/leafpic/localized.svg)](https://crowdin.com/project/leafpic) 4 | [![Donate](https://img.shields.io/badge/donate-paypal-blue.svg)](https://www.paypal.me/HoraApps) 5 | 6 | 7 | # LeafPic 8 | 9 | LeafPic is a fluid, material-designed alternative gallery, it also is ad-free and open source under GPLv3 license. It doesn't miss any of the main features of a stock gallery, and we also have plans to add more useful features.
10 | 11 |
12 | 13 | Get it on F-Droid 15 | 16 | 17 | Get it on Google Play 20 | 21 |
22 |

23 | 24 | ## Screenshots 25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 |
33 | 34 | #### Contributing 35 | ## This project is no longer maintained 36 | 37 | ###### Code 38 | If you are a developer and you wish to contribute to the app please fork the project 39 | and submit a pull request on the [dev branch](https://gitlab.com/HoraApps/LeafPic/tree/dev). 40 | 41 | ###### Issues 42 | You can trace the status of known issues [here](https://gitlab.com/HoraApps/LeafPic/issues), 43 | also feel free to file a new issue (helpful description, screenshots and logcat are appreciated), or send me an [email](mailto:dnld.sht@gmail.com) if you have any questions. 44 | 45 | ###### Translations 46 | If you are able to contribute with a new translation of a missing language or if you want to improve an existing one, we greatly appreciate any suggestion! 47 | The project uses [Crowdin](https://crowdin.com/project/leafpic), a platform that allows anybody to contribute to translating the app 48 | 49 | #### Licensing 50 | LeafPic is licensed under the [GNU v3 Public License](https://gitlab.com/HoraApps/LeafPic/blob/dev/LICENSE). 51 | In addition to the terms set by the GNU v3 Public License, we ask that if you use any code from this repository that you send us a message to let us know. 52 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /src/main/gen/ 3 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/dnld/Android/Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep fullSizeOptions here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -keep class butterknife.** { *; } 19 | -dontwarn butterknife.internal.** 20 | -keep class **$$ViewBinder { *; } 21 | 22 | -keepclasseswithmembernames class * { 23 | @butterknife.* ; 24 | } 25 | 26 | -keepclasseswithmembernames class * { 27 | @butterknife.* ; 28 | } 29 | 30 | -keepnames class * { @butterknife.Bind *;} 31 | 32 | -dontwarn okio.** -------------------------------------------------------------------------------- /app/src/main/assets/latest_changelog.md: -------------------------------------------------------------------------------- 1 | v0.6-beta-1 2 | ================== 3 | - Fixed crash on startup and some random crash 4 | - Fixed crash opening video (Nougat) 5 | - fixed zoom out issue with SubScaling ImageView enabled 6 | - Updated translations 7 | - General improvements -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/App.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic; 2 | 3 | import android.support.multidex.MultiDexApplication; 4 | 5 | import com.mikepenz.community_material_typeface_library.CommunityMaterial; 6 | import com.mikepenz.fontawesome_typeface_library.FontAwesome; 7 | import com.mikepenz.google_material_typeface_library.GoogleMaterial; 8 | import com.mikepenz.iconics.Iconics; 9 | import com.orhanobut.hawk.Hawk; 10 | import com.squareup.leakcanary.LeakCanary; 11 | 12 | import org.horaapps.leafpic.util.ApplicationUtils; 13 | import org.horaapps.leafpic.util.preferences.Prefs; 14 | 15 | /** 16 | * Created by dnld on 28/04/16. 17 | */ 18 | public class App extends MultiDexApplication { 19 | 20 | private static App mInstance; 21 | 22 | @Override 23 | public void onCreate() { 24 | super.onCreate(); 25 | mInstance = this; 26 | 27 | ApplicationUtils.init(this); 28 | 29 | /** This process is dedicated to LeakCanary for heap analysis. 30 | * You should not init your app in this process. */ 31 | if (LeakCanary.isInAnalyzerProcess(this)) { 32 | return; 33 | } 34 | LeakCanary.install(this); 35 | 36 | registerFontIcons(); 37 | initialiseStorage(); 38 | } 39 | 40 | public static App getInstance() { 41 | return mInstance; 42 | } 43 | 44 | private void registerFontIcons() { 45 | Iconics.registerFont(new GoogleMaterial()); 46 | Iconics.registerFont(new CommunityMaterial()); 47 | Iconics.registerFont(new FontAwesome()); 48 | } 49 | 50 | private void initialiseStorage() { 51 | Prefs.init(this); 52 | Hawk.init(this).build(); 53 | } 54 | } -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/CardViewStyle.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic; 2 | 3 | 4 | /** 5 | * Created by Jibo on 20/11/2016. 6 | */ 7 | public enum CardViewStyle { 8 | 9 | MATERIAL(0, R.layout.card_album_material), 10 | FLAT(1, R.layout.card_album_flat), 11 | COMPACT(2, R.layout.card_album_compact); 12 | 13 | private static final int size = CardViewStyle.values().length; 14 | 15 | int value; 16 | int layout; 17 | 18 | CardViewStyle(int value, int layout) { 19 | this.value = value; 20 | this.layout = layout; 21 | } 22 | 23 | public int getLayout() { 24 | return layout; 25 | } 26 | 27 | public int getValue() { return value; } 28 | 29 | public static int getSize() { 30 | return size; 31 | } 32 | 33 | public static CardViewStyle fromValue(int value){ 34 | switch (value){ 35 | case 0: default: return MATERIAL; 36 | case 1: return FLAT; 37 | case 2: return COMPACT; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/CustomGlideModule.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic; 2 | 3 | import android.content.Context; 4 | 5 | import com.bumptech.glide.Glide; 6 | import com.bumptech.glide.GlideBuilder; 7 | import com.bumptech.glide.Registry; 8 | 9 | /** 10 | * Created by dnld on 10/03/16. 11 | */ 12 | 13 | public class CustomGlideModule implements com.bumptech.glide.module.GlideModule { 14 | 15 | @Override 16 | public void registerComponents(Context context, Glide glide, Registry registry) { 17 | 18 | } 19 | 20 | @Override 21 | public void applyOptions(Context context, GlideBuilder builder) { 22 | // Apply options to the builder here. 23 | /*builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888); 24 | 25 | MemorySizeCalculator calculator = new MemorySizeCalculator(context); 26 | int defaultMemoryCacheSize = calculator.getMemoryCacheSize(); 27 | int defaultBitmapPoolSize = calculator.getBitmapPoolSize(); 28 | 29 | int customMemoryCacheSize = (int) (1.2 * defaultMemoryCacheSize); 30 | int customBitmapPoolSize = (int) (1.2 * defaultBitmapPoolSize); 31 | 32 | builder.setMemoryCache(new LruResourceCache(customMemoryCacheSize)); 33 | builder.setBitmapPool(new LruBitmapPool(customBitmapPoolSize)); 34 | 35 | int cacheSize100MegaBytes = 104857600; 36 | 37 | builder.setDiskCache( 38 | new InternalCacheDiskCacheFactory(context, cacheSize100MegaBytes) 39 | );*/ 40 | } 41 | } -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/SecretConstants.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic; 2 | 3 | import android.content.Context; 4 | 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.util.Properties; 8 | 9 | /** 10 | * Created by dnld on 31/07/16. 11 | */ 12 | 13 | public class SecretConstants { 14 | 15 | private static String base64EncodedPublicKey; 16 | 17 | public static String MAP_BOX_TOKEN = "pk.eyJ1IjoiZG5sZCIsImEiOiJjaXJycmVham4wMGRsaGpuaHQ4Y3Fhb2MzIn0.kUn2aNbfpS3-wDJ-s0DLFw"; 18 | 19 | public static String getBase64EncodedPublicKey(Context context) { 20 | if (base64EncodedPublicKey == null) { 21 | InputStream input; 22 | try { 23 | input = context.getAssets().open("secretconstants.properties"); 24 | Properties properties = new Properties(); 25 | properties.load(input); 26 | base64EncodedPublicKey = properties.getProperty("gplaykey"); 27 | } catch (IOException e) { 28 | // file not found 29 | base64EncodedPublicKey = ""; 30 | } 31 | 32 | } 33 | return base64EncodedPublicKey; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/about/Contact.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.about; 2 | 3 | /** 4 | * Created by dnld on 04/03/18. 5 | */ 6 | 7 | public class Contact { 8 | private String value; 9 | private String label; 10 | 11 | public Contact(String value, String label) { 12 | this.value = value; 13 | this.label = label; 14 | } 15 | 16 | public String getLabel() { 17 | return label; 18 | } 19 | 20 | public void setLabel(String label) { 21 | this.label = label; 22 | } 23 | 24 | public String getValue() { 25 | return value; 26 | } 27 | 28 | public void setValue(String value) { 29 | this.value = value; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/about/ContactButton.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.about; 2 | 3 | import android.content.Context; 4 | import android.graphics.Typeface; 5 | import android.support.annotation.NonNull; 6 | import android.view.ViewGroup; 7 | import android.widget.LinearLayout; 8 | 9 | import org.horaapps.leafpic.R; 10 | import org.horaapps.liz.ThemeHelper; 11 | import org.horaapps.liz.ui.ThemedTextView; 12 | 13 | /** 14 | * Created by dnld on 04/03/18. 15 | */ 16 | 17 | public class ContactButton extends ThemedTextView { 18 | 19 | public ContactButton(Context context) { 20 | super(context); 21 | setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); 22 | int padd = (int) context.getResources().getDimension(R.dimen.developer_links_small_padding); 23 | setPadding(padd, padd, padd, padd); 24 | setTextSize(16); 25 | } 26 | 27 | public void bold() { 28 | setTypeface(null, Typeface.BOLD); 29 | } 30 | 31 | @Override 32 | public void refreshTheme(ThemeHelper theme) { 33 | setTextColor(theme.getAccentColor()); 34 | } 35 | 36 | public void setText(@NonNull String text) { 37 | super.setText(text.toUpperCase()); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/about/ContactListener.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.about; 2 | 3 | /** 4 | * Created by dnld on 04/03/18. 5 | */ 6 | 7 | public interface ContactListener { 8 | void onContactClicked(Contact contact); 9 | void onMailClicked(String mail); 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/about/Contributor.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.about; 2 | 3 | import android.support.annotation.DrawableRes; 4 | 5 | import java.util.ArrayList; 6 | 7 | /** 8 | * Created by dnld on 04/03/18. 9 | */ 10 | 11 | public class Contributor { 12 | private String name; 13 | private String description; 14 | private String email; 15 | private @DrawableRes 16 | int profileImage; 17 | 18 | private ArrayList contacts; 19 | 20 | public Contributor(String name, String description, @DrawableRes int profileImage) { 21 | this.name = name; 22 | this.description = description; 23 | this.profileImage = profileImage; 24 | this.contacts = new ArrayList<>(); 25 | } 26 | 27 | public void setEmail(String email) { 28 | this.email = email; 29 | } 30 | 31 | public void addSocial(String label, String url) { 32 | Contact c = new Contact(url, label); 33 | contacts.add(c); 34 | } 35 | 36 | public String getEmail() { 37 | return email; 38 | } 39 | 40 | public String getName() { 41 | return name; 42 | } 43 | 44 | public String getDescription() { 45 | return description; 46 | } 47 | 48 | public int getProfileImage() { 49 | return profileImage; 50 | } 51 | 52 | public ArrayList getContacts() { 53 | return contacts; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/about/ContributorViewHolder.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.about; 2 | 3 | import android.support.annotation.DrawableRes; 4 | import android.support.annotation.Nullable; 5 | import android.view.View; 6 | import android.widget.LinearLayout; 7 | import android.widget.TextView; 8 | 9 | import org.horaapps.leafpic.R; 10 | import org.horaapps.liz.ThemeHelper; 11 | import org.horaapps.liz.ThemedViewHolder; 12 | 13 | import java.util.ArrayList; 14 | 15 | import butterknife.BindView; 16 | import butterknife.ButterKnife; 17 | import de.hdodenhof.circleimageview.CircleImageView; 18 | 19 | /** 20 | * Custom ViewHolder for populating a Contributor onto {@link ContributorsAdapter} 21 | */ 22 | public class ContributorViewHolder extends ThemedViewHolder { 23 | 24 | @BindView(R.id.contributor_profile_image) CircleImageView profileImage; 25 | @BindView(R.id.contributor_name) TextView contribName; 26 | @BindView(R.id.contributor_description) TextView contribDescription; 27 | @BindView(R.id.contributor_contacts) LinearLayout contribContacts; 28 | 29 | public ContributorViewHolder(View view) { 30 | super(view); 31 | ButterKnife.bind(this, itemView); 32 | } 33 | 34 | private void setProfileImage(@DrawableRes int profileImage) { 35 | this.profileImage.setImageResource(profileImage); 36 | } 37 | 38 | private void setName(@Nullable String name) { 39 | contribName.setText(name); 40 | } 41 | 42 | private void setDescription(@Nullable String description) { 43 | contribDescription.setText(description); 44 | } 45 | 46 | @Override 47 | public void refreshTheme(ThemeHelper themeHelper) { 48 | super.refreshTheme(themeHelper); 49 | int borderColor = themeHelper.getInvertedBackgroundColor(); 50 | profileImage.setBorderColor(borderColor); 51 | } 52 | 53 | public void load(Contributor contributor, ContactListener listener) { 54 | setName(contributor.getName()); 55 | setDescription(contributor.getDescription()); 56 | setProfileImage(contributor.getProfileImage()); 57 | 58 | contribContacts.removeAllViews(); 59 | 60 | if (contributor.getEmail() != null) { 61 | ContactButton email = new ContactButton(itemView.getContext()); 62 | email.setText(itemView.getContext().getString(R.string.send_email)); 63 | email.bold(); 64 | email.setOnClickListener(v -> listener.onMailClicked(contributor.getEmail())); 65 | contribContacts.addView(email); 66 | } 67 | 68 | ArrayList contacts = contributor.getContacts(); 69 | for (Contact contact : contacts) { 70 | ContactButton c = new ContactButton(itemView.getContext()); 71 | c.setText(contact.getLabel()); 72 | c.setOnClickListener(v -> listener.onContactClicked(contact)); 73 | contribContacts.addView(c); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/about/ContributorsAdapter.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.about; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.ViewGroup; 6 | 7 | import org.horaapps.leafpic.R; 8 | import org.horaapps.liz.ThemedAdapter; 9 | 10 | import java.util.ArrayList; 11 | 12 | /** 13 | * Created by dnld on 04/03/18. 14 | */ 15 | 16 | public class ContributorsAdapter extends ThemedAdapter { 17 | 18 | private ContactListener listener; 19 | private ArrayList contributors; 20 | 21 | ContributorsAdapter(Context context, ArrayList contributors, ContactListener listener) { 22 | super(context); 23 | this.contributors = contributors; 24 | this.listener = listener; 25 | } 26 | 27 | @Override 28 | public ContributorViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 29 | return new ContributorViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.view_about_contributor, parent, false)); 30 | } 31 | 32 | @Override 33 | public void onBindViewHolder(ContributorViewHolder holder, int position) { 34 | Contributor contributor = contributors.get(position); 35 | holder.load(contributor, listener); 36 | super.onBindViewHolder(holder, position); 37 | } 38 | 39 | @Override 40 | public int getItemCount() { 41 | return contributors.size(); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/activities/base/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.activities.base; 2 | 3 | import android.support.annotation.CallSuper; 4 | 5 | import org.horaapps.liz.ThemedActivity; 6 | 7 | import io.reactivex.disposables.CompositeDisposable; 8 | import io.reactivex.disposables.Disposable; 9 | 10 | public abstract class BaseActivity extends ThemedActivity { 11 | CompositeDisposable disposables = new CompositeDisposable(); 12 | 13 | 14 | public void disposeLater(Disposable disposable) { 15 | disposables.add(disposable); 16 | } 17 | 18 | @CallSuper 19 | @Override 20 | protected void onDestroy() { 21 | disposables.dispose(); 22 | super.onDestroy(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/activities/base/SharedMediaActivity.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.activities.base; 2 | 3 | import android.content.DialogInterface; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.support.v7.app.AlertDialog; 7 | import android.widget.Toast; 8 | 9 | import org.horaapps.leafpic.R; 10 | import org.horaapps.leafpic.data.StorageHelper; 11 | import org.horaapps.leafpic.util.AlertDialogsHelper; 12 | 13 | /** 14 | * Created by dnld on 03/08/16. 15 | */ 16 | 17 | public abstract class SharedMediaActivity extends BaseActivity { 18 | 19 | private int REQUEST_CODE_SD_CARD_PERMISSIONS = 42; 20 | 21 | public void requestSdCardPermissions() { 22 | AlertDialog textDialog = AlertDialogsHelper.getTextDialog(this, R.string.sd_card_write_permission_title, R.string.sd_card_permissions_message); 23 | textDialog.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.ok_action).toUpperCase(), new DialogInterface.OnClickListener() { 24 | @Override 25 | public void onClick(DialogInterface dialogInterface, int i) { 26 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) 27 | startActivityForResult(new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE), REQUEST_CODE_SD_CARD_PERMISSIONS); 28 | } 29 | }); 30 | textDialog.show(); 31 | } 32 | 33 | @Override 34 | public void onActivityResult(final int requestCode, final int resultCode, final Intent resultData) { 35 | if (resultCode == RESULT_OK) { 36 | if (requestCode == REQUEST_CODE_SD_CARD_PERMISSIONS) { 37 | Uri treeUri = resultData.getData(); 38 | // Persist URI in shared preference so that you can use it later. 39 | StorageHelper.saveSdCardInfo(getApplicationContext(), treeUri); 40 | getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 41 | Toast.makeText(this, R.string.got_permission_wr_sdcard, Toast.LENGTH_SHORT).show(); 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/adapters/MediaPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.adapters; 2 | 3 | import android.support.annotation.NonNull; 4 | import android.support.v4.app.Fragment; 5 | import android.support.v4.app.FragmentManager; 6 | import android.support.v4.app.FragmentStatePagerAdapter; 7 | import android.support.v4.view.PagerAdapter; 8 | import android.util.SparseArray; 9 | import android.view.ViewGroup; 10 | 11 | import org.horaapps.leafpic.data.Media; 12 | import org.horaapps.leafpic.fragments.GifFragment; 13 | import org.horaapps.leafpic.fragments.ImageFragment; 14 | import org.horaapps.leafpic.fragments.VideoFragment; 15 | 16 | import java.util.ArrayList; 17 | 18 | /** 19 | * Created by dnld on 18/02/16. 20 | */ 21 | 22 | public class MediaPagerAdapter extends FragmentStatePagerAdapter { 23 | 24 | private final String TAG = "asd"; 25 | private ArrayList media; 26 | private SparseArray registeredFragments = new SparseArray<>(); 27 | 28 | public MediaPagerAdapter(FragmentManager fm, ArrayList media) { 29 | super(fm); 30 | this.media = media; 31 | } 32 | 33 | @Override 34 | public Fragment getItem(int pos) { 35 | Media media = this.media.get(pos); 36 | if (media.isVideo()) return VideoFragment.newInstance(media); 37 | if (media.isGif()) return GifFragment.newInstance(media); 38 | else return ImageFragment.newInstance(media); 39 | } 40 | 41 | @NonNull 42 | @Override 43 | public Object instantiateItem(ViewGroup container, int position) { 44 | Fragment fragment = (Fragment) super.instantiateItem(container, position); 45 | registeredFragments.put(position, fragment); 46 | return fragment; 47 | } 48 | 49 | @Override 50 | public void destroyItem(ViewGroup container, int position, Object object) { 51 | registeredFragments.remove(position); 52 | super.destroyItem(container, position, object); 53 | } 54 | 55 | public Fragment getRegisteredFragment(int position) { 56 | return registeredFragments.get(position); 57 | } 58 | 59 | public void swapDataSet(ArrayList media) { 60 | this.media = media; 61 | notifyDataSetChanged(); 62 | } 63 | 64 | @Override 65 | public int getItemPosition(@NonNull Object object) { 66 | return PagerAdapter.POSITION_NONE; 67 | } 68 | 69 | @Override 70 | public int getCount() { 71 | return media.size(); 72 | } 73 | } -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/animations/DepthPageTransformer.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.animations; 2 | 3 | import android.support.v4.view.ViewPager; 4 | import android.view.View; 5 | 6 | /** 7 | * Created by dnld on 1/18/16. 8 | */ 9 | public class DepthPageTransformer implements ViewPager.PageTransformer { 10 | private static final float MIN_SCALE = 0.75f; 11 | 12 | public void transformPage(View view, float position) { 13 | int pageWidth = view.getWidth(); 14 | 15 | if (position < -1) { // [-Infinity,-1) 16 | // This page is way off-screen to the left. 17 | view.setAlpha(0); 18 | 19 | } else if (position <= 0) { // [-1,0] 20 | // Use the default slide transition when moving to the left page 21 | view.setAlpha(1); 22 | view.setTranslationX(0); 23 | view.setScaleX(1); 24 | view.setScaleY(1); 25 | 26 | } else if (position <= 1) { // (0,1] 27 | // Fade the page out. 28 | view.setAlpha(1 - position); 29 | 30 | // Counteract the default slide transition 31 | view.setTranslationX(pageWidth * -position); 32 | 33 | // Scale the page down (between MIN_SCALE and 1) 34 | float scaleFactor = MIN_SCALE 35 | + (1 - MIN_SCALE) * (1 - Math.abs(position)); 36 | view.setScaleX(scaleFactor); 37 | view.setScaleY(scaleFactor); 38 | 39 | } else { // (1,+Infinity] 40 | // This page is way off-screen to the right. 41 | view.setAlpha(0); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/data/AlbumSettings.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.data; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import org.horaapps.leafpic.data.filter.FilterMode; 7 | import org.horaapps.leafpic.data.sort.SortingMode; 8 | import org.horaapps.leafpic.data.sort.SortingOrder; 9 | 10 | import java.io.Serializable; 11 | 12 | /** 13 | * Created by dnld on 2/4/16. 14 | */ 15 | public class AlbumSettings implements Serializable, Parcelable { 16 | 17 | String coverPath; 18 | int sortingMode, sortingOrder; 19 | boolean pinned; 20 | FilterMode filterMode = FilterMode.ALL; 21 | 22 | public static AlbumSettings getDefaults() { 23 | return new AlbumSettings(null, SortingMode.DATE.getValue(), 1, 0); 24 | } 25 | 26 | AlbumSettings(String cover, int sortingMode, int sortingOrder, int pinned) { 27 | this.coverPath = cover; 28 | this.sortingMode = sortingMode; 29 | this.sortingOrder = sortingOrder; 30 | this.pinned = pinned == 1; 31 | } 32 | 33 | public SortingMode getSortingMode() { 34 | return SortingMode.fromValue(sortingMode); 35 | } 36 | 37 | public SortingOrder getSortingOrder() { 38 | return SortingOrder.fromValue(sortingOrder); 39 | } 40 | 41 | @Override 42 | public int describeContents() { 43 | return 0; 44 | } 45 | 46 | @Override 47 | public void writeToParcel(Parcel dest, int flags) { 48 | dest.writeString(this.coverPath); 49 | dest.writeInt(this.sortingMode); 50 | dest.writeInt(this.sortingOrder); 51 | dest.writeByte(this.pinned ? (byte) 1 : (byte) 0); 52 | dest.writeInt(this.filterMode == null ? -1 : this.filterMode.ordinal()); 53 | } 54 | 55 | /** This is the constructor used by CREATOR. */ 56 | protected AlbumSettings(Parcel in) { 57 | this.coverPath = in.readString(); 58 | this.sortingMode = in.readInt(); 59 | this.sortingOrder = in.readInt(); 60 | this.pinned = in.readByte() != 0; 61 | int tmpFilterMode = in.readInt(); 62 | this.filterMode = tmpFilterMode == -1 ? null : FilterMode.values()[tmpFilterMode]; 63 | } 64 | 65 | /** It is a non-null static field that must be in parcelable. */ 66 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 67 | 68 | @Override 69 | public AlbumSettings createFromParcel(Parcel source) { 70 | return new AlbumSettings(source); 71 | } 72 | 73 | @Override 74 | public AlbumSettings[] newArray(int size) { 75 | return new AlbumSettings[size]; 76 | } 77 | }; 78 | } -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/data/CursorHandler.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.data; 2 | 3 | import android.database.Cursor; 4 | 5 | /** 6 | * Created by dnld on 3/13/17. 7 | */ 8 | 9 | public interface CursorHandler { 10 | 11 | T handle(Cursor cu); 12 | static String [] getProjection() { 13 | return new String[0]; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/data/IAlbum.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.data; 2 | 3 | /** 4 | * Created by dnld on 6/28/17. 5 | */ 6 | 7 | public interface IAlbum { 8 | String getName(); 9 | String getPath(); 10 | int getCount(); 11 | Media getCover(); 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/data/filter/FilterMode.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.data.filter; 2 | 3 | /** 4 | * Created by dnld on 18/08/16. 5 | */ 6 | 7 | public enum FilterMode { 8 | ALL, 9 | IMAGES, 10 | GIF, 11 | VIDEO, 12 | NO_VIDEO 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/data/filter/FoldersFileFilter.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.data.filter; 2 | 3 | import java.io.File; 4 | import java.io.FileFilter; 5 | 6 | /** 7 | * Created by dnld on 24/04/16. 8 | */ 9 | public class FoldersFileFilter implements FileFilter { 10 | @Override 11 | public boolean accept(File pathname) { 12 | return pathname.isDirectory(); 13 | } 14 | } -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/data/filter/IMediaFilter.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.data.filter; 2 | 3 | import org.horaapps.leafpic.data.Media; 4 | 5 | /** 6 | * Created by dnld on 4/10/17. 7 | */ 8 | 9 | public interface IMediaFilter { 10 | boolean accept(Media media); 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/data/filter/ImageFileFilter.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.data.filter; 2 | 3 | import java.io.File; 4 | import java.io.FilenameFilter; 5 | import java.util.regex.Pattern; 6 | 7 | /** 8 | * Created by dnld on 24/04/16. 9 | */ 10 | public class ImageFileFilter implements FilenameFilter { 11 | 12 | private Pattern pattern; 13 | 14 | public ImageFileFilter(boolean includeVideo) { 15 | pattern = includeVideo 16 | ? Pattern.compile(".(jpg|png|gif|jpe|jpeg|bmp|webp|mp4|mkv|webm|avi)$", Pattern.CASE_INSENSITIVE) 17 | : Pattern.compile(".(jpg|png|gif|jpe|jpeg|bmp|webp)$", Pattern.CASE_INSENSITIVE); 18 | } 19 | 20 | @Override 21 | public boolean accept(File dir, String filename) { 22 | return new File(dir, filename).isFile() && pattern.matcher(filename).find(); 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/data/filter/MediaFilter.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.data.filter; 2 | 3 | import org.horaapps.leafpic.data.Media; 4 | 5 | /** 6 | * Created by dnld on 4/10/17. 7 | */ 8 | public class MediaFilter { 9 | public static IMediaFilter getFilter(FilterMode mode) { 10 | switch (mode) { 11 | case ALL: default: 12 | return media -> true; 13 | case GIF: 14 | return Media::isGif; 15 | case VIDEO: 16 | return Media::isVideo; 17 | case IMAGES: return Media::isImage; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/data/filter/NotHiddenFoldersFilter.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.data.filter; 2 | 3 | import java.io.File; 4 | import java.io.FileFilter; 5 | 6 | /** 7 | * Created by dnld on 24/04/16. 8 | */ 9 | public class NotHiddenFoldersFilter implements FileFilter { 10 | @Override 11 | public boolean accept(File file) { 12 | return file.isDirectory() && !file.isHidden() && !new File(file, ".nomedia").exists(); 13 | } 14 | } -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/data/metadata/MediaDetailsMap.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.data.metadata; 2 | 3 | import java.util.HashMap; 4 | import java.util.TreeMap; 5 | 6 | /** 7 | * Created by dnld on 16/08/16. 8 | */ 9 | 10 | public class MediaDetailsMap extends HashMap { 11 | 12 | private TreeMap keys; 13 | 14 | public MediaDetailsMap() { 15 | super(); 16 | keys = new TreeMap<>(); 17 | } 18 | 19 | public String getValue(int index) { 20 | String key = keys.get(index); 21 | return super.get(key); 22 | } 23 | 24 | public String getLabel(int index) { 25 | return keys.get(index); 26 | } 27 | 28 | @Override 29 | public String put(String key, String value) { 30 | keys.put(keys.size(), key); 31 | return super.put(key, value); 32 | } 33 | 34 | public int[] getKeySet() { 35 | int[] array = new int[keys.size()]; 36 | for(int i = 0; i < keys.size(); i++) array[i]=i; 37 | return array; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/data/provider/ContentProviderHelper.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.data.provider; 2 | 3 | import android.content.Context; 4 | import android.database.Cursor; 5 | import android.provider.MediaStore; 6 | 7 | import org.horaapps.leafpic.data.Album; 8 | 9 | import java.io.File; 10 | 11 | /** 12 | * Created by dnld on 24/07/16. 13 | */ 14 | 15 | @Deprecated 16 | public class ContentProviderHelper { 17 | 18 | @Deprecated 19 | public static long getAlbumId(Context context, String mediaPath) { 20 | long id = -1; 21 | Cursor cur = context.getContentResolver().query(MediaStore.Files.getContentUri("external"), 22 | new String[]{ MediaStore.Files.FileColumns.PARENT }, 23 | MediaStore.Files.FileColumns.DATA+"=?", new String[]{ mediaPath }, null); 24 | 25 | if(cur != null && cur.moveToFirst()){ 26 | id = cur.getLong(0); 27 | cur.close(); 28 | } 29 | 30 | return id; 31 | } 32 | 33 | @Deprecated 34 | public static Album getAlbumFromMedia(Context context, String mediaPath) { 35 | File parentFolder = new File(mediaPath).getParentFile(); 36 | if (parentFolder == null || !parentFolder.isDirectory()) 37 | return null; 38 | 39 | return new Album(context, parentFolder.getPath(), getAlbumId(context, mediaPath), parentFolder.getName(), 0); 40 | } 41 | } 42 | 43 | 44 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/data/provider/QueryUtils.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.data.provider; 2 | 3 | import android.content.ContentResolver; 4 | import android.database.Cursor; 5 | 6 | import org.horaapps.leafpic.data.CursorHandler; 7 | 8 | import io.reactivex.Observable; 9 | 10 | 11 | /** 12 | * Created by dnld on 3/13/17. 13 | */ 14 | 15 | public class QueryUtils { 16 | 17 | public static Observable query(Query q, ContentResolver cr, CursorHandler ch) { 18 | return Observable.create(subscriber -> { 19 | Cursor cursor = null; 20 | try { 21 | cursor = q.getCursor(cr); 22 | if (cursor != null && cursor.getCount() > 0) 23 | while (cursor.moveToNext()) subscriber.onNext(ch.handle(cursor)); 24 | subscriber.onComplete(); 25 | } 26 | catch (Exception err) { subscriber.onError(err); } 27 | finally { if (cursor != null) cursor.close(); } 28 | }); 29 | } 30 | 31 | /** 32 | * return only the first element if there is one 33 | * 34 | * @param q 35 | * @param cr 36 | * @param ch 37 | * @param 38 | * @return 39 | */ 40 | public static Observable querySingle(Query q, ContentResolver cr, CursorHandler ch) { 41 | return Observable.create(subscriber -> { 42 | Cursor cursor = null; 43 | try { 44 | cursor = q.getCursor(cr); 45 | if (cursor != null && cursor.moveToFirst()) 46 | subscriber.onNext(ch.handle(cursor)); 47 | subscriber.onComplete(); 48 | } 49 | catch (Exception err) { subscriber.onError(err); } 50 | finally { if (cursor != null) cursor.close(); } 51 | }); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/data/sort/MediaComparators.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.data.sort; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | import org.horaapps.leafpic.data.AlbumSettings; 6 | import org.horaapps.leafpic.data.Media; 7 | import org.horaapps.leafpic.timeline.data.TimelineHeaderModel; 8 | import org.horaapps.leafpic.util.NumericComparator; 9 | 10 | import java.util.Comparator; 11 | 12 | /** 13 | * Created by dnld on 26/04/16. 14 | */ 15 | 16 | public class MediaComparators { 17 | 18 | public static Comparator getComparator(AlbumSettings settings) { 19 | return getComparator(settings.getSortingMode(), settings.getSortingOrder()); 20 | } 21 | 22 | public static Comparator getComparator(SortingMode sortingMode, SortingOrder sortingOrder) { 23 | return sortingOrder == SortingOrder.ASCENDING 24 | ? getComparator(sortingMode) : reverse(getComparator(sortingMode)); 25 | } 26 | 27 | public static Comparator getTimelineComparator(@NonNull SortingOrder sortingOrder) { 28 | return sortingOrder.isAscending() ? getTimelineComparator() : reverse(getTimelineComparator()); 29 | } 30 | 31 | public static Comparator getComparator(SortingMode sortingMode) { 32 | switch (sortingMode) { 33 | case NAME: return getNameComparator(); 34 | case DATE: default: return getDateComparator(); 35 | case SIZE: return getSizeComparator(); 36 | case TYPE: return getTypeComparator(); 37 | case NUMERIC: return getNumericComparator(); 38 | } 39 | } 40 | 41 | private static Comparator reverse(Comparator comparator) { 42 | return (o1, o2) -> comparator.compare(o2, o1); 43 | } 44 | 45 | private static Comparator getDateComparator() { 46 | return (f1, f2) -> f1.getDateModified().compareTo(f2.getDateModified()); 47 | } 48 | 49 | private static Comparator getNameComparator() { 50 | return (f1, f2) -> f1.getPath().compareTo(f2.getPath()); 51 | } 52 | 53 | private static Comparator getSizeComparator() { 54 | return (f1, f2) -> Long.compare(f1.getSize(), f2.getSize()); 55 | } 56 | 57 | private static Comparator getTypeComparator() { 58 | return (f1, f2) -> f1.getMimeType().compareTo(f2.getMimeType()); 59 | } 60 | 61 | private static Comparator getNumericComparator() { 62 | return (f1, f2) -> NumericComparator.filevercmp(f1.getPath(), f2.getPath()); 63 | } 64 | 65 | private static Comparator getTimelineComparator() { 66 | return (t1, t2) -> t1.getDate().compareTo(t2.getDate()); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/data/sort/SortingMode.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.data.sort; 2 | 3 | import android.provider.MediaStore; 4 | 5 | /** 6 | * Created by dnld on 18/08/16. 7 | */ 8 | 9 | public enum SortingMode { 10 | NAME (0, MediaStore.MediaColumns.DISPLAY_NAME, MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME), 11 | DATE (1, MediaStore.MediaColumns.DATE_MODIFIED, "max(" + MediaStore.Images.Media.DATE_MODIFIED + ")"), 12 | SIZE(2, MediaStore.MediaColumns.SIZE, "count(*)"), 13 | TYPE(3, MediaStore.MediaColumns.MIME_TYPE), 14 | NUMERIC(4, MediaStore.MediaColumns.DISPLAY_NAME, MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME); 15 | 16 | int value; 17 | String mediaColumn; 18 | String albumsColumn; 19 | 20 | SortingMode(int value, String mediaColumn) { 21 | this.value = value; 22 | this.mediaColumn = mediaColumn; 23 | this.albumsColumn = mediaColumn; 24 | } 25 | 26 | SortingMode(int value, String mediaColumn, String albumsColumn) { 27 | this.value = value; 28 | this.mediaColumn = mediaColumn; 29 | this.albumsColumn = albumsColumn; 30 | } 31 | 32 | public String getMediaColumn() { 33 | return mediaColumn; 34 | } 35 | 36 | public String getAlbumsColumn() { 37 | return albumsColumn; 38 | } 39 | 40 | public int getValue() { 41 | return value; 42 | } 43 | 44 | public static SortingMode fromValue(int value) { 45 | switch (value) { 46 | case 0: return NAME; 47 | case 1: default: return DATE; 48 | case 2: return SIZE; 49 | case 3: return TYPE; 50 | case 4: return NUMERIC; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/data/sort/SortingOrder.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.data.sort; 2 | 3 | /** 4 | * Created by dnld on 18/08/16. 5 | */ 6 | 7 | public enum SortingOrder { 8 | ASCENDING(1), DESCENDING(0); 9 | 10 | int value; 11 | 12 | SortingOrder(int value) { 13 | this.value = value; 14 | } 15 | 16 | public int getValue() { 17 | return value; 18 | } 19 | 20 | public boolean isAscending() { 21 | return value == ASCENDING.getValue(); 22 | } 23 | 24 | public static SortingOrder fromValue(boolean value) { 25 | return value ? ASCENDING : DESCENDING; 26 | } 27 | 28 | public static SortingOrder fromValue(int value) { 29 | return value == 0 ? DESCENDING : ASCENDING; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/fragments/BaseFragment.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.fragments; 2 | 3 | import android.content.Context; 4 | 5 | import org.horaapps.liz.ThemedFragment; 6 | 7 | /** 8 | * Base Fragment for abstraction logic. 9 | */ 10 | public abstract class BaseFragment extends ThemedFragment { 11 | 12 | private NothingToShowListener nothingToShowListener; 13 | 14 | @Override 15 | public void onAttach(Context context) { 16 | super.onAttach(context); 17 | 18 | if (context instanceof NothingToShowListener) 19 | nothingToShowListener = (NothingToShowListener) context; 20 | } 21 | 22 | public NothingToShowListener getNothingToShowListener() { 23 | return nothingToShowListener; 24 | } 25 | 26 | public void setNothingToShowListener(NothingToShowListener nothingToShowListener) { 27 | this.nothingToShowListener = nothingToShowListener; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/fragments/BaseMediaFragment.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.fragments; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.support.annotation.NonNull; 6 | import android.view.View; 7 | 8 | import org.horaapps.leafpic.data.Media; 9 | import org.horaapps.liz.ThemeHelper; 10 | 11 | /** 12 | * A Base Fragment for showing Media. 13 | */ 14 | public abstract class BaseMediaFragment extends BaseFragment { 15 | 16 | private static final String ARGS_MEDIA = "args_media"; 17 | 18 | protected Media media; 19 | private MediaTapListener mediaTapListener; 20 | 21 | @NonNull 22 | protected static T newInstance(@NonNull T mediaFragment, 23 | @NonNull Media media) { 24 | 25 | Bundle args = new Bundle(); 26 | args.putParcelable(ARGS_MEDIA, media); 27 | mediaFragment.setArguments(args); 28 | return mediaFragment; 29 | } 30 | 31 | @Override 32 | public void onAttach(Context context) { 33 | super.onAttach(context); 34 | if (context instanceof MediaTapListener) mediaTapListener = (MediaTapListener) context; 35 | } 36 | 37 | private void fetchArgs() { 38 | Bundle args = getArguments(); 39 | if (args == null) throw new RuntimeException("Must pass arguments to Media Fragments!"); 40 | media = getArguments().getParcelable(ARGS_MEDIA); 41 | } 42 | 43 | @Override 44 | public void onCreate(Bundle savedInstanceState) { 45 | super.onCreate(savedInstanceState); 46 | fetchArgs(); 47 | } 48 | 49 | @Override 50 | public void refreshTheme(ThemeHelper themeHelper) { 51 | // Default implementation 52 | } 53 | 54 | protected void setTapListener(@NonNull View view) { 55 | view.setOnClickListener(v -> onTapped()); 56 | } 57 | 58 | private void onTapped() { 59 | mediaTapListener.onViewTapped(); 60 | } 61 | 62 | /** 63 | * Interface for listeners to react on Media Clicks. 64 | */ 65 | public interface MediaTapListener { 66 | 67 | /** 68 | * Called when user taps on the Media view. 69 | */ 70 | void onViewTapped(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/fragments/BaseMediaGridFragment.kt: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.fragments 2 | 3 | import android.content.Context 4 | import android.view.View 5 | 6 | import org.horaapps.leafpic.items.ActionsListener 7 | 8 | /** 9 | * Base class for fragments showing any kind of Media in a Grid fashion. 10 | * 11 | * Allows selection, multiple select Context Menus, etc. 12 | */ 13 | abstract class BaseMediaGridFragment : BaseFragment(), IFragment, ActionsListener { 14 | 15 | lateinit var editModeListener: EditModeListener 16 | 17 | override fun onAttach(context: Context?) { 18 | super.onAttach(context) 19 | if (context is EditModeListener) editModeListener = context 20 | else throw RuntimeException("Parent must implement Edit Mode Listener!") 21 | } 22 | 23 | fun onBackPressed() = when (editMode()) { 24 | true -> { 25 | exitContextMenu() 26 | true 27 | } 28 | false -> false 29 | } 30 | 31 | /** 32 | * Exit the Context Menu. 33 | */ 34 | protected fun exitContextMenu() { 35 | clearSelected() 36 | updateToolbar() 37 | } 38 | 39 | /** 40 | * Update the Toolbar for switching between Edit Mode. 41 | */ 42 | protected fun updateToolbar() { 43 | editModeListener.changedEditMode( 44 | editMode(), 45 | getSelectedCount(), 46 | getTotalCount(), 47 | getToolbarButtonListener(editMode()), 48 | getToolbarTitle()) 49 | 50 | // Refresh the Toolbar menu 51 | activity?.invalidateOptionsMenu() 52 | } 53 | 54 | /** 55 | * The total selected item count. 56 | */ 57 | abstract fun getSelectedCount(): Int 58 | 59 | /** 60 | * The total number of items displayed. 61 | */ 62 | abstract fun getTotalCount(): Int 63 | 64 | /** 65 | * A listener to be invoked when user taps on the Toolbar icon. 66 | */ 67 | abstract fun getToolbarButtonListener(editMode: Boolean): View.OnClickListener? 68 | 69 | /** 70 | * Text to display on the toolbar. 71 | */ 72 | abstract fun getToolbarTitle(): String? 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/fragments/EditModeListener.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.fragments; 2 | 3 | import android.view.View; 4 | 5 | import javax.annotation.Nullable; 6 | 7 | /** 8 | * Created by dnld on 12/16/17. 9 | */ 10 | 11 | public interface EditModeListener { 12 | 13 | /** 14 | * Propagate Edit Mode switches to listeners. 15 | * 16 | * @param editMode Whether we are in Edit Mode or not. 17 | * @param selected The number of items selected. 18 | * @param total The total number of items. 19 | * @param listener The listener for Toolbar Back Button presses. 20 | * @param title The Toolbar's title. 21 | */ 22 | void changedEditMode(boolean editMode, int selected, int total, @Nullable View.OnClickListener listener, @Nullable String title); 23 | 24 | /** 25 | * Propagate the selected item count to listeners. 26 | * 27 | * @param count The number of items selected. 28 | * @param total The total number of items. 29 | */ 30 | void onItemsSelected(int count, int total); 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/fragments/GifFragment.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.fragments; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.NonNull; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | import org.horaapps.leafpic.data.Media; 10 | 11 | import pl.droidsonroids.gif.GifImageView; 12 | 13 | /** 14 | * Media Fragment for showing an Image (static) 15 | */ 16 | public class GifFragment extends BaseMediaFragment { 17 | 18 | @NonNull 19 | public static GifFragment newInstance(@NonNull Media media) { 20 | return BaseMediaFragment.newInstance(new GifFragment(), media); 21 | } 22 | 23 | @Override 24 | public View onCreateView(@NonNull LayoutInflater inflater, 25 | ViewGroup container, 26 | Bundle savedInstanceState) { 27 | 28 | GifImageView photoView = new GifImageView(getContext()); 29 | photoView.setImageURI(media.getUri()); 30 | setTapListener(photoView); 31 | return photoView; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/fragments/IFragment.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.fragments; 2 | 3 | /** 4 | * Created by dnld on 3/24/17. 5 | */ 6 | 7 | public interface IFragment { 8 | 9 | boolean editMode(); 10 | boolean clearSelected(); 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/fragments/ImageFragment.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.fragments; 2 | 3 | import android.net.Uri; 4 | import android.os.Bundle; 5 | import android.support.annotation.NonNull; 6 | import android.support.annotation.Nullable; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | 11 | import com.davemorrissey.labs.subscaleview.ImageSource; 12 | import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView; 13 | 14 | import org.horaapps.leafpic.R; 15 | import org.horaapps.leafpic.data.Media; 16 | import org.horaapps.leafpic.util.BitmapUtils; 17 | 18 | import butterknife.BindView; 19 | import butterknife.ButterKnife; 20 | 21 | /** 22 | * A Media Fragment for showing an Image (static) 23 | */ 24 | public class ImageFragment extends BaseMediaFragment { 25 | 26 | @BindView(R.id.subsampling_view) SubsamplingScaleImageView imageView; 27 | 28 | @NonNull 29 | public static ImageFragment newInstance(@NonNull Media media) { 30 | return BaseMediaFragment.newInstance(new ImageFragment(), media); 31 | } 32 | 33 | @Override 34 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 35 | View rootView = inflater.inflate(R.layout.fragment_photo, container, false); 36 | ButterKnife.bind(this, rootView); 37 | return rootView; 38 | } 39 | 40 | @Override 41 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 42 | super.onViewCreated(view, savedInstanceState); 43 | Uri mediaUri = media.getUri(); 44 | imageView.setOrientation(BitmapUtils.getOrientation(mediaUri, getContext())); 45 | imageView.setImage(ImageSource.uri(mediaUri)); 46 | setTapListener(imageView); 47 | } 48 | 49 | @Override 50 | public void onDestroyView() { 51 | imageView.recycle(); 52 | super.onDestroyView(); 53 | } 54 | 55 | /** 56 | * Rotate the currently displaying media image. 57 | * 58 | * @param rotationInDegrees The rotation in degrees 59 | */ 60 | public void rotatePicture(int rotationInDegrees) { 61 | if (rotationInDegrees == -90 && imageView.getOrientation() == 0) imageView.setOrientation(SubsamplingScaleImageView.ORIENTATION_270); 62 | else imageView.setOrientation(Math.abs(imageView.getOrientation() + rotationInDegrees) % 360); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/fragments/NothingToShowListener.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.fragments; 2 | 3 | /** 4 | * Created by dnld on 12/16/17. 5 | */ 6 | 7 | public interface NothingToShowListener { 8 | 9 | void changedNothingToShow(boolean nothingToShow); 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/fragments/VideoFragment.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.fragments; 2 | 3 | import android.content.Intent; 4 | import android.net.Uri; 5 | import android.os.Bundle; 6 | import android.support.annotation.NonNull; 7 | import android.support.annotation.Nullable; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.widget.ImageView; 12 | 13 | import com.bumptech.glide.Glide; 14 | import com.bumptech.glide.load.engine.DiskCacheStrategy; 15 | import com.bumptech.glide.request.RequestOptions; 16 | 17 | import org.horaapps.leafpic.R; 18 | import org.horaapps.leafpic.data.Media; 19 | import org.horaapps.leafpic.data.StorageHelper; 20 | import org.horaapps.liz.ThemeHelper; 21 | import org.horaapps.liz.ui.ThemedIcon; 22 | 23 | import butterknife.BindView; 24 | import butterknife.ButterKnife; 25 | 26 | /** 27 | * A Media Fragment for showing a Video Preview. 28 | */ 29 | public class VideoFragment extends BaseMediaFragment { 30 | 31 | @BindView(R.id.media_view) ImageView previewView; 32 | @BindView(R.id.video_play_icon) ThemedIcon playVideoIcon; 33 | 34 | @NonNull 35 | public static VideoFragment newInstance(@NonNull Media media) { 36 | return BaseMediaFragment.newInstance(new VideoFragment(), media); 37 | } 38 | 39 | @Override 40 | public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 41 | View rootView = inflater.inflate(R.layout.fragment_video, container, false); 42 | ButterKnife.bind(this, rootView); 43 | return rootView; 44 | } 45 | 46 | @Override 47 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 48 | super.onViewCreated(view, savedInstanceState); 49 | 50 | playVideoIcon.setOnClickListener(v -> { 51 | Uri uri = StorageHelper.getUriForFile(getContext(), media.getFile()); 52 | Intent intent = new Intent(Intent.ACTION_VIEW).setDataAndType(uri, media.getMimeType()); 53 | intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 54 | startActivity(intent); 55 | }); 56 | 57 | // TODO: See where we can move this. Seems like boilerplate code that belongs in 58 | // a utility class or Builder of some sort. 59 | RequestOptions options = 60 | new RequestOptions().signature(media.getSignature()).centerCrop() 61 | .diskCacheStrategy( 62 | DiskCacheStrategy.AUTOMATIC); 63 | 64 | Glide.with(getContext()).load(media.getUri()).apply(options).into(previewView); 65 | setTapListener(previewView); 66 | } 67 | 68 | @Override 69 | public void refreshTheme(ThemeHelper themeHelper) { 70 | playVideoIcon.refreshTheme(themeHelper); 71 | } 72 | } -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/interfaces/MediaClickListener.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.interfaces; 2 | 3 | import org.horaapps.leafpic.data.Album; 4 | import org.horaapps.leafpic.data.Media; 5 | 6 | import java.util.ArrayList; 7 | 8 | public interface MediaClickListener { 9 | 10 | void onMediaClick(Album album, ArrayList media, int position); 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/items/ActionsListener.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.items; 2 | 3 | /** 4 | * Created by dnld on 10/03/18. 5 | */ 6 | 7 | /** 8 | * Interface for listeners to be alerted when this entity wants to 9 | * perform some actions on items. 10 | */ 11 | public interface ActionsListener { 12 | 13 | /** 14 | * Used when the user clicks on an item. 15 | * 16 | * @param position The position that was clicked. 17 | */ 18 | void onItemSelected(int position); 19 | 20 | /** 21 | * Use to toggle Select Mode states 22 | * 23 | * @param selectMode Whether we want to be in select mode or not. 24 | */ 25 | void onSelectMode(boolean selectMode); 26 | 27 | /** 28 | * Used to notify listeners about selection counts. 29 | * 30 | * @param selectionCount The number of selected items 31 | * @param totalCount The number of total items 32 | */ 33 | void onSelectionCountChanged(int selectionCount, int totalCount); 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/progress/ErrorCause.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.progress; 2 | 3 | import android.support.annotation.Nullable; 4 | 5 | import java.util.ArrayList; 6 | 7 | public class ErrorCause { 8 | 9 | private String title; 10 | private ArrayList causes; 11 | 12 | public ErrorCause(String title, ArrayList causes) { 13 | this.title = title; 14 | this.causes = causes; 15 | } 16 | 17 | public ErrorCause(String title) { 18 | this.title = title; 19 | this.causes = new ArrayList<>(1); 20 | } 21 | 22 | public void addCause(String cause) { 23 | this.causes.add(cause); 24 | } 25 | 26 | public String getTitle() { 27 | return title; 28 | } 29 | 30 | public boolean hasErrors() { 31 | return causes.size() > 0; 32 | } 33 | 34 | public @Nullable 35 | ErrorCause get() { 36 | if (hasErrors()) return this; 37 | else return null; 38 | } 39 | 40 | public ArrayList getCauses() { 41 | return causes; 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | StringBuilder b = new StringBuilder(); 47 | b.append(title).append("\n"); 48 | 49 | for (String cause : causes) { 50 | b.append(cause).append("\n"); 51 | } 52 | 53 | return b.toString(); 54 | } 55 | 56 | public static ErrorCause fromThrowable(Throwable throwable) { 57 | if (throwable instanceof ProgressException) 58 | return ((ProgressException) throwable).getError(); 59 | else return new ErrorCause(throwable.getMessage()); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/progress/ErrorCauseAdapter.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.progress; 2 | 3 | import android.content.Context; 4 | import android.support.annotation.NonNull; 5 | import android.view.LayoutInflater; 6 | import android.view.ViewGroup; 7 | 8 | import org.horaapps.leafpic.R; 9 | import org.horaapps.liz.ThemedAdapter; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | public class ErrorCauseAdapter extends ThemedAdapter { 15 | 16 | private List errors; 17 | 18 | public ErrorCauseAdapter(Context context, List errors) { 19 | super(context); 20 | this.errors = errors; 21 | } 22 | 23 | public void setErrors(ArrayList errors) { 24 | this.errors = errors; 25 | notifyDataSetChanged(); 26 | } 27 | 28 | @NonNull 29 | @Override 30 | public ErrorCauseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 31 | return new ErrorCauseViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_error_cause, parent, false)); 32 | } 33 | 34 | @Override 35 | public void onBindViewHolder(@NonNull ErrorCauseViewHolder holder, int position) { 36 | holder.load(errors.get(position)); 37 | super.onBindViewHolder(holder, position); 38 | } 39 | 40 | @Override 41 | public int getItemCount() { 42 | return errors.size(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/progress/ErrorCauseViewHolder.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.progress; 2 | 3 | import android.view.View; 4 | 5 | import org.horaapps.leafpic.R; 6 | import org.horaapps.liz.ThemedViewHolder; 7 | import org.horaapps.liz.ui.ThemedLinearLayout; 8 | import org.horaapps.liz.ui.ThemedTextView; 9 | 10 | import butterknife.BindView; 11 | import butterknife.ButterKnife; 12 | 13 | public class ErrorCauseViewHolder extends ThemedViewHolder { 14 | 15 | @BindView(R.id.error_title) 16 | ThemedTextView title; 17 | 18 | @BindView(R.id.error_causes) 19 | ThemedLinearLayout causes; 20 | 21 | ErrorCauseViewHolder(View itemView) { 22 | super(itemView); 23 | ButterKnife.bind(this, itemView); 24 | } 25 | 26 | public void load(ErrorCause errorCause) { 27 | title.setText(errorCause.getTitle()); 28 | 29 | causes.removeAllViews(); 30 | for (String c : errorCause.getCauses()) { 31 | ThemedTextView textView = new ThemedTextView(itemView.getContext()); 32 | textView.setStyleColor(ThemedTextView.SUB_TEXT_COLOR); 33 | //textView.setTextSize(); 34 | textView.setText(c); 35 | causes.addView(textView); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/progress/ProgressException.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.progress; 2 | 3 | public class ProgressException extends Exception { 4 | 5 | private ErrorCause error; 6 | 7 | public ProgressException(ErrorCause error) { 8 | this.error = error; 9 | } 10 | 11 | public ProgressException(String error) { 12 | this.error = new ErrorCause(error); 13 | } 14 | 15 | public ErrorCause getError() { 16 | return error; 17 | } 18 | 19 | @Override 20 | public String toString() { 21 | return error.toString(); 22 | } 23 | 24 | @Override 25 | public String getMessage() { 26 | return toString(); 27 | } 28 | 29 | @Override 30 | public String getLocalizedMessage() { 31 | return toString(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/settings/ThemedSetting.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.settings; 2 | 3 | import org.horaapps.liz.ThemedActivity; 4 | 5 | /** 6 | * Created by dnld on 12/9/16. 7 | */ 8 | 9 | class ThemedSetting { 10 | 11 | private ThemedActivity activity; 12 | 13 | ThemedSetting(ThemedActivity activity) { 14 | this.activity = activity; 15 | } 16 | 17 | public ThemedSetting() { 18 | } 19 | 20 | public ThemedActivity getActivity() { 21 | return activity; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/timeline/data/TimelineHeaderModel.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.timeline.data; 2 | 3 | import android.support.annotation.NonNull; 4 | import android.support.annotation.Nullable; 5 | 6 | import java.util.Calendar; 7 | import java.util.Date; 8 | import java.util.GregorianCalendar; 9 | 10 | /** 11 | * Model for showing the Timeline headers. 12 | */ 13 | public class TimelineHeaderModel implements TimelineItem { 14 | 15 | private Calendar calendar; 16 | private String headerText; 17 | 18 | public TimelineHeaderModel(@NonNull Date date) { 19 | this(date.getTime()); 20 | } 21 | 22 | public TimelineHeaderModel(long timeInMillis) { 23 | calendar = new GregorianCalendar(); 24 | calendar.setTimeInMillis(timeInMillis); 25 | } 26 | 27 | public TimelineHeaderModel(@NonNull Calendar calendar) { 28 | this.calendar = calendar; 29 | } 30 | 31 | public void setHeaderText(@NonNull String headerText) { 32 | this.headerText = headerText; 33 | } 34 | 35 | @NonNull 36 | public Calendar getDate() { 37 | return calendar; 38 | } 39 | 40 | @Nullable 41 | public String getHeaderText() { 42 | return headerText; 43 | } 44 | 45 | @Override 46 | public int getTimelineType() { 47 | return TYPE_HEADER; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/timeline/data/TimelineItem.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.timeline.data; 2 | 3 | import android.support.annotation.IntDef; 4 | 5 | /** 6 | * Interface to define that this item is capable of being displayed on timeline 7 | */ 8 | public interface TimelineItem { 9 | 10 | int TYPE_HEADER = 101; 11 | int TYPE_MEDIA = 102; 12 | 13 | @IntDef({TYPE_HEADER, TYPE_MEDIA}) 14 | @interface TimelineItemType {} 15 | 16 | @TimelineItemType 17 | int getTimelineType(); 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/util/AnimationUtils.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.util; 2 | 3 | import android.support.v4.view.ViewPager; 4 | import android.support.v7.widget.RecyclerView; 5 | 6 | import org.horaapps.leafpic.util.preferences.Prefs; 7 | 8 | /** 9 | * Created by dnld on 24/02/18. 10 | */ 11 | 12 | public class AnimationUtils { 13 | 14 | public static RecyclerView.ItemAnimator getItemAnimator(RecyclerView.ItemAnimator itemAnimator) { 15 | if(Prefs.animationsEnabled()) { 16 | return itemAnimator; 17 | } 18 | return null; 19 | } 20 | 21 | public static ViewPager.PageTransformer getPageTransformer(ViewPager.PageTransformer pageTransformer) { 22 | if(Prefs.animationsEnabled()) { 23 | return pageTransformer; 24 | } 25 | return null; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/util/ApplicationUtils.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.util; 2 | 3 | import android.content.Context; 4 | import android.support.annotation.NonNull; 5 | 6 | import org.horaapps.leafpic.BuildConfig; 7 | 8 | /** 9 | * Data class for holding Application-related data. 10 | */ 11 | public class ApplicationUtils { 12 | 13 | private static String PACKAGE_NAME; 14 | 15 | public static void init(@NonNull Context context) { 16 | PACKAGE_NAME = context.getPackageName(); 17 | } 18 | 19 | /** 20 | * Get the Application's package name specified in Manifest 21 | */ 22 | @NonNull 23 | public static String getPackageName() { 24 | return PACKAGE_NAME; 25 | } 26 | 27 | @NonNull 28 | public static String getAppVersion() { 29 | return BuildConfig.VERSION_NAME; 30 | } 31 | 32 | public static boolean isDebug() { 33 | return BuildConfig.DEBUG; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/util/ArrayUtils.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.util; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | /** 6 | * All kinds of Array helpers belong here 7 | */ 8 | public final class ArrayUtils { 9 | 10 | /** 11 | * Find the index of an element in an array. 12 | * Performs a linear search across the array. 13 | * 14 | * @param array The array to search 15 | * @param element The element to find 16 | * @return The position of element in array, else -1 if not found. 17 | */ 18 | public static int getIndex(@NonNull T[] array, @NonNull T element) { 19 | for (int pos = 0; pos < array.length; pos++) { 20 | if (array[pos].equals(element)) return pos; 21 | } 22 | return -1; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/util/BitmapUtils.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.util; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.Canvas; 6 | import android.graphics.Color; 7 | import android.net.Uri; 8 | import android.support.media.ExifInterface; 9 | 10 | import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView; 11 | 12 | import java.io.IOException; 13 | import java.io.InputStream; 14 | 15 | /** 16 | * Created by dnld on 3/25/17. 17 | */ 18 | 19 | public class BitmapUtils { 20 | 21 | public static Bitmap addWhiteBorder(Bitmap bmp, int borderSize) { 22 | Bitmap bmpWithBorder = Bitmap.createBitmap(bmp.getWidth() + borderSize * 2, bmp.getHeight() + borderSize * 2, bmp.getConfig()); 23 | Canvas canvas = new Canvas(bmpWithBorder); 24 | canvas.drawColor(Color.WHITE); 25 | canvas.drawBitmap(bmp, borderSize, borderSize, null); 26 | return bmpWithBorder; 27 | } 28 | 29 | public static Bitmap getCroppedBitmap(Bitmap srcBmp){ 30 | Bitmap dstBmp; 31 | if (srcBmp.getWidth() >= srcBmp.getHeight()){ 32 | dstBmp = Bitmap.createBitmap(srcBmp, 33 | srcBmp.getWidth()/2 - srcBmp.getHeight()/2, 0, 34 | srcBmp.getHeight(), srcBmp.getHeight() 35 | ); 36 | } else { 37 | dstBmp = Bitmap.createBitmap(srcBmp, 0, 38 | srcBmp.getHeight()/2 - srcBmp.getWidth()/2, 39 | srcBmp.getWidth(), srcBmp.getWidth() 40 | ); 41 | } 42 | return dstBmp; 43 | } 44 | 45 | public static int getOrientation(Uri uri, Context ctx){ 46 | 47 | try (InputStream in = ctx.getContentResolver().openInputStream(uri)) { 48 | if (in == null) { 49 | return 0; 50 | } 51 | ExifInterface exif = new ExifInterface(in); 52 | int orientation = exif.getAttributeInt( ExifInterface.TAG_ORIENTATION, 1 ); 53 | 54 | switch ( orientation ) { 55 | case ExifInterface.ORIENTATION_ROTATE_180: 56 | return SubsamplingScaleImageView.ORIENTATION_180; 57 | case ExifInterface.ORIENTATION_ROTATE_90: 58 | return SubsamplingScaleImageView.ORIENTATION_90; 59 | case ExifInterface.ORIENTATION_ROTATE_270: 60 | return SubsamplingScaleImageView.ORIENTATION_270; 61 | default: 62 | return SubsamplingScaleImageView.ORIENTATION_0; 63 | } 64 | } catch ( IOException e ) { 65 | return 0; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/util/ChromeCustomTabs.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.util; 2 | 3 | import android.content.ComponentName; 4 | import android.content.Context; 5 | import android.net.Uri; 6 | import android.support.annotation.ColorInt; 7 | import android.support.annotation.NonNull; 8 | import android.support.customtabs.CustomTabsClient; 9 | import android.support.customtabs.CustomTabsIntent; 10 | import android.support.customtabs.CustomTabsServiceConnection; 11 | 12 | import org.horaapps.liz.ThemeHelper; 13 | 14 | /** 15 | * A Chrome Custom Tabs wrapper to preload and show URLs in Chrome Custom Tabs. 16 | * Also provides a static method to launch a tab directly without warm up. 17 | */ 18 | public class ChromeCustomTabs { 19 | 20 | private CustomTabsServiceConnection serviceConnection; 21 | private CustomTabsIntent mCustomTabsIntent; 22 | 23 | @ColorInt private int toolbarColor; 24 | private Context context; 25 | 26 | public ChromeCustomTabs(@NonNull Context context) { 27 | this.context = context; 28 | toolbarColor = ThemeHelper.getInstance(context).getPrimaryColor(); 29 | initService(); 30 | } 31 | 32 | private void initService() { 33 | 34 | serviceConnection = new CustomTabsServiceConnection() { 35 | @Override 36 | public void onCustomTabsServiceConnected(ComponentName componentName, CustomTabsClient customTabsClient) { 37 | customTabsClient.warmup(0L); 38 | } 39 | 40 | @Override 41 | public void onServiceDisconnected(ComponentName name) { 42 | // NO-OP 43 | } 44 | }; 45 | 46 | // Bind the Chrome Custom Tabs service 47 | CustomTabsClient.bindCustomTabsService(context, ApplicationUtils.getPackageName(), serviceConnection); 48 | 49 | mCustomTabsIntent = new CustomTabsIntent.Builder() 50 | .setShowTitle(true) 51 | .setToolbarColor(toolbarColor) 52 | .build(); 53 | } 54 | 55 | public void launchUrl(String Url) { 56 | mCustomTabsIntent.launchUrl(context, Uri.parse(Url)); 57 | } 58 | 59 | /** 60 | * Allow the Chrome Custom Tabs service to disconnect and GC. 61 | */ 62 | public void destroy() { 63 | context.unbindService(serviceConnection); 64 | } 65 | 66 | /** 67 | * Launches a Chrome Custom Tab without warmup / service. 68 | * 69 | * @param context The context - used for launching an Activity. 70 | * @param url The URL to load. 71 | */ 72 | public static void launchUrl(@NonNull Context context, @NonNull String url) { 73 | CustomTabsIntent customTabsIntent = new CustomTabsIntent.Builder().build(); 74 | customTabsIntent.launchUrl(context, Uri.parse(url)); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/util/DeviceUtils.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.util; 2 | 3 | import android.content.res.Configuration; 4 | import android.content.res.Resources; 5 | import android.support.annotation.NonNull; 6 | 7 | /** 8 | * Utility class for accessing Android device-specific information 9 | */ 10 | public class DeviceUtils { 11 | 12 | /** 13 | * Returns the state of device being in Landscape orientation. 14 | */ 15 | public static boolean isLandscape(@NonNull Resources resources) { 16 | return resources.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; 17 | } 18 | 19 | /** 20 | * Returns the state of device being in Portrait orientation. 21 | */ 22 | public static boolean isPortrait(@NonNull Resources resources) { 23 | return resources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/util/FingerPrint.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.util; 2 | 3 | import android.app.KeyguardManager; 4 | import android.content.Context; 5 | import android.hardware.fingerprint.FingerprintManager; 6 | import android.os.Build; 7 | import android.support.annotation.RequiresApi; 8 | 9 | import org.horaapps.leafpic.R; 10 | 11 | import static android.content.Context.FINGERPRINT_SERVICE; 12 | import static android.content.Context.KEYGUARD_SERVICE; 13 | 14 | /** 15 | * Created by gilbert on 24/03/2017. 16 | */ 17 | 18 | public class FingerPrint { 19 | 20 | @RequiresApi(api = Build.VERSION_CODES.M) 21 | public static boolean checkFinger(Context ctx) { 22 | 23 | // Keyguard Manager 24 | KeyguardManager keyguardManager = (KeyguardManager) ctx.getSystemService(KEYGUARD_SERVICE); 25 | // Fingerprint Manager 26 | FingerprintManager fingerprintManager = (FingerprintManager) ctx.getSystemService(FINGERPRINT_SERVICE); 27 | 28 | try { 29 | // Check if the fingerprint sensor is present 30 | if (!fingerprintManager.isHardwareDetected()) { 31 | // Update the UI with a message 32 | StringUtils.showToast(ctx, ctx.getString(R.string.fp_not_supported)); 33 | return false; 34 | } 35 | 36 | if (!fingerprintManager.hasEnrolledFingerprints()) { 37 | StringUtils.showToast(ctx, ctx.getString(R.string.fp_not_configured)); 38 | return false; 39 | } 40 | 41 | if (!keyguardManager.isKeyguardSecure()) { 42 | StringUtils.showToast(ctx, ctx.getString(R.string.fp_not_enabled_sls)); 43 | return false; 44 | } 45 | } 46 | catch(SecurityException se) { 47 | se.printStackTrace(); 48 | } 49 | return true; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/util/GLideRotateTransformation.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.util; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.Matrix; 5 | import android.support.annotation.NonNull; 6 | 7 | import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; 8 | import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; 9 | 10 | import java.security.MessageDigest; 11 | 12 | /** 13 | * Created by dnld on 9/17/17. 14 | */ 15 | 16 | public class GLideRotateTransformation extends BitmapTransformation { 17 | 18 | private float rotateRotationAngle = 0f; 19 | 20 | public GLideRotateTransformation(float rotateRotationAngle) { 21 | this.rotateRotationAngle = rotateRotationAngle; 22 | } 23 | 24 | @Override 25 | protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) { 26 | Matrix matrix = new Matrix(); 27 | 28 | matrix.postRotate(rotateRotationAngle); 29 | 30 | return Bitmap.createBitmap(toTransform, 0, 0, toTransform.getWidth(), toTransform.getHeight(), matrix, true); 31 | } 32 | 33 | @Override 34 | public void updateDiskCacheKey(MessageDigest messageDigest) { 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/util/LegacyCompatFileProvider.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.util; 2 | 3 | import android.content.Context; 4 | import android.database.Cursor; 5 | import android.net.Uri; 6 | import android.support.v4.content.FileProvider; 7 | 8 | import com.commonsware.cwac.provider.LegacyCompatCursorWrapper; 9 | 10 | import java.io.File; 11 | 12 | /** 13 | * Created by dnld on 16/10/17. 14 | */ 15 | 16 | public class LegacyCompatFileProvider extends FileProvider { 17 | 18 | @Override 19 | public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { 20 | return(new LegacyCompatCursorWrapper(super.query(uri, projection, selection, selectionArgs, sortOrder))); 21 | } 22 | 23 | public static Uri getUri(Context context, File file) { 24 | return getUriForFile(context, ApplicationUtils.getPackageName() + ".provider", file); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/util/Measure.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.util; 2 | 3 | import android.content.Context; 4 | import android.content.res.Resources; 5 | import android.graphics.Point; 6 | import android.util.DisplayMetrics; 7 | import android.view.Display; 8 | import android.view.WindowManager; 9 | 10 | /** 11 | * Created by dnld on 11/03/16. 12 | */ 13 | public class Measure { 14 | 15 | public static final String TAG = "Measure"; 16 | 17 | public static int pxToDp(int px, Context c) { 18 | DisplayMetrics displayMetrics = c.getResources().getDisplayMetrics(); 19 | return Math.round(px * (displayMetrics.ydpi / DisplayMetrics.DENSITY_DEFAULT)); 20 | } 21 | public static float dpToPx(int dp, Context context) { 22 | return dp * (context.getResources().getDisplayMetrics().density); 23 | } 24 | 25 | public static int getStatusBarHeight(Resources r) { 26 | int resourceId = r.getIdentifier("status_bar_height", "dimen", "android"); 27 | if (resourceId > 0) 28 | return r.getDimensionPixelSize(resourceId); 29 | 30 | return 0; 31 | } 32 | 33 | public static int getNavBarHeight(Context ct){ 34 | return getNavigationBarSize(ct).y; 35 | } 36 | 37 | public static Point getNavigationBarSize(Context context) { 38 | Point appUsableSize = getAppUsableScreenSize(context); 39 | Point realScreenSize = getRealScreenSize(context); 40 | 41 | // navigation bar on the right 42 | if (appUsableSize.x < realScreenSize.x) { 43 | return new Point(realScreenSize.x - appUsableSize.x, appUsableSize.y); 44 | } 45 | 46 | // navigation bar at the bottom 47 | if (appUsableSize.y < realScreenSize.y) { 48 | return new Point(appUsableSize.x, realScreenSize.y - appUsableSize.y); 49 | } 50 | 51 | // navigation bar is not present 52 | return new Point(); 53 | } 54 | 55 | private static Point getAppUsableScreenSize(Context context) { 56 | WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 57 | Display display = windowManager.getDefaultDisplay(); 58 | Point size = new Point(); 59 | display.getSize(size); 60 | return size; 61 | } 62 | 63 | private static Point getRealScreenSize(Context context) { 64 | WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 65 | Display display = windowManager.getDefaultDisplay(); 66 | Point size = new Point(); 67 | display.getRealSize(size); 68 | return size; 69 | } 70 | 71 | public static int rotateBy(int current, int degrees) { 72 | // TODO: 21/08/16 a better way should exist 73 | /*int rotation = current + degrees; 74 | if (rotation > 359) rotation -=360; 75 | if (rotation < 0) rotation +=360;*/ 76 | return (current + degrees) % 360; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/util/MediaUtils.kt: -------------------------------------------------------------------------------- 1 | @file: JvmName("MediaUtils") 2 | 3 | package org.horaapps.leafpic.util 4 | 5 | import android.content.Context 6 | import android.content.Intent 7 | import android.net.Uri 8 | import android.support.v4.app.FragmentManager 9 | import android.widget.Toast 10 | import org.horaapps.leafpic.R 11 | import org.horaapps.leafpic.data.Media 12 | import org.horaapps.leafpic.data.MediaHelper 13 | import org.horaapps.leafpic.progress.ProgressBottomSheet 14 | import java.util.* 15 | 16 | /** 17 | * Share the given Media with an application. 18 | */ 19 | fun shareMedia(context: Context, mediaList: List) { 20 | val intent = Intent(Intent.ACTION_SEND_MULTIPLE) 21 | 22 | val types = HashMap() 23 | val files = ArrayList() 24 | 25 | for (f in mediaList) { 26 | val mimeType = MimeTypeUtils.getTypeMime(f.mimeType) 27 | var count = 0 28 | if (types.containsKey(mimeType)) { 29 | count = types[mimeType]!! 30 | } 31 | types[mimeType] = count 32 | files.add(LegacyCompatFileProvider.getUri(context, f.file)) 33 | } 34 | 35 | val fileTypes = types.keys 36 | if (fileTypes.size > 1) { 37 | Toast.makeText(context, R.string.waring_share_multiple_file_types, Toast.LENGTH_SHORT).show() 38 | } 39 | 40 | val max = -1 41 | var type: String? = null 42 | for (fileType in fileTypes) { 43 | val count = types[fileType]!! 44 | if (count > max) { 45 | type = fileType 46 | } 47 | } 48 | 49 | intent.type = type!! + "/*" 50 | 51 | intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, files) 52 | intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION 53 | context.startActivity(Intent.createChooser(intent, context.getString(R.string.send_to))) 54 | } 55 | 56 | fun deleteMedia(context: Context, mediaList: List, fragmentManager: FragmentManager, deleteListener: ProgressBottomSheet.Listener) { 57 | val sources = ArrayList>(mediaList.size) 58 | for (media in mediaList) 59 | sources.add(MediaHelper.deleteMedia(context.applicationContext, media)) 60 | 61 | val bottomSheet = ProgressBottomSheet.Builder(R.string.delete_bottom_sheet_title) 62 | .autoDismiss(false) 63 | .sources(sources) 64 | .listener(deleteListener) 65 | .build() 66 | 67 | bottomSheet.showNow(fragmentManager, null) 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/util/MimeTypeUtils.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.util; 2 | 3 | import android.webkit.MimeTypeMap; 4 | 5 | public class MimeTypeUtils { 6 | 7 | public static final String UNKNOWN_MIME_TYPE = "unknown/unknown"; 8 | 9 | public static String getMimeType(String path) { 10 | int index; 11 | if (path == null || (index = path.lastIndexOf('.')) == -1) 12 | return UNKNOWN_MIME_TYPE; 13 | 14 | String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(path.substring(index + 1).toLowerCase()); 15 | return mime != null ? mime : UNKNOWN_MIME_TYPE; 16 | } 17 | 18 | public static String getGenericMIME(String mime) { 19 | return mime.split("/")[0] + "/*"; 20 | } 21 | 22 | public static String getTypeMime(String mime) { 23 | return mime.split("/")[0]; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/util/PermissionUtils.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.util; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.content.Context; 6 | import android.content.pm.PackageManager; 7 | import android.support.v4.app.ActivityCompat; 8 | import android.support.v4.content.ContextCompat; 9 | import android.support.v7.app.AppCompatActivity; 10 | 11 | /** 12 | * Created by dnld on 01/04/16. 13 | */ 14 | public final class PermissionUtils { 15 | 16 | public static boolean checkPermissions(Context context, String... permissions) { 17 | for (String permission : permissions) { 18 | if (!checkPermission(context, permission)) { 19 | return false; 20 | } 21 | } 22 | return true; 23 | } 24 | 25 | private static boolean checkPermission(Context context, String permission) { 26 | return ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED; 27 | } 28 | 29 | public static boolean isStoragePermissionsGranted(Context context) { 30 | return checkPermissions(context, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE); 31 | } 32 | 33 | public static void requestPermissions(Object o, int permissionId, String... permissions) { 34 | if (o instanceof Activity) { 35 | ActivityCompat.requestPermissions((AppCompatActivity) o, permissions, permissionId); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/util/RecyclerItemClickListener.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.util; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.view.GestureDetector; 5 | import android.view.MotionEvent; 6 | import android.view.View; 7 | 8 | /** 9 | * Created by dnld on 3/24/17. 10 | */ 11 | 12 | public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener { 13 | 14 | public interface OnItemClickListener { 15 | void onItemClick(View view, int position); 16 | void onLongItemClick(View view, int position); 17 | } 18 | 19 | private OnItemClickListener mListener; 20 | private GestureDetector mGestureDetector; 21 | public RecyclerItemClickListener(final RecyclerView rv, OnItemClickListener listener) { 22 | mListener = listener; 23 | mGestureDetector = new GestureDetector(rv.getContext(), new GestureDetector.SimpleOnGestureListener() { 24 | @Override 25 | public boolean onSingleTapUp(MotionEvent e) { 26 | return true; 27 | } 28 | @Override 29 | public void onLongPress(MotionEvent e) { 30 | View child = rv.findChildViewUnder(e.getX(), e.getY()); 31 | if (child != null && mListener != null) { 32 | mListener.onLongItemClick(child, rv.getChildAdapterPosition(child)); 33 | } 34 | } 35 | }); 36 | } 37 | @Override public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) { 38 | View childView = view.findChildViewUnder(e.getX(), e.getY()); 39 | if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) { 40 | mListener.onItemClick(childView, view.getChildAdapterPosition(childView)); 41 | return true; 42 | } 43 | return false; 44 | } 45 | @Override public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { } 46 | @Override 47 | public void onRequestDisallowInterceptTouchEvent (boolean disallowIntercept){} 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/util/ServerConstants.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.util; 2 | 3 | /** 4 | * Storage for server constants - specifically Web URLs 5 | */ 6 | public class ServerConstants { 7 | 8 | // GitHub URLs 9 | public static final String GITHUB_LEAFPIC = "https://github.com/HoraApps/LeafPic"; 10 | public static final String GITHUB_DONALD = "https://github.com/DNLDsht"; 11 | public static final String GITHUB_CALVIN = "https://github.com/CalvinNor"; 12 | public static final String GITHUB_GILBERT = "https://github.com/Mow3l"; 13 | 14 | // LeafPic URLs 15 | public static final String LEAFPIC_ISSUES = "https://github.com/HoraApps/LeafPic/issues"; 16 | public static final String LEAFPIC_CROWDIN = "https://crowdin.com/project/leafpic"; 17 | public static final String LEAFPIC_LICENSE = "https://github.com/HoraApps/LeafPic/blob/master/LICENSE"; 18 | public static final String LEAFPIC_CHANGELOG = "https://github.com/HoraApps/LeafPic/blob/dev/CHANGELOG.md"; 19 | 20 | 21 | public static final String TWITTER_ABOUT_DONALD = "https://twitter.com/dnld_sht"; 22 | public static final String TWITTER_ABOUT_GILBERT = "https://twitter.com/GilbertNdr"; 23 | public static final String GOOGLE_ABOUT_CALVIN = "https://plus.google.com/+CalvinNoronha2394"; 24 | 25 | // Email IDs 26 | public static final String MAIL_DONALD = "dnld.sht@gmail.com"; 27 | public static final String MAIL_GILBERT = "jibo95@gmail.com"; 28 | public static final String MAIL_CALVIN = "calvin.nrnha@gmail.com"; 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/util/StaticMapProvider.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.util; 2 | 3 | import com.drew.lang.GeoLocation; 4 | 5 | import org.horaapps.leafpic.SecretConstants; 6 | 7 | import java.util.Locale; 8 | 9 | // needed for Local.US 10 | 11 | /** 12 | * Created by dnld on 04/09/16. 13 | */ 14 | 15 | public enum StaticMapProvider { 16 | 17 | GOOGLE_MAPS(0), MAP_BOX(1), MAP_BOX_DARK(2), MAP_BOX_LIGHT(3), TYLER(4); 18 | 19 | int value; 20 | 21 | StaticMapProvider(int value) { 22 | this.value = value; 23 | } 24 | 25 | public int getValue() { return value;} 26 | 27 | /** String.format is locale dependent, we need to force a locale with point instead of comma in decimals, 28 | * otherwise (at least) mapbox does not work in some countries */ 29 | public String getUrl(GeoLocation location) { 30 | if (value>=1 && value <=3) //MAP_MOX invert coordinates 31 | return String.format(Locale.US, getUrl(value), location.getLongitude(), location.getLatitude()); 32 | return String.format(Locale.US, getUrl(value), location.getLatitude(), location.getLongitude()); 33 | } 34 | 35 | private String getUrl(int value) { 36 | switch (value) { 37 | case 0: default: return ("http://maps.google.com/maps/api/staticmap?center=%f,%f&zoom=15&size=500x300&scale=2&sensor=false"); 38 | case 1: return "https://api.mapbox.com/v4/mapbox.streets/%f,%f,15/500x300.jpg?access_token=" + SecretConstants.MAP_BOX_TOKEN; 39 | case 2: return "https://api.mapbox.com/v4/mapbox.dark/%f,%f,15/500x300.jpg?access_token="+ SecretConstants.MAP_BOX_TOKEN; 40 | case 3: return "https://api.mapbox.com/v4/mapbox.light/%f,%f,15/500x300.jpg?access_token="+ SecretConstants.MAP_BOX_TOKEN; 41 | case 4: return "https://tyler-demo.herokuapp.com/?greyscale=false&lat=%f&lon=%f&zoom=15&width=500&height=300&tile_url=http://[abcd].tile.stamen.com/watercolor/{zoom}/{x}/{y}.jpg"; 42 | } 43 | } 44 | 45 | public static StaticMapProvider fromValue(int value) { 46 | switch (value) { 47 | case 0: default: return GOOGLE_MAPS; 48 | case 1: return MAP_BOX; 49 | case 2: return MAP_BOX_DARK; 50 | case 3: return MAP_BOX_LIGHT; 51 | case 4: return TYLER; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/util/inapppurchase/IabBroadcastReceiver.java: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package org.horaapps.leafpic.util.inapppurchase; 17 | 18 | import android.content.BroadcastReceiver; 19 | import android.content.Context; 20 | import android.content.Intent; 21 | 22 | /** 23 | * Receiver for the "com.android.vending.billing.PURCHASES_UPDATED" Action 24 | * from the Play Store. 25 | * 26 | *

It is possible that an in-app item may be acquired without the 27 | * application calling getBuyIntent(), for example if the item can be 28 | * redeemed from inside the Play Store using a promotional code. If this 29 | * application isn't running at the time, then when it is started a call 30 | * to getPurchases() will be sufficient notification. However, if the 31 | * application is already running in the background when the item is acquired, 32 | * a message to this BroadcastReceiver will indicate that the an item 33 | * has been acquired.

34 | */ 35 | public class IabBroadcastReceiver extends BroadcastReceiver { 36 | 37 | /** 38 | * Listener interface for received broadcast messages. 39 | */ 40 | public interface IabBroadcastListener { 41 | void receivedBroadcast(); 42 | } 43 | 44 | /** 45 | * The Intent action that this Receiver should filter for. 46 | */ 47 | public static final String ACTION = "com.android.vending.billing.PURCHASES_UPDATED"; 48 | 49 | private final IabBroadcastListener mListener; 50 | 51 | public IabBroadcastReceiver(IabBroadcastListener listener) { 52 | mListener = listener; 53 | } 54 | 55 | @Override 56 | public void onReceive(Context context, Intent intent) { 57 | if (mListener != null) { 58 | mListener.receivedBroadcast(); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/util/inapppurchase/IabException.java: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package org.horaapps.leafpic.util.inapppurchase; 17 | 18 | /** 19 | * Exception thrown when something went wrong with in-app billing. 20 | * An IabException has an associated IabResult (an onError). 21 | * To get the IAB result that caused this exception to be thrown, 22 | * call {@link #getResult()}. 23 | */ 24 | public class IabException extends Exception { 25 | 26 | IabResult mResult; 27 | 28 | public IabException(IabResult r) { 29 | this(r, null); 30 | } 31 | public IabException(int response, String message) { 32 | this(new IabResult(response, message)); 33 | } 34 | public IabException(IabResult r, Exception cause) { 35 | super(r.getMessage(), cause); 36 | mResult = r; 37 | } 38 | public IabException(int response, String message, Exception cause) { 39 | this(new IabResult(response, message), cause); 40 | } 41 | 42 | /** Returns the IAB result (onError) that this exception signals. */ 43 | public IabResult getResult() { return mResult; } 44 | } -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/util/inapppurchase/IabResult.java: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package org.horaapps.leafpic.util.inapppurchase; 17 | 18 | /** 19 | * Represents the result of an in-app billing operation. 20 | * A result is composed of a response code (an integer) and possibly a 21 | * message (String). You can get those by calling 22 | * {@link #getResponse} and {@link #getMessage()}, respectively. You 23 | * can also inquire whether a result is a onAuthenticated or a failure by 24 | * calling {@link #isSuccess()} and {@link #isFailure()}. 25 | */ 26 | public class IabResult { 27 | 28 | int mResponse; 29 | String mMessage; 30 | 31 | public IabResult(int response, String message) { 32 | mResponse = response; 33 | if (message == null || message.trim().length() == 0) { 34 | mMessage = IabHelper.getResponseDesc(response); 35 | } 36 | else { 37 | mMessage = message + " (response: " + IabHelper.getResponseDesc(response) + ")"; 38 | } 39 | } 40 | public int getResponse() { return mResponse; } 41 | public String getMessage() { return mMessage; } 42 | public boolean isSuccess() { return mResponse == IabHelper.BILLING_RESPONSE_RESULT_OK; } 43 | public boolean isFailure() { return !isSuccess(); } 44 | public String toString() { return "IabResult: " + getMessage(); } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/util/inapppurchase/Purchase.java: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package org.horaapps.leafpic.util.inapppurchase; 17 | 18 | import org.json.JSONException; 19 | import org.json.JSONObject; 20 | 21 | /** 22 | * Represents an in-app billing purchase. 23 | */ 24 | public class Purchase { 25 | 26 | String mItemType; // ITEM_TYPE_INAPP or ITEM_TYPE_SUBS 27 | String mOrderId; 28 | String mPackageName; 29 | String mSku; 30 | long mPurchaseTime; 31 | int mPurchaseState; 32 | String mDeveloperPayload; 33 | String mToken; 34 | String mOriginalJson; 35 | String mSignature; 36 | boolean mIsAutoRenewing; 37 | 38 | public Purchase(String itemType, String jsonPurchaseInfo, String signature) throws JSONException { 39 | mItemType = itemType; 40 | mOriginalJson = jsonPurchaseInfo; 41 | JSONObject o = new JSONObject(mOriginalJson); 42 | mOrderId = o.optString("orderId"); 43 | mPackageName = o.optString("packageName"); 44 | mSku = o.optString("productId"); 45 | mPurchaseTime = o.optLong("purchaseTime"); 46 | mPurchaseState = o.optInt("purchaseState"); 47 | mDeveloperPayload = o.optString("developerPayload"); 48 | mToken = o.optString("token", o.optString("purchaseToken")); 49 | mIsAutoRenewing = o.optBoolean("autoRenewing"); 50 | mSignature = signature; 51 | } 52 | 53 | public String getItemType() { return mItemType; } 54 | public String getOrderId() { return mOrderId; } 55 | public String getPackageName() { return mPackageName; } 56 | public String getSku() { return mSku; } 57 | public long getPurchaseTime() { return mPurchaseTime; } 58 | public int getPurchaseState() { return mPurchaseState; } 59 | public String getDeveloperPayload() { return mDeveloperPayload; } 60 | public String getToken() { return mToken; } 61 | public String getOriginalJson() { return mOriginalJson; } 62 | public String getSignature() { return mSignature; } 63 | public boolean isAutoRenewing() { return mIsAutoRenewing; } 64 | 65 | @Override 66 | public String toString() { return "PurchaseInfo(type:" + mItemType + "):" + mOriginalJson; } 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/util/inapppurchase/SkuDetails.java: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2012 Google Inc. 2 | * 3 | * Licensed under the Apache License, Version 2.0 (the "License"); 4 | * you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | package org.horaapps.leafpic.util.inapppurchase; 17 | 18 | import org.json.JSONException; 19 | import org.json.JSONObject; 20 | 21 | /** 22 | * Represents an in-app product's listing details. 23 | */ 24 | public class SkuDetails { 25 | 26 | private final String mItemType; 27 | private final String mSku; 28 | private final String mType; 29 | private final String mPrice; 30 | private final long mPriceAmountMicros; 31 | private final String mPriceCurrencyCode; 32 | private final String mTitle; 33 | private final String mDescription; 34 | private final String mJson; 35 | 36 | public SkuDetails(String jsonSkuDetails) throws JSONException { 37 | this(IabHelper.ITEM_TYPE_INAPP, jsonSkuDetails); 38 | } 39 | 40 | public SkuDetails(String itemType, String jsonSkuDetails) throws JSONException { 41 | mItemType = itemType; 42 | mJson = jsonSkuDetails; 43 | JSONObject o = new JSONObject(mJson); 44 | mSku = o.optString("productId"); 45 | mType = o.optString("type"); 46 | mPrice = o.optString("price"); 47 | mPriceAmountMicros = o.optLong("price_amount_micros"); 48 | mPriceCurrencyCode = o.optString("price_currency_code"); 49 | mTitle = o.optString("title"); 50 | mDescription = o.optString("description"); 51 | } 52 | 53 | public String getSku() { return mSku; } 54 | public String getType() { return mType; } 55 | public String getPrice() { return mPrice; } 56 | public long getPriceAmountMicros() { return mPriceAmountMicros; } 57 | public String getPriceCurrencyCode() { return mPriceCurrencyCode; } 58 | public String getTitle() { return mTitle; } 59 | public String getDescription() { return mDescription; } 60 | 61 | @Override 62 | public String toString() { 63 | return "SkuDetails:" + mJson; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/util/preferences/Defaults.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.util.preferences; 2 | 3 | import org.horaapps.leafpic.CardViewStyle; 4 | import org.horaapps.leafpic.data.sort.SortingMode; 5 | import org.horaapps.leafpic.data.sort.SortingOrder; 6 | 7 | /** 8 | * Class for storing Preference default values. 9 | */ 10 | public final class Defaults { 11 | 12 | // Prevent class instantiation 13 | private Defaults() {} 14 | 15 | public static final int FOLDER_COLUMNS_PORTRAIT = 2; 16 | public static final int FOLDER_COLUMNS_LANDSCAPE = 3; 17 | 18 | public static final int MEDIA_COLUMNS_PORTRAIT = 3; 19 | public static final int MEDIA_COLUMNS_LANDSCAPE = 4; 20 | 21 | public static final int TIMELINE_ITEMS_PORTRAIT = 4; 22 | public static final int TIMELINE_ITEMS_LANDSCAPE = 5; 23 | 24 | public static final int ALBUM_SORTING_MODE = SortingMode.DATE.getValue(); 25 | public static final int ALBUM_SORTING_ORDER = SortingOrder.DESCENDING.getValue(); 26 | public static final int CARD_STYLE = CardViewStyle.MATERIAL.getValue(); 27 | 28 | public static final boolean SHOW_VIDEOS = true; 29 | public static final boolean SHOW_MEDIA_COUNT = true; 30 | public static final boolean SHOW_ALBUM_PATH = false; 31 | 32 | public static final int LAST_VERSION_CODE = 0; 33 | public static final boolean SHOW_EASTER_EGG = false; 34 | 35 | public static final boolean ANIMATIONS_DISABLED = false; 36 | 37 | public static final boolean TIMELINE_ENABLED = false; 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/util/preferences/Keys.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.util.preferences; 2 | 3 | /** 4 | * Class for Preference keys. 5 | */ 6 | public final class Keys { 7 | 8 | // Prevent class instantiation 9 | private Keys() {} 10 | 11 | public static final String FOLDER_COLUMNS_PORTRAIT = "folder_columns_portrait"; 12 | public static final String FOLDER_COLUMNS_LANDSCAPE = "folder_columns_landscape"; 13 | 14 | public static final String MEDIA_COLUMNS_PORTRAIT = "media_columns_portrait"; 15 | public static final String MEDIA_COLUMNS_LANDSCAPE = "media_columns_landscape"; 16 | 17 | public static final String ALBUM_SORTING_MODE = "album_sorting_mode"; 18 | public static final String ALBUM_SORTING_ORDER = "album_sorting_order"; 19 | 20 | public static final String SHOW_VIDEOS = "show_videos"; 21 | public static final String SHOW_MEDIA_COUNT = "show_media_count"; 22 | public static final String SHOW_ALBUM_PATH = "show_album_path"; 23 | 24 | public static final String CARD_STYLE = "card_style"; 25 | 26 | public static final String LAST_VERSION_CODE = "last_version_code"; 27 | public static final String SHOW_EASTER_EGG = "show_easter_egg"; 28 | 29 | public static final String ANIMATIONS_DISABLED = "disable_animations"; 30 | 31 | // Feature flags 32 | public static final String TIMELINE_ENABLED = "enable_timeline"; 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/util/preferences/SharedPrefs.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.util.preferences; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.support.annotation.NonNull; 6 | 7 | /** 8 | * Android's SharedPreferences for storing key-value pairs. 9 | * DO NOT INSTANTIATE THIS CLASS - Use {@link Prefs} for your needs. 10 | */ 11 | /* package */ final class SharedPrefs { 12 | 13 | private static final String PREFERENCES_NAME = "org.horaapps.leafpic.SHARED_PREFS"; 14 | private static final int PREFERENCES_MODE = Context.MODE_PRIVATE; 15 | 16 | private final SharedPreferences sharedPrefs; 17 | 18 | /* package */ SharedPrefs(@NonNull Context context) { 19 | sharedPrefs = context.getApplicationContext() 20 | .getSharedPreferences(PREFERENCES_NAME, PREFERENCES_MODE); 21 | } 22 | 23 | @NonNull 24 | private SharedPreferences.Editor getEditor() { 25 | return sharedPrefs.edit(); 26 | } 27 | 28 | /* package */ int get(@NonNull String key, int defaultValue) { 29 | return sharedPrefs.getInt(key, defaultValue); 30 | } 31 | 32 | /* package */ void put(@NonNull String key, int value) { 33 | getEditor().putInt(key, value).commit(); 34 | } 35 | 36 | /* package */ boolean get(@NonNull String key, boolean defaultValue) { 37 | return sharedPrefs.getBoolean(key, defaultValue); 38 | } 39 | 40 | /* package */ void put(@NonNull String key, boolean value) { 41 | getEditor().putBoolean(key, value).commit(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/views/FabScrollBehaviour.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.views; 2 | 3 | import android.content.Context; 4 | import android.support.design.widget.CoordinatorLayout; 5 | import android.support.design.widget.FloatingActionButton; 6 | import android.support.v4.view.ViewCompat; 7 | import android.util.AttributeSet; 8 | import android.view.View; 9 | import android.view.animation.AccelerateInterpolator; 10 | import android.view.animation.DecelerateInterpolator; 11 | 12 | /** 13 | * Created by dnld on 06/03/16. 14 | */ 15 | public class FabScrollBehaviour extends FloatingActionButton.Behavior { 16 | 17 | public FabScrollBehaviour(Context context, AttributeSet attributeSet) { 18 | super(); 19 | } 20 | 21 | @Override 22 | public void onNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { 23 | super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed); 24 | if (dyConsumed > 0) 25 | child.animate().translationY(child.getHeight()*4).setInterpolator(new AccelerateInterpolator(2)).start(); 26 | else 27 | child.animate().translationY(/*-Measure.getNavigationBarSize(coordinatorLayout 28 | .getContext()).y*/0).setInterpolator(new DecelerateInterpolator(2)).start(); 29 | } 30 | 31 | @Override 32 | public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View directTargetChild, View target, int nestedScrollAxes) { 33 | return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/views/GridSpacingItemDecoration.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.views; 2 | 3 | import android.graphics.Rect; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.View; 6 | 7 | /** 8 | * Created by Jibo on 10/03/2016. 9 | */ 10 | public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration { 11 | 12 | private int spanCount; 13 | private int spacing; 14 | private boolean includeEdge; 15 | 16 | public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) { 17 | this.spanCount = spanCount; 18 | this.spacing = spacing; 19 | this.includeEdge = includeEdge; 20 | } 21 | 22 | @Override 23 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 24 | int position = parent.getChildAdapterPosition(view); // item position 25 | int column = position % spanCount; // item column 26 | 27 | if (includeEdge) { 28 | outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing) 29 | outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing) 30 | if (position < spanCount) { // top edge 31 | outRect.top = spacing; 32 | } 33 | outRect.bottom = spacing; // item bottom 34 | } else { 35 | outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing) 36 | outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing) 37 | if (position >= spanCount) { 38 | outRect.top = spacing; // item top 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/views/HackyViewPager.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.views; 2 | 3 | /** 4 | * Found at http://stackoverflow.com/questions/7814017/is-it-possible-to-disable-scrolling-on-a-viewpager. 5 | * Convenient way to temporarily disable ViewPager navigation while interacting with ImageView. 6 | *

7 | * Julia Zudikova 8 | */ 9 | 10 | import android.content.Context; 11 | import android.support.v4.view.ViewPager; 12 | import android.util.AttributeSet; 13 | import android.view.MotionEvent; 14 | 15 | /** 16 | * Hacky fix for Issue #4 and 17 | * http://code.google.com/p/android/issues/detail?id=18990 18 | *

19 | * ScaleGestureDetector seems to mess up the touch events, which means that 20 | * ViewGroups which make use of onInterceptTouchEvent throw a lot of 21 | * IllegalArgumentException: pointerIndex out of range. 22 | *

23 | * There's not much I can do in my code for now, but we can mask the result by 24 | * just catching the problem and ignoring it. 25 | * 26 | * @author Chris Banes 27 | */ 28 | public class HackyViewPager extends ViewPager { 29 | 30 | private boolean isLocked; 31 | 32 | public HackyViewPager(Context context) { 33 | super(context); 34 | isLocked = false; 35 | } 36 | 37 | public HackyViewPager(Context context, AttributeSet attrs) { 38 | super(context, attrs); 39 | isLocked = false; 40 | } 41 | 42 | @Override 43 | public boolean onInterceptTouchEvent(MotionEvent ev) { 44 | if (!isLocked) { 45 | try { 46 | return super.onInterceptTouchEvent(ev); 47 | } catch (IllegalArgumentException e) { 48 | e.printStackTrace(); 49 | return false; 50 | } 51 | } 52 | return false; 53 | } 54 | 55 | @Override 56 | public boolean onTouchEvent(MotionEvent event) { 57 | return !isLocked && super.onTouchEvent(event); 58 | } 59 | 60 | public void toggleLock() { 61 | isLocked = !isLocked; 62 | } 63 | 64 | public boolean isLocked() { 65 | return isLocked; 66 | } 67 | 68 | public void setLocked(boolean isLocked) { 69 | this.isLocked = isLocked; 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/views/SettingBasic.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.views; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.os.Build; 6 | import android.support.annotation.Nullable; 7 | import android.support.annotation.StringRes; 8 | import android.util.AttributeSet; 9 | import android.view.LayoutInflater; 10 | import android.widget.FrameLayout; 11 | import android.widget.TextView; 12 | 13 | import org.horaapps.leafpic.R; 14 | import org.horaapps.liz.ThemeHelper; 15 | import org.horaapps.liz.Themed; 16 | import org.horaapps.liz.ui.ThemedIcon; 17 | 18 | import butterknife.BindView; 19 | import butterknife.ButterKnife; 20 | 21 | /** 22 | * Created by darken (darken@darken.eu) on 04.03.2017. 23 | */ 24 | public class SettingBasic extends FrameLayout implements Themed { 25 | 26 | private final String iconString; 27 | @StringRes private final int titleRes; 28 | @StringRes private final int captionRes; 29 | @BindView(R.id.icon) 30 | ThemedIcon icon; 31 | @BindView(R.id.title) TextView title; 32 | @BindView(R.id.caption) TextView caption; 33 | 34 | public SettingBasic(Context context) { 35 | this(context, null); 36 | } 37 | 38 | public SettingBasic(Context context, @Nullable AttributeSet attrs) { 39 | this(context, attrs, 0); 40 | } 41 | 42 | public SettingBasic(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 43 | super(context, attrs, defStyleAttr); 44 | 45 | setBackgroundResource(R.drawable.ripple); 46 | 47 | LayoutInflater inflater = LayoutInflater.from(getContext()); 48 | inflater.inflate(R.layout.view_setting_basic, this); 49 | 50 | TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.SettingBasic); 51 | iconString = a.getString(R.styleable.SettingBasic_settingIcon); 52 | titleRes = a.getResourceId(R.styleable.SettingBasic_settingTitle, 0); 53 | captionRes = a.getResourceId(R.styleable.SettingBasic_settingCaption, 0); 54 | int minimumApi = a.getInteger(R.styleable.SettingBasic_settingMinApi, 0); 55 | a.recycle(); 56 | 57 | if (Build.VERSION.SDK_INT < minimumApi) setVisibility(GONE); 58 | } 59 | 60 | @Override 61 | protected void onFinishInflate() { 62 | ButterKnife.bind(this); 63 | 64 | icon.setIcon(icon.getIcon().icon(iconString)); 65 | title.setText(titleRes); 66 | caption.setText(captionRes); 67 | 68 | /* 69 | setPadding((int) getResources().getDimension(R.dimen.medium_spacing), 0, (int) getResources().getDimension(R.dimen.medium_spacing), 0); 70 | setMinimumHeight((int) getResources().getDimension(R.dimen.listitem_height_twoline)); 71 | */ 72 | super.onFinishInflate(); 73 | } 74 | 75 | @Override 76 | public void refreshTheme(ThemeHelper themeHelper) { 77 | 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/views/SquareImageView.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.views; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.ImageView; 6 | 7 | /** 8 | * Created by dnld on 09/05/16. 9 | */ 10 | public class SquareImageView extends ImageView { 11 | 12 | public SquareImageView(Context context) { 13 | super(context); 14 | } 15 | 16 | public SquareImageView(Context context, AttributeSet attrs) { 17 | super(context, attrs); 18 | } 19 | 20 | public SquareImageView(Context context, AttributeSet attrs, int defStyleAttr) { 21 | super(context, attrs, defStyleAttr); 22 | } 23 | 24 | @Override 25 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 26 | // Set a square layout. 27 | super.onMeasure(widthMeasureSpec, widthMeasureSpec); 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/views/SquareRelativeLayout.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.views; 2 | 3 | import android.annotation.TargetApi; 4 | import android.content.Context; 5 | import android.os.Build; 6 | import android.util.AttributeSet; 7 | import android.widget.RelativeLayout; 8 | 9 | /** 10 | * Created by dnld on 09/05/16. 11 | */ 12 | public class SquareRelativeLayout extends RelativeLayout { 13 | 14 | public SquareRelativeLayout(Context context) { 15 | super(context); 16 | } 17 | 18 | public SquareRelativeLayout(Context context, AttributeSet attrs) { 19 | super(context, attrs); 20 | } 21 | 22 | public SquareRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { 23 | super(context, attrs, defStyleAttr); 24 | } 25 | 26 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 27 | public SquareRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 28 | super(context, attrs, defStyleAttr, defStyleRes); 29 | } 30 | 31 | @Override 32 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 33 | // Set a square layout. 34 | super.onMeasure(widthMeasureSpec, widthMeasureSpec); 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/views/ZoomImageView.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.views; 2 | 3 | import android.animation.AnimatorSet; 4 | import android.view.GestureDetector; 5 | import android.view.VelocityTracker; 6 | import android.view.animation.DecelerateInterpolator; 7 | import android.widget.Scroller; 8 | 9 | /** 10 | * Created by dnld on 20/09/16. 11 | */ 12 | 13 | public class ZoomImageView { 14 | 15 | private boolean draggingDown = false; 16 | private float dragY; 17 | private float translationX; 18 | private float translationY; 19 | private float scale = 1; 20 | private float animateToX; 21 | private float animateToY; 22 | private float animateToScale; 23 | private float animationValue; 24 | private int currentRotation; 25 | private long animationStartTime; 26 | private AnimatorSet imageMoveAnimation; 27 | private AnimatorSet changeModeAnimation; 28 | private GestureDetector gestureDetector; 29 | private DecelerateInterpolator interpolator = new DecelerateInterpolator(1.5f); 30 | private float pinchStartDistance; 31 | private float pinchStartScale = 1; 32 | private float pinchCenterX; 33 | private float pinchCenterY; 34 | private float pinchStartX; 35 | private float pinchStartY; 36 | private float moveStartX; 37 | private float moveStartY; 38 | private float minX; 39 | private float maxX; 40 | private float minY; 41 | private float maxY; 42 | private boolean canZoom = true; 43 | private boolean changingPage = false; 44 | private boolean zooming = false; 45 | private boolean moving = false; 46 | private boolean doubleTap = false; 47 | private boolean invalidCoords = false; 48 | private boolean canDragDown = true; 49 | private boolean zoomAnimation = false; 50 | private boolean discardTap = false; 51 | private int switchImageAfterAnimation = 0; 52 | private VelocityTracker velocityTracker = null; 53 | private Scroller scroller = null; 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/views/themeable/ThemedCardView.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.views.themeable; 2 | 3 | import android.content.Context; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.widget.CardView; 6 | import android.util.AttributeSet; 7 | 8 | import org.horaapps.liz.ThemeHelper; 9 | import org.horaapps.liz.Themed; 10 | 11 | /** 12 | * Created by darken (darken@darken.eu) on 04.03.2017. 13 | */ 14 | public class ThemedCardView extends CardView implements Themed { 15 | 16 | public ThemedCardView(Context context) { 17 | this(context, null); 18 | } 19 | 20 | public ThemedCardView(Context context, @Nullable AttributeSet attrs) { 21 | this(context, attrs, 0); 22 | } 23 | 24 | public ThemedCardView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 25 | super(context, attrs, defStyleAttr); 26 | } 27 | 28 | @Override 29 | public void refreshTheme(ThemeHelper themeHelper) { 30 | setCardBackgroundColor(themeHelper.getCardBackgroundColor()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/views/themeable/ThemedSettingsCaption.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.views.themeable; 2 | 3 | import android.content.Context; 4 | import android.support.annotation.Nullable; 5 | import android.util.AttributeSet; 6 | 7 | import org.horaapps.liz.ThemeHelper; 8 | import org.horaapps.liz.Themed; 9 | 10 | /** 11 | * Created by darken (darken@darken.eu) on 04.03.2017. 12 | */ 13 | public class ThemedSettingsCaption extends android.support.v7.widget.AppCompatTextView implements Themed { 14 | 15 | public ThemedSettingsCaption(Context context) { 16 | this(context, null); 17 | } 18 | 19 | public ThemedSettingsCaption(Context context, @Nullable AttributeSet attrs) { 20 | this(context, attrs, 0); 21 | } 22 | 23 | public ThemedSettingsCaption(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 24 | super(context, attrs, defStyleAttr); 25 | } 26 | 27 | @Override 28 | public void refreshTheme(ThemeHelper themeHelper) { 29 | setTextColor(themeHelper.getSubTextColor()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/views/themeable/ThemedSettingsCategory.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.views.themeable; 2 | 3 | import android.content.Context; 4 | import android.support.annotation.Nullable; 5 | import android.util.AttributeSet; 6 | 7 | import org.horaapps.liz.ThemeHelper; 8 | import org.horaapps.liz.Themed; 9 | 10 | /** 11 | * Created by darken (darken@darken.eu) on 04.03.2017. 12 | */ 13 | public class ThemedSettingsCategory extends android.support.v7.widget.AppCompatTextView implements Themed { 14 | 15 | public ThemedSettingsCategory(Context context) { 16 | this(context, null); 17 | } 18 | 19 | public ThemedSettingsCategory(Context context, @Nullable AttributeSet attrs) { 20 | this(context, attrs, 0); 21 | } 22 | 23 | public ThemedSettingsCategory(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 24 | super(context, attrs, defStyleAttr); 25 | } 26 | 27 | @Override 28 | public void refreshTheme(ThemeHelper themeHelper) { 29 | themeHelper.setTextViewColor(this, themeHelper.getAccentColor()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/views/themeable/ThemedSettingsIcon.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.views.themeable; 2 | 3 | import android.content.Context; 4 | import android.support.annotation.Nullable; 5 | import android.util.AttributeSet; 6 | 7 | import org.horaapps.liz.ThemeHelper; 8 | import org.horaapps.liz.Themed; 9 | import org.horaapps.liz.ui.ThemedIcon; 10 | 11 | /** 12 | * Created by darken (darken@darken.eu) on 04.03.2017. 13 | */ 14 | public class ThemedSettingsIcon extends ThemedIcon implements Themed { 15 | 16 | public ThemedSettingsIcon(Context context) { 17 | this(context, null); 18 | } 19 | 20 | public ThemedSettingsIcon(Context context, @Nullable AttributeSet attrs) { 21 | this(context, attrs, 0); 22 | } 23 | 24 | public ThemedSettingsIcon(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 25 | super(context, attrs, defStyleAttr); 26 | } 27 | 28 | @Override 29 | public void refreshTheme(ThemeHelper themeHelper) { 30 | setColor(themeHelper.getIconColor()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/org/horaapps/leafpic/views/themeable/ThemedSettingsTitle.java: -------------------------------------------------------------------------------- 1 | package org.horaapps.leafpic.views.themeable; 2 | 3 | import android.content.Context; 4 | import android.support.annotation.Nullable; 5 | import android.util.AttributeSet; 6 | 7 | import org.horaapps.liz.ThemeHelper; 8 | import org.horaapps.liz.Themed; 9 | 10 | /** 11 | * Created by darken (darken@darken.eu) on 04.03.2017. 12 | */ 13 | public class ThemedSettingsTitle extends android.support.v7.widget.AppCompatTextView implements Themed { 14 | 15 | public ThemedSettingsTitle(Context context) { 16 | this(context, null); 17 | } 18 | 19 | public ThemedSettingsTitle(Context context, @Nullable AttributeSet attrs) { 20 | this(context, attrs, 0); 21 | } 22 | 23 | public ThemedSettingsTitle(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 24 | super(context, attrs, defStyleAttr); 25 | } 26 | 27 | @Override 28 | public void refreshTheme(ThemeHelper themeHelper) { 29 | setTextColor(themeHelper.getTextColor()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/res/anim/fade_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/anim/fade_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/anim/grid_layout_animation.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_bottom_in.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_down.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_fade_card.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_fade_photos.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/anim/zoom_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v19/ripple.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/ripple.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 16 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/calvin_header.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnevenSoftware/LeafPic/cfec78746776ed4bbccf9387bfc3f6ba1bb2c181/app/src/main/res/drawable/calvin_header.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/calvin_profile.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnevenSoftware/LeafPic/cfec78746776ed4bbccf9387bfc3f6ba1bb2c181/app/src/main/res/drawable/calvin_profile.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/donald_header.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnevenSoftware/LeafPic/cfec78746776ed4bbccf9387bfc3f6ba1bb2c181/app/src/main/res/drawable/donald_header.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/donald_profile.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnevenSoftware/LeafPic/cfec78746776ed4bbccf9387bfc3f6ba1bb2c181/app/src/main/res/drawable/donald_profile.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/gilbert_header.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnevenSoftware/LeafPic/cfec78746776ed4bbccf9387bfc3f6ba1bb2c181/app/src/main/res/drawable/gilbert_header.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/gilbert_profile.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnevenSoftware/LeafPic/cfec78746776ed4bbccf9387bfc3f6ba1bb2c181/app/src/main/res/drawable/gilbert_profile.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_close.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_delete.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_empty.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnevenSoftware/LeafPic/cfec78746776ed4bbccf9387bfc3f6ba1bb2c181/app/src/main/res/drawable/ic_empty.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_empty_amoled.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnevenSoftware/LeafPic/cfec78746776ed4bbccf9387bfc3f6ba1bb2c181/app/src/main/res/drawable/ic_empty_amoled.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_empty_white.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnevenSoftware/LeafPic/cfec78746776ed4bbccf9387bfc3f6ba1bb2c181/app/src/main/res/drawable/ic_empty_white.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_error.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnevenSoftware/LeafPic/cfec78746776ed4bbccf9387bfc3f6ba1bb2c181/app/src/main/res/drawable/ic_error.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_filter.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_gif_white_18dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnevenSoftware/LeafPic/cfec78746776ed4bbccf9387bfc3f6ba1bb2c181/app/src/main/res/drawable/ic_gif_white_18dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_group.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_holder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnevenSoftware/LeafPic/cfec78746776ed4bbccf9387bfc3f6ba1bb2c181/app/src/main/res/drawable/ic_holder.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_scrollbar.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnevenSoftware/LeafPic/cfec78746776ed4bbccf9387bfc3f6ba1bb2c181/app/src/main/res/drawable/ic_scrollbar.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_select_all.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_share.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/leaf_pic.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UnevenSoftware/LeafPic/cfec78746776ed4bbccf9387bfc3f6ba1bb2c181/app/src/main/res/drawable/leaf_pic.webp -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_affix.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 20 | 21 |