├── .editorconfig ├── .gitignore ├── CHANGELOG.md ├── FAQ.md ├── LICENSE ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── leafpicrevived ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ └── latest_changelog.md │ ├── ic_launcher-web.png │ ├── java │ └── com │ │ └── alienpants │ │ └── leafpicrevived │ │ ├── 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 │ │ └── SlidePageTransformer.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 │ │ └── 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 │ ├── alienpants_logo.png │ ├── calvin_header.jpg │ ├── calvin_profile.jpg │ ├── donald_header.jpg │ ├── donald_profile.jpg │ ├── gilbert_header.jpg │ ├── gilbert_profile.jpg │ ├── ic_close.xml │ ├── ic_delete.xml │ ├── ic_empty.png │ ├── ic_empty_amoled.png │ ├── ic_empty_white.png │ ├── ic_error.png │ ├── ic_filter.xml │ ├── ic_gif_white_18dp.png │ ├── ic_group.xml │ ├── ic_holder.png │ ├── ic_launcher_background.xml │ ├── ic_scrollbar.png │ ├── ic_select_all.xml │ ├── ic_share.xml │ ├── leaf_pic.png │ ├── leaf_pic_featureimage.png │ ├── line.xml │ ├── line_drawable.xml │ ├── thumb.xml │ ├── thumb_drawable.xml │ └── tom_profile.jpg │ ├── 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_album_small.xml │ ├── card_photo.xml │ ├── card_track_folder.xml │ ├── delete_folder_dialog_item.xml │ ├── dialog_advanced_sharing.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-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.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 │ ├── ic_launcher_background.xml │ ├── icons.xml │ ├── preferences-key.xml │ ├── shity_strings.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ └── provider_paths.xml ├── screenshots ├── 1.png ├── 2.png ├── 3.png ├── 4.png └── 5.png ├── scripts └── crowdin.sh ├── settings.gradle └── shiftcolorpicker ├── LICENSE.md ├── build.gradle └── src └── main ├── AndroidManifest.xml ├── java └── uz │ └── shift │ └── colorpicker │ ├── LineColorPicker.java │ ├── OnColorChangedListener.java │ └── Palette.java └── res └── values └── attrs.xml /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | *.aab 5 | 6 | # Files for the ART/Dalvik VM 7 | *.dex 8 | 9 | # Java class files 10 | *.class 11 | 12 | # Generated files 13 | bin/ 14 | gen/ 15 | out/ 16 | 17 | # Gradle files 18 | .gradle/ 19 | build/ 20 | 21 | # Local configuration file (sdk path, etc) 22 | local.properties 23 | 24 | # Proguard folder generated by Eclipse 25 | proguard/ 26 | 27 | # Log Files 28 | *.log 29 | 30 | # Android Studio Navigation editor temp files 31 | .navigation/ 32 | 33 | # Android Studio captures folder 34 | captures/ 35 | 36 | # IntelliJ 37 | *.iml 38 | .idea 39 | .idea/workspace.xml 40 | .idea/tasks.xml 41 | .idea/gradle.xml 42 | .idea/assetWizardSettings.xml 43 | .idea/dictionaries 44 | .idea/libraries 45 | .idea/caches 46 | 47 | # Keystore files 48 | # Uncomment the following line if you do not want to check your keystore files in. 49 | *.jks 50 | keystore.properties 51 | private_key.pepk 52 | 53 | # External native build folder generated in Android Studio 2.2 and later 54 | .externalNativeBuild 55 | 56 | # Google Services (e.g. APIs or Firebase) 57 | google-services.json 58 | 59 | # Freeline 60 | freeline.py 61 | freeline/ 62 | freeline_project_description.json 63 | 64 | # fastlane 65 | fastlane/report.xml 66 | fastlane/Preview.html 67 | fastlane/screenshots 68 | fastlane/test_output 69 | fastlane/readme.md 70 | 71 | .DS_Store 72 | /build 73 | /captures 74 | /scripts/crowdin.key 75 | /app/src/main/assets/secretconstants.properties 76 | 77 | # debug purpose 78 | inappstoragereader 79 | -------------------------------------------------------------------------------- /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 | # Updated 20220829 2 | ## This repository is currently archived 3 | 4 | No further work or updated are currently planned. Feel free to fork and make your own releases 5 | 6 | 7 | 8 | 9 | 10 | # LeafPic Revived 11 | 12 | LeafPic Revived 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.
13 | 14 |
15 | 16 | Get it on Google Play 19 | 20 |
21 |
22 | 23 | Get it on F-Droid 25 | 26 | (Not regularly updated) 27 |
28 |

29 |

30 |

31 | 32 | ## Screenshots 33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 |
41 | 42 | #### Contributing 43 | 44 | ###### Supporting Maintenance 45 | If you'd like to support the ongoing maintenance and development of this project, please consider contributing: 46 | 47 | Donate using Liberapay 48 | 49 | ###### Code 50 | If you are a developer and you wish to contribute to the app please fork the project 51 | and submit a pull request on the [dev branch](https://github.com/apcro/leafpicrevived/development). 52 | 53 | ###### Issues 54 | You can trace the status of known issues [here](https://github.com/apcro/leafpicrevived/issues). 55 | 56 | ###### Translations 57 | **NOTE** Existing translations are included as per the last commit to the GitLab project. New translations are not currently available. 58 | 59 | #### Licensing 60 | LeafPic is licensed under the [GNU v3 Public License](https://raw.githubusercontent.com/apcro/leafpicrevived/master/LICENSE). 61 | 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. 62 | 63 | #### NOTE 64 | The original LeafPic is abandoned: https://gitlab.com/HoraApps/LeafPic/issues/605#note_155379129 65 | This is a copy of the GitLab source code, updated to AndroidX, with some elements removed. 66 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext.kotlin_version = '1.3.72' 5 | 6 | repositories { 7 | jcenter() 8 | google() 9 | } 10 | dependencies { 11 | 12 | classpath 'com.android.tools.build:gradle:3.6.3' 13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 14 | 15 | // NOTE: Do not place your application dependencies here; they belong 16 | // in the individual module build.gradle files 17 | } 18 | } 19 | 20 | project.ext { 21 | supportVersion = "28.0.0" 22 | sdkVersion = 28 23 | } 24 | 25 | allprojects { 26 | repositories { 27 | jcenter() 28 | google() 29 | } 30 | } 31 | 32 | task clean(type: Delete) { 33 | delete rootProject.buildDir 34 | } 35 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | ## Project-wide Gradle settings. 2 | # 3 | # For more details on how to configure your build environment visit 4 | # http://www.gradle.org/docs/current/userguide/build_environment.html 5 | # 6 | # Specifies the JVM arguments used for the daemon process. 7 | # The setting is particularly useful for tweaking memory settings. 8 | # Default value: -Xmx1024m -XX:MaxPermSize=256m 9 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 10 | # 11 | # When configured, Gradle will run in incubating parallel mode. 12 | # This option should only be used with decoupled projects. More details, visit 13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 14 | # org.gradle.parallel=true 15 | #Sat Mar 25 09:54:58 CET 2017 16 | android.useAndroidX=true 17 | android.enableJetifier=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apcro/leafpicrevived/b5b3a0f82da11eddbc7edb12d3acc4987a950c9b/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Mar 01 10:39:49 GMT 2020 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip -------------------------------------------------------------------------------- /leafpicrevived/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /src/main/gen/ 3 | -------------------------------------------------------------------------------- /leafpicrevived/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 | -keep class uz.shift.colorpicker.** { *; } 22 | 23 | -keep class .R 24 | -keep class **.R$* { 25 | ; 26 | } 27 | 28 | -keepclasseswithmembernames class * { 29 | @butterknife.* ; 30 | } 31 | 32 | -keepclasseswithmembernames class * { 33 | @butterknife.* ; 34 | } 35 | 36 | -keepnames class * { @butterknife.Bind *;} 37 | 38 | -dontwarn okio.** -------------------------------------------------------------------------------- /leafpicrevived/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apcro/leafpicrevived/b5b3a0f82da11eddbc7edb12d3acc4987a950c9b/leafpicrevived/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/App.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived; 2 | 3 | import androidx.multidex.MultiDexApplication; 4 | 5 | import com.alienpants.leafpicrevived.util.ApplicationUtils; 6 | import com.alienpants.leafpicrevived.util.preferences.Prefs; 7 | import com.orhanobut.hawk.Hawk; 8 | 9 | /** 10 | * Created by dnld on 28/04/16. 11 | */ 12 | public class App extends MultiDexApplication { 13 | 14 | private static App mInstance; 15 | 16 | @Override 17 | public void onCreate() { 18 | super.onCreate(); 19 | mInstance = this; 20 | 21 | ApplicationUtils.init(this); 22 | 23 | initialiseStorage(); 24 | } 25 | 26 | public static App getInstance() { 27 | return mInstance; 28 | } 29 | 30 | private void initialiseStorage() { 31 | Prefs.init(this); 32 | Hawk.init(this).build(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/CardViewStyle.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived; 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 | SMALL(3, R.layout.card_album_small); 13 | 14 | private static final int size = CardViewStyle.values().length; 15 | 16 | int value; 17 | int layout; 18 | 19 | CardViewStyle(int value, int layout) { 20 | this.value = value; 21 | this.layout = layout; 22 | } 23 | 24 | public int getLayout() { 25 | return layout; 26 | } 27 | 28 | public int getValue() { 29 | return value; 30 | } 31 | 32 | public static int getSize() { 33 | return size; 34 | } 35 | 36 | public static CardViewStyle fromValue(int value) { 37 | switch (value) { 38 | case 0: 39 | default: 40 | return MATERIAL; 41 | case 1: 42 | return FLAT; 43 | case 2: 44 | return COMPACT; 45 | case 3: 46 | return SMALL; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/CustomGlideModule.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived; 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 | import com.bumptech.glide.module.GlideModule; 9 | 10 | /** 11 | * Created by dnld on 10/03/16. 12 | */ 13 | 14 | public class CustomGlideModule implements GlideModule { 15 | 16 | @Override 17 | public void registerComponents(Context context, Glide glide, Registry registry) { 18 | 19 | } 20 | 21 | @Override 22 | public void applyOptions(Context context, GlideBuilder builder) { 23 | // Apply options to the builder here. 24 | /*builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888); 25 | 26 | MemorySizeCalculator calculator = new MemorySizeCalculator(context); 27 | int defaultMemoryCacheSize = calculator.getMemoryCacheSize(); 28 | int defaultBitmapPoolSize = calculator.getBitmapPoolSize(); 29 | 30 | int customMemoryCacheSize = (int) (1.2 * defaultMemoryCacheSize); 31 | int customBitmapPoolSize = (int) (1.2 * defaultBitmapPoolSize); 32 | 33 | builder.setMemoryCache(new LruResourceCache(customMemoryCacheSize)); 34 | builder.setBitmapPool(new LruBitmapPool(customBitmapPoolSize)); 35 | 36 | int cacheSize100MegaBytes = 104857600; 37 | 38 | builder.setDiskCache( 39 | new InternalCacheDiskCacheFactory(context, cacheSize100MegaBytes) 40 | );*/ 41 | } 42 | } -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/SecretConstants.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived; 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 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/about/Contact.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.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 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/about/ContactButton.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.about; 2 | 3 | import android.content.Context; 4 | import android.graphics.Typeface; 5 | import android.view.ViewGroup; 6 | import android.widget.LinearLayout; 7 | 8 | import androidx.annotation.NonNull; 9 | 10 | import com.alienpants.leafpicrevived.R; 11 | 12 | import org.horaapps.liz.ThemeHelper; 13 | import org.horaapps.liz.ui.ThemedTextView; 14 | 15 | /** 16 | * Created by dnld on 04/03/18. 17 | */ 18 | 19 | public class ContactButton extends ThemedTextView { 20 | 21 | public ContactButton(Context context) { 22 | super(context); 23 | setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); 24 | int padd = (int) context.getResources().getDimension(R.dimen.developer_links_small_padding); 25 | setPadding(padd, padd, padd, padd); 26 | setTextSize(16); 27 | } 28 | 29 | public void bold() { 30 | setTypeface(null, Typeface.BOLD); 31 | } 32 | 33 | @Override 34 | public void refreshTheme(ThemeHelper theme) { 35 | setTextColor(theme.getAccentColor()); 36 | } 37 | 38 | public void setText(@NonNull String text) { 39 | super.setText(text.toUpperCase()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/about/ContactListener.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.about; 2 | 3 | /** 4 | * Created by dnld on 04/03/18. 5 | */ 6 | 7 | public interface ContactListener { 8 | void onContactClicked(Contact contact); 9 | 10 | void onMailClicked(String mail); 11 | } 12 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/about/Contributor.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.about; 2 | 3 | 4 | import androidx.annotation.DrawableRes; 5 | 6 | import java.util.ArrayList; 7 | 8 | /** 9 | * Created by dnld on 04/03/18. 10 | */ 11 | 12 | public class Contributor { 13 | private String name; 14 | private String description; 15 | private String email; 16 | private @DrawableRes 17 | int profileImage; 18 | 19 | private ArrayList contacts; 20 | 21 | public Contributor(String name, String description, @DrawableRes int profileImage) { 22 | this.name = name; 23 | this.description = description; 24 | this.profileImage = profileImage; 25 | this.contacts = new ArrayList<>(); 26 | } 27 | 28 | public void setEmail(String email) { 29 | this.email = email; 30 | } 31 | 32 | public void addSocial(String label, String url) { 33 | Contact c = new Contact(url, label); 34 | contacts.add(c); 35 | } 36 | 37 | public String getEmail() { 38 | return email; 39 | } 40 | 41 | public String getName() { 42 | return name; 43 | } 44 | 45 | public String getDescription() { 46 | return description; 47 | } 48 | 49 | public int getProfileImage() { 50 | return profileImage; 51 | } 52 | 53 | public ArrayList getContacts() { 54 | return contacts; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/about/ContributorsAdapter.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.about; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.ViewGroup; 6 | 7 | import com.alienpants.leafpicrevived.R; 8 | 9 | import org.horaapps.liz.ThemedAdapter; 10 | 11 | import java.util.ArrayList; 12 | 13 | /** 14 | * Created by dnld on 04/03/18. 15 | */ 16 | 17 | public class ContributorsAdapter extends ThemedAdapter { 18 | 19 | private ContactListener listener; 20 | private ArrayList contributors; 21 | 22 | ContributorsAdapter(Context context, ArrayList contributors, ContactListener listener) { 23 | super(context); 24 | this.contributors = contributors; 25 | this.listener = listener; 26 | } 27 | 28 | @Override 29 | public ContributorViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 30 | return new ContributorViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.view_about_contributor, parent, false)); 31 | } 32 | 33 | @Override 34 | public void onBindViewHolder(ContributorViewHolder holder, int position) { 35 | Contributor contributor = contributors.get(position); 36 | holder.load(contributor, listener); 37 | super.onBindViewHolder(holder, position); 38 | } 39 | 40 | @Override 41 | public int getItemCount() { 42 | return contributors.size(); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/activities/base/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.activities.base; 2 | 3 | import android.content.res.Configuration; 4 | import android.os.Build; 5 | import android.os.Bundle; 6 | 7 | import androidx.annotation.CallSuper; 8 | 9 | import com.alienpants.leafpicrevived.util.preferences.Prefs; 10 | 11 | import org.horaapps.liz.ThemedActivity; 12 | 13 | import java.util.Locale; 14 | 15 | import io.reactivex.disposables.CompositeDisposable; 16 | import io.reactivex.disposables.Disposable; 17 | 18 | public abstract class BaseActivity extends ThemedActivity { 19 | CompositeDisposable disposables = new CompositeDisposable(); 20 | 21 | 22 | public void disposeLater(Disposable disposable) { 23 | disposables.add(disposable); 24 | } 25 | 26 | @Override 27 | public void onCreate(Bundle savedInstanceState) { 28 | if (Prefs.forceEnglish()) forceEnglish(); 29 | super.onCreate(savedInstanceState); 30 | } 31 | 32 | public void forceEnglish() { 33 | changeLocale(new Locale("en")); 34 | } 35 | 36 | private void changeLocale(Locale locale) { 37 | Configuration configuration = getResources().getConfiguration(); 38 | Locale currentLocale; 39 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) 40 | currentLocale = configuration.getLocales().get(0); 41 | else currentLocale = configuration.locale; 42 | 43 | if (!currentLocale.getLanguage().equals(locale.getLanguage())) { 44 | Configuration config = new Configuration(); 45 | config.locale = locale; 46 | getBaseContext().getResources().updateConfiguration(config, null); 47 | } 48 | } 49 | 50 | @CallSuper 51 | @Override 52 | protected void onDestroy() { 53 | disposables.dispose(); 54 | super.onDestroy(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/activities/base/SharedMediaActivity.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.activities.base; 2 | 3 | import android.content.DialogInterface; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.widget.Toast; 7 | 8 | import androidx.appcompat.app.AlertDialog; 9 | 10 | import com.alienpants.leafpicrevived.R; 11 | import com.alienpants.leafpicrevived.data.StorageHelper; 12 | import com.alienpants.leafpicrevived.util.AlertDialogsHelper; 13 | 14 | /** 15 | * Created by dnld on 03/08/16. 16 | */ 17 | 18 | public abstract class SharedMediaActivity extends BaseActivity { 19 | 20 | private int REQUEST_CODE_SD_CARD_PERMISSIONS = 42; 21 | 22 | public void requestSdCardPermissions() { 23 | AlertDialog textDialog = AlertDialogsHelper.getTextDialog(this, R.string.sd_card_write_permission_title, R.string.sd_card_permissions_message); 24 | textDialog.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.ok_action).toUpperCase(), new DialogInterface.OnClickListener() { 25 | @Override 26 | public void onClick(DialogInterface dialogInterface, int i) { 27 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) 28 | startActivityForResult(new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE), REQUEST_CODE_SD_CARD_PERMISSIONS); 29 | } 30 | }); 31 | textDialog.show(); 32 | } 33 | 34 | @Override 35 | public void onActivityResult(final int requestCode, final int resultCode, final Intent resultData) { 36 | if (resultCode == RESULT_OK) { 37 | if (requestCode == REQUEST_CODE_SD_CARD_PERMISSIONS) { 38 | Uri treeUri = resultData.getData(); 39 | // Persist URI in shared preference so that you can use it later. 40 | StorageHelper.saveSdCardInfo(getApplicationContext(), treeUri); 41 | getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 42 | Toast.makeText(this, R.string.got_permission_wr_sdcard, Toast.LENGTH_SHORT).show(); 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/adapters/MediaPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.adapters; 2 | 3 | 4 | import android.util.SparseArray; 5 | import android.view.ViewGroup; 6 | 7 | import androidx.annotation.NonNull; 8 | import androidx.fragment.app.Fragment; 9 | import androidx.fragment.app.FragmentManager; 10 | import androidx.fragment.app.FragmentStatePagerAdapter; 11 | import androidx.viewpager.widget.PagerAdapter; 12 | 13 | import com.alienpants.leafpicrevived.data.Media; 14 | import com.alienpants.leafpicrevived.fragments.GifFragment; 15 | import com.alienpants.leafpicrevived.fragments.ImageFragment; 16 | import com.alienpants.leafpicrevived.fragments.VideoFragment; 17 | 18 | import java.util.ArrayList; 19 | 20 | /** 21 | * Created by dnld on 18/02/16. 22 | */ 23 | 24 | public class MediaPagerAdapter extends FragmentStatePagerAdapter { 25 | 26 | private final String TAG = "asd"; 27 | private ArrayList media; 28 | private SparseArray registeredFragments = new SparseArray<>(); 29 | 30 | public MediaPagerAdapter(FragmentManager fm, ArrayList media) { 31 | super(fm); 32 | this.media = media; 33 | } 34 | 35 | @Override 36 | public Fragment getItem(int pos) { 37 | Media media = this.media.get(pos); 38 | if (media.isVideo()) return VideoFragment.newInstance(media); 39 | if (media.isGif()) return GifFragment.newInstance(media); 40 | else return ImageFragment.newInstance(media); 41 | } 42 | 43 | @NonNull 44 | @Override 45 | public Object instantiateItem(ViewGroup container, int position) { 46 | Fragment fragment = (Fragment) super.instantiateItem(container, position); 47 | registeredFragments.put(position, fragment); 48 | return fragment; 49 | } 50 | 51 | @Override 52 | public void destroyItem(ViewGroup container, int position, Object object) { 53 | registeredFragments.remove(position); 54 | super.destroyItem(container, position, object); 55 | } 56 | 57 | public Fragment getRegisteredFragment(int position) { 58 | return registeredFragments.get(position); 59 | } 60 | 61 | public void swapDataSet(ArrayList media) { 62 | this.media = media; 63 | notifyDataSetChanged(); 64 | } 65 | 66 | @Override 67 | public int getItemPosition(@NonNull Object object) { 68 | return PagerAdapter.POSITION_NONE; 69 | } 70 | 71 | @Override 72 | public int getCount() { 73 | return media.size(); 74 | } 75 | } -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/animations/DepthPageTransformer.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.animations; 2 | 3 | import android.view.View; 4 | 5 | import androidx.viewpager.widget.ViewPager; 6 | 7 | /** 8 | * Created by dnld on 1/18/16. 9 | */ 10 | public class DepthPageTransformer implements ViewPager.PageTransformer { 11 | private static final float MIN_SCALE = 0.75f; 12 | 13 | public void transformPage(View view, float position) { 14 | int pageWidth = view.getWidth(); 15 | 16 | if (position < -1) { // [-Infinity,-1) 17 | // This page is way off-screen to the left. 18 | view.setAlpha(0); 19 | 20 | } else if (position <= 0) { // [-1,0] 21 | // Use the default slide transition when moving to the left page 22 | view.setAlpha(1); 23 | view.setTranslationX(0); 24 | view.setScaleX(1); 25 | view.setScaleY(1); 26 | 27 | } else if (position <= 1) { // (0,1] 28 | // Fade the page out. 29 | view.setAlpha(1 - position); 30 | 31 | // Counteract the default slide transition 32 | view.setTranslationX(pageWidth * -position); 33 | 34 | // Scale the page down (between MIN_SCALE and 1) 35 | float scaleFactor = MIN_SCALE 36 | + (1 - MIN_SCALE) * (1 - Math.abs(position)); 37 | view.setScaleX(scaleFactor); 38 | view.setScaleY(scaleFactor); 39 | 40 | } else { // (1,+Infinity] 41 | // This page is way off-screen to the right. 42 | view.setAlpha(0); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/animations/SlidePageTransformer.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.animations; 2 | 3 | import android.view.View; 4 | 5 | import androidx.viewpager.widget.ViewPager; 6 | 7 | /** 8 | * Created by dnld on 1/18/16. 9 | */ 10 | public class SlidePageTransformer implements ViewPager.PageTransformer { 11 | private static final float MIN_SCALE = 0.75f; 12 | 13 | public void transformPage(View view, float position) { 14 | int pageWidth = view.getWidth(); 15 | 16 | if (position < -1) { // [-Infinity,-1) 17 | // This page is way off-screen to the left. 18 | view.setAlpha(0); 19 | 20 | } else if (position <= 0) { // [-1,0] 21 | // Use the default slide transition when moving to the left page 22 | view.setAlpha(1); 23 | view.setTranslationX(0); 24 | view.setScaleX(1); 25 | view.setScaleY(1); 26 | 27 | } else if (position <= 1) { // (0,1] 28 | // Fade the page out. 29 | view.setAlpha(1 - position); 30 | 31 | // Counteract the default slide transition 32 | view.setTranslationX(pageWidth * -position); 33 | 34 | } else { // (1,+Infinity] 35 | // This page is way off-screen to the right. 36 | view.setAlpha(0); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/data/AlbumSettings.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.data; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.alienpants.leafpicrevived.data.filter.FilterMode; 7 | import com.alienpants.leafpicrevived.data.sort.SortingMode; 8 | import com.alienpants.leafpicrevived.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 | /** 56 | * This is the constructor used by CREATOR. 57 | */ 58 | protected AlbumSettings(Parcel in) { 59 | this.coverPath = in.readString(); 60 | this.sortingMode = in.readInt(); 61 | this.sortingOrder = in.readInt(); 62 | this.pinned = in.readByte() != 0; 63 | int tmpFilterMode = in.readInt(); 64 | this.filterMode = tmpFilterMode == -1 ? null : FilterMode.values()[tmpFilterMode]; 65 | } 66 | 67 | /** 68 | * It is a non-null static field that must be in parcelable. 69 | */ 70 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 71 | 72 | @Override 73 | public AlbumSettings createFromParcel(Parcel source) { 74 | return new AlbumSettings(source); 75 | } 76 | 77 | @Override 78 | public AlbumSettings[] newArray(int size) { 79 | return new AlbumSettings[size]; 80 | } 81 | }; 82 | } 83 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/data/CursorHandler.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.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 | 13 | static String[] getProjection() { 14 | return new String[0]; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/data/IAlbum.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.data; 2 | 3 | /** 4 | * Created by dnld on 6/28/17. 5 | */ 6 | 7 | public interface IAlbum { 8 | String getName(); 9 | 10 | String getPath(); 11 | 12 | int getCount(); 13 | 14 | Media getCover(); 15 | } 16 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/data/filter/FilterMode.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.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 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/data/filter/FoldersFileFilter.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.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 | } -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/data/filter/IMediaFilter.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.data.filter; 2 | 3 | import com.alienpants.leafpicrevived.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 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/data/filter/ImageFileFilter.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.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 | } -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/data/filter/MediaFilter.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.data.filter; 2 | 3 | import com.alienpants.leafpicrevived.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: 12 | default: 13 | return media -> true; 14 | case GIF: 15 | return Media::isGif; 16 | case VIDEO: 17 | return Media::isVideo; 18 | case IMAGES: 19 | return Media::isImage; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/data/filter/NotHiddenFoldersFilter.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.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 | } -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/data/metadata/MediaDetailsMap.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.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 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/data/provider/ContentProviderHelper.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.data.provider; 2 | 3 | import android.content.Context; 4 | import android.database.Cursor; 5 | import android.provider.MediaStore; 6 | 7 | import com.alienpants.leafpicrevived.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 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/data/provider/QueryUtils.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.data.provider; 2 | 3 | import android.content.ContentResolver; 4 | import android.database.Cursor; 5 | 6 | import com.alienpants.leafpicrevived.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 | } catch (Exception err) { 26 | subscriber.onError(err); 27 | } finally { 28 | if (cursor != null) cursor.close(); 29 | } 30 | }); 31 | } 32 | 33 | /** 34 | * return only the first element if there is one 35 | * 36 | * @param q 37 | * @param cr 38 | * @param ch 39 | * @param 40 | * @return 41 | */ 42 | public static Observable querySingle(Query q, ContentResolver cr, CursorHandler ch) { 43 | return Observable.create(subscriber -> { 44 | Cursor cursor = null; 45 | try { 46 | cursor = q.getCursor(cr); 47 | if (cursor != null && cursor.moveToFirst()) 48 | subscriber.onNext(ch.handle(cursor)); 49 | subscriber.onComplete(); 50 | } catch (Exception err) { 51 | subscriber.onError(err); 52 | } finally { 53 | if (cursor != null) cursor.close(); 54 | } 55 | }); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/data/sort/MediaComparators.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.data.sort; 2 | 3 | 4 | import androidx.annotation.NonNull; 5 | 6 | import com.alienpants.leafpicrevived.data.AlbumSettings; 7 | import com.alienpants.leafpicrevived.data.Media; 8 | import com.alienpants.leafpicrevived.timeline.data.TimelineHeaderModel; 9 | import com.alienpants.leafpicrevived.util.NumericComparator; 10 | 11 | import java.util.Comparator; 12 | 13 | /** 14 | * Created by dnld on 26/04/16. 15 | */ 16 | 17 | public class MediaComparators { 18 | 19 | public static Comparator getComparator(AlbumSettings settings) { 20 | return getComparator(settings.getSortingMode(), settings.getSortingOrder()); 21 | } 22 | 23 | public static Comparator getComparator(SortingMode sortingMode, SortingOrder sortingOrder) { 24 | return sortingOrder == SortingOrder.ASCENDING 25 | ? getComparator(sortingMode) : reverse(getComparator(sortingMode)); 26 | } 27 | 28 | public static Comparator getTimelineComparator(@NonNull SortingOrder sortingOrder) { 29 | return sortingOrder.isAscending() ? getTimelineComparator() : reverse(getTimelineComparator()); 30 | } 31 | 32 | public static Comparator getComparator(SortingMode sortingMode) { 33 | switch (sortingMode) { 34 | case NAME: 35 | return getNameComparator(); 36 | case DATE: 37 | default: 38 | return getDateComparator(); 39 | case SIZE: 40 | return getSizeComparator(); 41 | case TYPE: 42 | return getTypeComparator(); 43 | case NUMERIC: 44 | return getNumericComparator(); 45 | } 46 | } 47 | 48 | private static Comparator reverse(Comparator comparator) { 49 | return (o1, o2) -> comparator.compare(o2, o1); 50 | } 51 | 52 | private static Comparator getDateComparator() { 53 | return (f1, f2) -> f1.getDateModified().compareTo(f2.getDateModified()); 54 | } 55 | 56 | private static Comparator getNameComparator() { 57 | return (f1, f2) -> f1.getPath().compareTo(f2.getPath()); 58 | } 59 | 60 | private static Comparator getSizeComparator() { 61 | return (f1, f2) -> Long.compare(f1.getSize(), f2.getSize()); 62 | } 63 | 64 | private static Comparator getTypeComparator() { 65 | return (f1, f2) -> f1.getMimeType().compareTo(f2.getMimeType()); 66 | } 67 | 68 | private static Comparator getNumericComparator() { 69 | return (f1, f2) -> NumericComparator.filevercmp(f1.getPath(), f2.getPath()); 70 | } 71 | 72 | private static Comparator getTimelineComparator() { 73 | return (t1, t2) -> t1.getDate().compareTo(t2.getDate()); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/data/sort/SortingMode.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.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: 47 | return NAME; 48 | case 1: 49 | default: 50 | return DATE; 51 | case 2: 52 | return SIZE; 53 | case 3: 54 | return TYPE; 55 | case 4: 56 | return NUMERIC; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/data/sort/SortingOrder.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.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 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/fragments/BaseFragment.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.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 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/fragments/BaseMediaFragment.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.fragments; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | 7 | import androidx.annotation.NonNull; 8 | 9 | import com.alienpants.leafpicrevived.data.Media; 10 | 11 | import org.horaapps.liz.ThemeHelper; 12 | 13 | /** 14 | * A Base Fragment for showing Media. 15 | */ 16 | public abstract class BaseMediaFragment extends BaseFragment { 17 | 18 | private static final String ARGS_MEDIA = "args_media"; 19 | 20 | protected Media media; 21 | private MediaTapListener mediaTapListener; 22 | 23 | @NonNull 24 | protected static T newInstance(@NonNull T mediaFragment, 25 | @NonNull Media media) { 26 | 27 | Bundle args = new Bundle(); 28 | args.putParcelable(ARGS_MEDIA, media); 29 | mediaFragment.setArguments(args); 30 | return mediaFragment; 31 | } 32 | 33 | @Override 34 | public void onAttach(Context context) { 35 | super.onAttach(context); 36 | if (context instanceof MediaTapListener) mediaTapListener = (MediaTapListener) context; 37 | } 38 | 39 | private void fetchArgs() { 40 | Bundle args = getArguments(); 41 | if (args == null) throw new RuntimeException("Must pass arguments to Media Fragments!"); 42 | media = getArguments().getParcelable(ARGS_MEDIA); 43 | } 44 | 45 | @Override 46 | public void onCreate(Bundle savedInstanceState) { 47 | super.onCreate(savedInstanceState); 48 | fetchArgs(); 49 | } 50 | 51 | @Override 52 | public void refreshTheme(ThemeHelper themeHelper) { 53 | // Default implementation 54 | } 55 | 56 | protected void setTapListener(@NonNull View view) { 57 | view.setOnClickListener(v -> onTapped()); 58 | } 59 | 60 | private void onTapped() { 61 | mediaTapListener.onViewTapped(); 62 | } 63 | 64 | /** 65 | * Interface for listeners to react on Media Clicks. 66 | */ 67 | public interface MediaTapListener { 68 | 69 | /** 70 | * Called when user taps on the Media view. 71 | */ 72 | void onViewTapped(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/fragments/BaseMediaGridFragment.kt: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.fragments 2 | 3 | import android.content.Context 4 | import android.view.View 5 | 6 | import com.alienpants.leafpicrevived.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 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/fragments/EditModeListener.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.fragments; 2 | 3 | import android.view.View; 4 | 5 | import androidx.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 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/fragments/GifFragment.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.fragments; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | 8 | import androidx.annotation.NonNull; 9 | 10 | import com.alienpants.leafpicrevived.data.Media; 11 | 12 | import pl.droidsonroids.gif.GifImageView; 13 | 14 | /** 15 | * Media Fragment for showing an Image (static) 16 | */ 17 | public class GifFragment extends BaseMediaFragment { 18 | 19 | @NonNull 20 | public static GifFragment newInstance(@NonNull Media media) { 21 | return BaseMediaFragment.newInstance(new GifFragment(), media); 22 | } 23 | 24 | @Override 25 | public View onCreateView(@NonNull LayoutInflater inflater, 26 | ViewGroup container, 27 | Bundle savedInstanceState) { 28 | 29 | GifImageView photoView = new GifImageView(getContext()); 30 | photoView.setImageURI(media.getUri()); 31 | setTapListener(photoView); 32 | return photoView; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/fragments/IFragment.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.fragments; 2 | 3 | /** 4 | * Created by dnld on 3/24/17. 5 | */ 6 | 7 | public interface IFragment { 8 | 9 | boolean editMode(); 10 | 11 | boolean clearSelected(); 12 | } 13 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/fragments/ImageFragment.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.fragments; 2 | 3 | import android.net.Uri; 4 | import android.os.Bundle; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | 9 | import androidx.annotation.NonNull; 10 | import androidx.annotation.Nullable; 11 | 12 | import com.alienpants.leafpicrevived.R; 13 | import com.alienpants.leafpicrevived.data.Media; 14 | import com.alienpants.leafpicrevived.util.BitmapUtils; 15 | import com.davemorrissey.labs.subscaleview.ImageSource; 16 | import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView; 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) 27 | SubsamplingScaleImageView imageView; 28 | 29 | @NonNull 30 | public static ImageFragment newInstance(@NonNull Media media) { 31 | return BaseMediaFragment.newInstance(new ImageFragment(), media); 32 | } 33 | 34 | @Override 35 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 36 | View rootView = inflater.inflate(R.layout.fragment_photo, container, false); 37 | ButterKnife.bind(this, rootView); 38 | return rootView; 39 | } 40 | 41 | @Override 42 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 43 | super.onViewCreated(view, savedInstanceState); 44 | Uri mediaUri = media.getUri(); 45 | 46 | imageView.setOrientation(BitmapUtils.getOrientation(mediaUri, getContext())); 47 | imageView.setImage(ImageSource.uri(mediaUri)); 48 | setTapListener(imageView); 49 | 50 | } 51 | 52 | @Override 53 | public void onDestroyView() { 54 | imageView.recycle(); 55 | super.onDestroyView(); 56 | } 57 | 58 | /** 59 | * Rotate the currently displaying media image. 60 | * 61 | * @param rotationInDegrees The rotation in degrees 62 | */ 63 | public void rotatePicture(int rotationInDegrees) { 64 | if (rotationInDegrees == -90 && imageView.getOrientation() == 0) { 65 | imageView.setOrientation(SubsamplingScaleImageView.ORIENTATION_270); 66 | } else { 67 | imageView.setOrientation(Math.abs(imageView.getOrientation() + rotationInDegrees) % 360); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/fragments/NothingToShowListener.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.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 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/interfaces/MediaClickListener.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.interfaces; 2 | 3 | import com.alienpants.leafpicrevived.data.Album; 4 | import com.alienpants.leafpicrevived.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 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/items/ActionsListener.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.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 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/progress/ErrorCause.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.progress; 2 | 3 | 4 | import androidx.annotation.Nullable; 5 | 6 | import java.util.ArrayList; 7 | 8 | public class ErrorCause { 9 | 10 | private String title; 11 | private ArrayList causes; 12 | 13 | public ErrorCause(String title, ArrayList causes) { 14 | this.title = title; 15 | this.causes = causes; 16 | } 17 | 18 | public ErrorCause(String title) { 19 | this.title = title; 20 | this.causes = new ArrayList<>(1); 21 | } 22 | 23 | public void addCause(String cause) { 24 | this.causes.add(cause); 25 | } 26 | 27 | public String getTitle() { 28 | return title; 29 | } 30 | 31 | public boolean hasErrors() { 32 | return causes.size() > 0; 33 | } 34 | 35 | public @Nullable 36 | ErrorCause get() { 37 | if (hasErrors()) return this; 38 | else return null; 39 | } 40 | 41 | public ArrayList getCauses() { 42 | return causes; 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | StringBuilder b = new StringBuilder(); 48 | b.append(title).append("\n"); 49 | 50 | for (String cause : causes) { 51 | b.append(cause).append("\n"); 52 | } 53 | 54 | return b.toString(); 55 | } 56 | 57 | public static ErrorCause fromThrowable(Throwable throwable) { 58 | if (throwable instanceof ProgressException) 59 | return ((ProgressException) throwable).getError(); 60 | else return new ErrorCause(throwable.getMessage()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/progress/ErrorCauseAdapter.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.progress; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.ViewGroup; 6 | 7 | import androidx.annotation.NonNull; 8 | 9 | import com.alienpants.leafpicrevived.R; 10 | 11 | import org.horaapps.liz.ThemedAdapter; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | public class ErrorCauseAdapter extends ThemedAdapter { 17 | 18 | private List errors; 19 | 20 | public ErrorCauseAdapter(Context context, List errors) { 21 | super(context); 22 | this.errors = errors; 23 | } 24 | 25 | public void setErrors(ArrayList errors) { 26 | this.errors = errors; 27 | notifyDataSetChanged(); 28 | } 29 | 30 | @NonNull 31 | @Override 32 | public ErrorCauseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 33 | return new ErrorCauseViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_error_cause, parent, false)); 34 | } 35 | 36 | @Override 37 | public void onBindViewHolder(@NonNull ErrorCauseViewHolder holder, int position) { 38 | holder.load(errors.get(position)); 39 | super.onBindViewHolder(holder, position); 40 | } 41 | 42 | @Override 43 | public int getItemCount() { 44 | return errors.size(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/progress/ErrorCauseViewHolder.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.progress; 2 | 3 | import android.view.View; 4 | 5 | import com.alienpants.leafpicrevived.R; 6 | 7 | import org.horaapps.liz.ThemedViewHolder; 8 | import org.horaapps.liz.ui.ThemedLinearLayout; 9 | import org.horaapps.liz.ui.ThemedTextView; 10 | 11 | import butterknife.BindView; 12 | import butterknife.ButterKnife; 13 | 14 | public class ErrorCauseViewHolder extends ThemedViewHolder { 15 | 16 | @BindView(R.id.error_title) 17 | ThemedTextView title; 18 | 19 | @BindView(R.id.error_causes) 20 | ThemedLinearLayout causes; 21 | 22 | ErrorCauseViewHolder(View itemView) { 23 | super(itemView); 24 | ButterKnife.bind(this, itemView); 25 | } 26 | 27 | public void load(ErrorCause errorCause) { 28 | title.setText(errorCause.getTitle()); 29 | 30 | causes.removeAllViews(); 31 | for (String c : errorCause.getCauses()) { 32 | ThemedTextView textView = new ThemedTextView(itemView.getContext()); 33 | textView.setStyleColor(ThemedTextView.SUB_TEXT_COLOR); 34 | //textView.setTextSize(); 35 | textView.setText(c); 36 | causes.addView(textView); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/progress/ProgressException.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.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 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/settings/ThemedSetting.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.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 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/timeline/data/TimelineHeaderModel.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.timeline.data; 2 | 3 | 4 | import androidx.annotation.NonNull; 5 | import androidx.annotation.Nullable; 6 | 7 | import java.util.Calendar; 8 | import java.util.Date; 9 | import java.util.GregorianCalendar; 10 | 11 | /** 12 | * Model for showing the Timeline headers. 13 | */ 14 | public class TimelineHeaderModel implements TimelineItem { 15 | 16 | private Calendar calendar; 17 | private String headerText; 18 | 19 | public TimelineHeaderModel(@NonNull Date date) { 20 | this(date.getTime()); 21 | } 22 | 23 | public TimelineHeaderModel(long timeInMillis) { 24 | calendar = new GregorianCalendar(); 25 | calendar.setTimeInMillis(timeInMillis); 26 | } 27 | 28 | public TimelineHeaderModel(@NonNull Calendar calendar) { 29 | this.calendar = calendar; 30 | } 31 | 32 | public void setHeaderText(@NonNull String headerText) { 33 | this.headerText = headerText; 34 | } 35 | 36 | @NonNull 37 | public Calendar getDate() { 38 | return calendar; 39 | } 40 | 41 | @Nullable 42 | public String getHeaderText() { 43 | return headerText; 44 | } 45 | 46 | @Override 47 | public int getTimelineType() { 48 | return TYPE_HEADER; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/timeline/data/TimelineItem.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.timeline.data; 2 | 3 | 4 | import androidx.annotation.IntDef; 5 | 6 | /** 7 | * Interface to define that this item is capable of being displayed on timeline 8 | */ 9 | public interface TimelineItem { 10 | 11 | int TYPE_HEADER = 101; 12 | int TYPE_MEDIA = 102; 13 | 14 | @IntDef({TYPE_HEADER, TYPE_MEDIA}) 15 | @interface TimelineItemType { 16 | } 17 | 18 | @TimelineItemType 19 | int getTimelineType(); 20 | } 21 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/util/AnimationUtils.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.util; 2 | 3 | import androidx.recyclerview.widget.RecyclerView; 4 | import androidx.viewpager.widget.ViewPager; 5 | 6 | import com.alienpants.leafpicrevived.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 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/util/ApplicationUtils.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.util; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.annotation.NonNull; 6 | 7 | import com.alienpants.leafpicrevived.BuildConfig; 8 | 9 | /** 10 | * Data class for holding Application-related data. 11 | */ 12 | public class ApplicationUtils { 13 | 14 | private static String PACKAGE_NAME; 15 | 16 | public static void init(@NonNull Context context) { 17 | PACKAGE_NAME = context.getPackageName(); 18 | } 19 | 20 | /** 21 | * Get the Application's package name specified in Manifest 22 | */ 23 | @NonNull 24 | public static String getPackageName() { 25 | return PACKAGE_NAME; 26 | } 27 | 28 | @NonNull 29 | public static String getAppVersion() { 30 | return BuildConfig.VERSION_NAME; 31 | } 32 | 33 | public static boolean isDebug() { 34 | return BuildConfig.DEBUG; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/util/ArrayUtils.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.util; 2 | 3 | 4 | import androidx.annotation.NonNull; 5 | 6 | /** 7 | * All kinds of Array helpers belong here 8 | */ 9 | public final class ArrayUtils { 10 | 11 | /** 12 | * Find the index of an element in an array. 13 | * Performs a linear search across the array. 14 | * 15 | * @param array The array to search 16 | * @param element The element to find 17 | * @return The position of element in array, else -1 if not found. 18 | */ 19 | public static int getIndex(@NonNull T[] array, @NonNull T element) { 20 | for (int pos = 0; pos < array.length; pos++) { 21 | if (array[pos].equals(element)) return pos; 22 | } 23 | return -1; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/util/BitmapUtils.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.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 | 9 | import androidx.exifinterface.media.ExifInterface; 10 | 11 | import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView; 12 | 13 | import java.io.IOException; 14 | import java.io.InputStream; 15 | 16 | /** 17 | * Created by dnld on 3/25/17. 18 | */ 19 | 20 | public class BitmapUtils { 21 | 22 | public static Bitmap addWhiteBorder(Bitmap bmp, int borderSize) { 23 | Bitmap bmpWithBorder = Bitmap.createBitmap(bmp.getWidth() + borderSize * 2, bmp.getHeight() + borderSize * 2, bmp.getConfig()); 24 | Canvas canvas = new Canvas(bmpWithBorder); 25 | canvas.drawColor(Color.WHITE); 26 | canvas.drawBitmap(bmp, borderSize, borderSize, null); 27 | return bmpWithBorder; 28 | } 29 | 30 | public static Bitmap getCroppedBitmap(Bitmap srcBmp) { 31 | Bitmap dstBmp; 32 | if (srcBmp.getWidth() >= srcBmp.getHeight()) { 33 | dstBmp = Bitmap.createBitmap(srcBmp, 34 | srcBmp.getWidth() / 2 - srcBmp.getHeight() / 2, 0, 35 | srcBmp.getHeight(), srcBmp.getHeight() 36 | ); 37 | } else { 38 | dstBmp = Bitmap.createBitmap(srcBmp, 0, 39 | srcBmp.getHeight() / 2 - srcBmp.getWidth() / 2, 40 | srcBmp.getWidth(), srcBmp.getWidth() 41 | ); 42 | } 43 | return dstBmp; 44 | } 45 | 46 | public static int getOrientation(Uri uri, Context ctx) { 47 | 48 | try (InputStream in = ctx.getContentResolver().openInputStream(uri)) { 49 | if (in == null) { 50 | return 0; 51 | } 52 | ExifInterface exif = new ExifInterface(in); 53 | int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1); 54 | 55 | switch (orientation) { 56 | case ExifInterface.ORIENTATION_ROTATE_180: 57 | return SubsamplingScaleImageView.ORIENTATION_180; 58 | case ExifInterface.ORIENTATION_ROTATE_90: 59 | return SubsamplingScaleImageView.ORIENTATION_90; 60 | case ExifInterface.ORIENTATION_ROTATE_270: 61 | return SubsamplingScaleImageView.ORIENTATION_270; 62 | default: 63 | return SubsamplingScaleImageView.ORIENTATION_0; 64 | } 65 | } catch (IOException e) { 66 | return 0; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/util/ChromeCustomTabs.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.util; 2 | 3 | import android.content.ComponentName; 4 | import android.content.Context; 5 | import android.net.Uri; 6 | 7 | import androidx.annotation.ColorInt; 8 | import androidx.annotation.NonNull; 9 | import androidx.browser.customtabs.CustomTabsClient; 10 | import androidx.browser.customtabs.CustomTabsIntent; 11 | import androidx.browser.customtabs.CustomTabsServiceConnection; 12 | 13 | import org.horaapps.liz.ThemeHelper; 14 | 15 | /** 16 | * A Chrome Custom Tabs wrapper to preload and show URLs in Chrome Custom Tabs. 17 | * Also provides a static method to launch a tab directly without warm up. 18 | */ 19 | public class ChromeCustomTabs { 20 | 21 | private CustomTabsServiceConnection serviceConnection; 22 | private CustomTabsIntent mCustomTabsIntent; 23 | 24 | @ColorInt 25 | private int toolbarColor; 26 | private Context context; 27 | 28 | public ChromeCustomTabs(@NonNull Context context) { 29 | this.context = context; 30 | toolbarColor = ThemeHelper.getInstance(context).getPrimaryColor(); 31 | initService(); 32 | } 33 | 34 | private void initService() { 35 | 36 | serviceConnection = new CustomTabsServiceConnection() { 37 | @Override 38 | public void onCustomTabsServiceConnected(ComponentName componentName, CustomTabsClient customTabsClient) { 39 | customTabsClient.warmup(0L); 40 | } 41 | 42 | @Override 43 | public void onServiceDisconnected(ComponentName name) { 44 | // NO-OP 45 | } 46 | }; 47 | 48 | // Bind the Chrome Custom Tabs service 49 | CustomTabsClient.bindCustomTabsService(context, ApplicationUtils.getPackageName(), serviceConnection); 50 | 51 | mCustomTabsIntent = new CustomTabsIntent.Builder() 52 | .setShowTitle(true) 53 | .setToolbarColor(toolbarColor) 54 | .build(); 55 | } 56 | 57 | public void launchUrl(String Url) { 58 | mCustomTabsIntent.launchUrl(context, Uri.parse(Url)); 59 | } 60 | 61 | /** 62 | * Allow the Chrome Custom Tabs service to disconnect and GC. 63 | */ 64 | public void destroy() { 65 | context.unbindService(serviceConnection); 66 | } 67 | 68 | /** 69 | * Launches a Chrome Custom Tab without warmup / service. 70 | * 71 | * @param context The context - used for launching an Activity. 72 | * @param url The URL to load. 73 | */ 74 | public static void launchUrl(@NonNull Context context, @NonNull String url) { 75 | CustomTabsIntent customTabsIntent = new CustomTabsIntent.Builder().build(); 76 | customTabsIntent.launchUrl(context, Uri.parse(url)); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/util/DeviceUtils.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.util; 2 | 3 | import android.content.res.Configuration; 4 | import android.content.res.Resources; 5 | 6 | import androidx.annotation.NonNull; 7 | 8 | 9 | /** 10 | * Utility class for accessing Android device-specific information 11 | */ 12 | public class DeviceUtils { 13 | 14 | /** 15 | * Returns the state of device being in Landscape orientation. 16 | */ 17 | public static boolean isLandscape(@NonNull Resources resources) { 18 | return resources.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; 19 | } 20 | 21 | /** 22 | * Returns the state of device being in Portrait orientation. 23 | */ 24 | public static boolean isPortrait(@NonNull Resources resources) { 25 | return resources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/util/FingerPrint.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.util; 2 | 3 | import android.app.KeyguardManager; 4 | import android.content.Context; 5 | import android.hardware.fingerprint.FingerprintManager; 6 | import android.os.Build; 7 | 8 | import androidx.annotation.RequiresApi; 9 | 10 | import com.alienpants.leafpicrevived.R; 11 | 12 | import static android.content.Context.FINGERPRINT_SERVICE; 13 | import static android.content.Context.KEYGUARD_SERVICE; 14 | 15 | /** 16 | * Created by gilbert on 24/03/2017. 17 | */ 18 | 19 | public class FingerPrint { 20 | 21 | @RequiresApi(api = Build.VERSION_CODES.M) 22 | public static boolean checkFinger(Context ctx) { 23 | 24 | // Keyguard Manager 25 | KeyguardManager keyguardManager = (KeyguardManager) ctx.getSystemService(KEYGUARD_SERVICE); 26 | // Fingerprint Manager 27 | FingerprintManager fingerprintManager = (FingerprintManager) ctx.getSystemService(FINGERPRINT_SERVICE); 28 | 29 | try { 30 | // Check if the fingerprint sensor is present 31 | if (!fingerprintManager.isHardwareDetected()) { 32 | // Update the UI with a message 33 | StringUtils.showToast(ctx, ctx.getString(R.string.fp_not_supported)); 34 | return false; 35 | } 36 | 37 | if (!fingerprintManager.hasEnrolledFingerprints()) { 38 | StringUtils.showToast(ctx, ctx.getString(R.string.fp_not_configured)); 39 | return false; 40 | } 41 | 42 | if (!keyguardManager.isKeyguardSecure()) { 43 | StringUtils.showToast(ctx, ctx.getString(R.string.fp_not_enabled_sls)); 44 | return false; 45 | } 46 | } catch (SecurityException se) { 47 | se.printStackTrace(); 48 | } 49 | return true; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/util/GLideRotateTransformation.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.util; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.Matrix; 5 | 6 | import androidx.annotation.NonNull; 7 | 8 | import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; 9 | import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; 10 | 11 | import java.security.MessageDigest; 12 | 13 | /** 14 | * Created by dnld on 9/17/17. 15 | */ 16 | 17 | public class GLideRotateTransformation extends BitmapTransformation { 18 | 19 | private float rotateRotationAngle = 0f; 20 | 21 | public GLideRotateTransformation(float rotateRotationAngle) { 22 | this.rotateRotationAngle = rotateRotationAngle; 23 | } 24 | 25 | @Override 26 | protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) { 27 | Matrix matrix = new Matrix(); 28 | 29 | matrix.postRotate(rotateRotationAngle); 30 | 31 | return Bitmap.createBitmap(toTransform, 0, 0, toTransform.getWidth(), toTransform.getHeight(), matrix, true); 32 | } 33 | 34 | @Override 35 | public void updateDiskCacheKey(MessageDigest messageDigest) { 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/util/LegacyCompatFileProvider.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.util; 2 | 3 | import android.content.Context; 4 | import android.database.Cursor; 5 | import android.net.Uri; 6 | 7 | import androidx.core.content.FileProvider; 8 | 9 | import com.commonsware.cwac.provider.LegacyCompatCursorWrapper; 10 | 11 | import java.io.File; 12 | 13 | /** 14 | * Created by dnld on 16/10/17. 15 | */ 16 | 17 | public class LegacyCompatFileProvider extends FileProvider { 18 | 19 | @Override 20 | public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { 21 | return (new LegacyCompatCursorWrapper(super.query(uri, projection, selection, selectionArgs, sortOrder))); 22 | } 23 | 24 | public static Uri getUri(Context context, File file) { 25 | return getUriForFile(context, ApplicationUtils.getPackageName() + ".provider", file); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/util/MediaUtils.kt: -------------------------------------------------------------------------------- 1 | @file: JvmName("MediaUtils") 2 | 3 | package com.alienpants.leafpicrevived.util 4 | 5 | import android.content.Context 6 | import android.content.Intent 7 | import android.net.Uri 8 | import android.widget.Toast 9 | import androidx.fragment.app.FragmentManager 10 | import com.alienpants.leafpicrevived.R 11 | import com.alienpants.leafpicrevived.data.Media 12 | import com.alienpants.leafpicrevived.data.MediaHelper 13 | import com.alienpants.leafpicrevived.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 | // @TODO fix the bottom sheet. it builds, but does not appear. 62 | val bottomSheet = ProgressBottomSheet.Builder(R.string.delete_bottom_sheet_title) 63 | .autoDismiss(false) 64 | .sources(sources) 65 | .listener(deleteListener) 66 | .build() 67 | 68 | bottomSheet.showNow(fragmentManager, null) 69 | } 70 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/util/MimeTypeUtils.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.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 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/util/PermissionUtils.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.util; 2 | 3 | import android.Manifest; 4 | import android.app.Activity; 5 | import android.content.Context; 6 | import android.content.pm.PackageManager; 7 | 8 | import androidx.appcompat.app.AppCompatActivity; 9 | import androidx.core.app.ActivityCompat; 10 | import androidx.core.content.ContextCompat; 11 | 12 | /** 13 | * Created by dnld on 01/04/16. 14 | */ 15 | public final class PermissionUtils { 16 | 17 | public static boolean checkPermissions(Context context, String... permissions) { 18 | for (String permission : permissions) { 19 | if (!checkPermission(context, permission)) { 20 | return false; 21 | } 22 | } 23 | return true; 24 | } 25 | 26 | private static boolean checkPermission(Context context, String permission) { 27 | return ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED; 28 | } 29 | 30 | public static boolean isStoragePermissionsGranted(Context context) { 31 | return checkPermissions(context, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE); 32 | } 33 | 34 | public static void requestPermissions(Object o, int permissionId, String... permissions) { 35 | if (o instanceof Activity) { 36 | ActivityCompat.requestPermissions((AppCompatActivity) o, permissions, permissionId); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/util/RecyclerItemClickListener.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.util; 2 | 3 | import android.view.GestureDetector; 4 | import android.view.MotionEvent; 5 | import android.view.View; 6 | 7 | import androidx.recyclerview.widget.RecyclerView; 8 | 9 | /** 10 | * Created by dnld on 3/24/17. 11 | */ 12 | 13 | public class RecyclerItemClickListener implements RecyclerView.OnItemTouchListener { 14 | 15 | public interface OnItemClickListener { 16 | void onItemClick(View view, int position); 17 | 18 | void onLongItemClick(View view, int position); 19 | } 20 | 21 | private OnItemClickListener mListener; 22 | private GestureDetector mGestureDetector; 23 | 24 | public RecyclerItemClickListener(final RecyclerView rv, OnItemClickListener listener) { 25 | mListener = listener; 26 | mGestureDetector = new GestureDetector(rv.getContext(), new GestureDetector.SimpleOnGestureListener() { 27 | @Override 28 | public boolean onSingleTapUp(MotionEvent e) { 29 | return true; 30 | } 31 | 32 | @Override 33 | public void onLongPress(MotionEvent e) { 34 | View child = rv.findChildViewUnder(e.getX(), e.getY()); 35 | if (child != null && mListener != null) { 36 | mListener.onLongItemClick(child, rv.getChildAdapterPosition(child)); 37 | } 38 | } 39 | }); 40 | } 41 | 42 | @Override 43 | public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) { 44 | View childView = view.findChildViewUnder(e.getX(), e.getY()); 45 | if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) { 46 | mListener.onItemClick(childView, view.getChildAdapterPosition(childView)); 47 | return true; 48 | } 49 | return false; 50 | } 51 | 52 | @Override 53 | public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { 54 | } 55 | 56 | @Override 57 | public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/util/ServerConstants.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.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/apcro/leafpicrevived"; 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 | public static final String GITHUB_TOM = "https://github.com/apcro"; 14 | 15 | // LeafPic URLs 16 | public static final String LEAFPIC_ISSUES = "https://github.com/apcro/leafpicrevived/issues"; 17 | public static final String LEAFPIC_CROWDIN = "https://crowdin.com/project/leafpic"; 18 | public static final String LEAFPIC_LICENSE = "https://github.com/apcro/leafpicrevived/blob/master/LICENSE"; 19 | public static final String LEAFPIC_CHANGELOG = "https://github.com/apcro/leafpicrevived/blob/dev/CHANGELOG.md"; 20 | 21 | 22 | public static final String TWITTER_ABOUT_DONALD = "https://twitter.com/dnld_sht"; 23 | public static final String TWITTER_ABOUT_GILBERT = "https://twitter.com/GilbertNdr"; 24 | public static final String GOOGLE_ABOUT_CALVIN = "https://plus.google.com/+CalvinNoronha2394"; 25 | 26 | // Email IDs 27 | public static final String MAIL_DONALD = "dnld.sht@gmail.com"; 28 | public static final String MAIL_GILBERT = "jibo95@gmail.com"; 29 | public static final String MAIL_CALVIN = "calvin.nrnha@gmail.com"; 30 | public static final String MAIL_TOM = "cro@alienpants.com"; 31 | } 32 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/util/StaticMapProvider.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.util; 2 | 3 | import com.alienpants.leafpicrevived.SecretConstants; 4 | import com.drew.lang.GeoLocation; 5 | 6 | import java.util.Locale; 7 | 8 | // needed for Local.US 9 | 10 | /** 11 | * Created by dnld on 04/09/16. 12 | */ 13 | 14 | public enum StaticMapProvider { 15 | 16 | GOOGLE_MAPS(0), MAP_BOX(1), MAP_BOX_DARK(2), MAP_BOX_LIGHT(3), TYLER(4); 17 | 18 | int value; 19 | 20 | StaticMapProvider(int value) { 21 | this.value = value; 22 | } 23 | 24 | public int getValue() { 25 | return value; 26 | } 27 | 28 | /** 29 | * String.format is locale dependent, we need to force a locale with point instead of comma in decimals, 30 | * otherwise (at least) mapbox does not work in some countries 31 | */ 32 | public String getUrl(GeoLocation location) { 33 | if (value >= 1 && value <= 3) //MAP_MOX invert coordinates 34 | return String.format(Locale.US, getUrl(value), location.getLongitude(), location.getLatitude()); 35 | return String.format(Locale.US, getUrl(value), location.getLatitude(), location.getLongitude()); 36 | } 37 | 38 | private String getUrl(int value) { 39 | switch (value) { 40 | case 0: 41 | default: 42 | return ("http://maps.google.com/maps/api/staticmap?center=%f,%f&zoom=15&size=500x300&scale=2&sensor=false"); 43 | case 1: 44 | return "https://api.mapbox.com/v4/mapbox.streets/%f,%f,15/500x300.jpg?access_token=" + SecretConstants.MAP_BOX_TOKEN; 45 | case 2: 46 | return "https://api.mapbox.com/v4/mapbox.dark/%f,%f,15/500x300.jpg?access_token=" + SecretConstants.MAP_BOX_TOKEN; 47 | case 3: 48 | return "https://api.mapbox.com/v4/mapbox.light/%f,%f,15/500x300.jpg?access_token=" + SecretConstants.MAP_BOX_TOKEN; 49 | case 4: 50 | 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"; 51 | } 52 | } 53 | 54 | public static StaticMapProvider fromValue(int value) { 55 | switch (value) { 56 | case 0: 57 | default: 58 | return GOOGLE_MAPS; 59 | case 1: 60 | return MAP_BOX; 61 | case 2: 62 | return MAP_BOX_DARK; 63 | case 3: 64 | return MAP_BOX_LIGHT; 65 | case 4: 66 | return TYLER; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/util/preferences/Defaults.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.util.preferences; 2 | 3 | import com.alienpants.leafpicrevived.CardViewStyle; 4 | import com.alienpants.leafpicrevived.data.sort.SortingMode; 5 | import com.alienpants.leafpicrevived.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 | 16 | public static final int FOLDER_COLUMNS_PORTRAIT = 2; 17 | public static final int FOLDER_COLUMNS_LANDSCAPE = 3; 18 | 19 | public static final int MEDIA_COLUMNS_PORTRAIT = 3; 20 | public static final int MEDIA_COLUMNS_LANDSCAPE = 4; 21 | 22 | public static final int TIMELINE_ITEMS_PORTRAIT = 4; 23 | public static final int TIMELINE_ITEMS_LANDSCAPE = 5; 24 | 25 | public static final int ALBUM_SORTING_MODE = SortingMode.DATE.getValue(); 26 | public static final int ALBUM_SORTING_ORDER = SortingOrder.DESCENDING.getValue(); 27 | public static final int CARD_STYLE = CardViewStyle.MATERIAL.getValue(); 28 | 29 | public static final boolean SHOW_VIDEOS = true; 30 | public static final boolean SHOW_MEDIA_COUNT = true; 31 | public static final boolean SHOW_ALBUM_PATH = false; 32 | 33 | public static final int LAST_VERSION_CODE = 0; 34 | public static final boolean FORCE_ENGLISH = false; 35 | public static final boolean SHOW_EASTER_EGG = false; 36 | 37 | public static final boolean ANIMATIONS_DISABLED = false; 38 | 39 | public static final boolean TIMELINE_ENABLED = false; 40 | public static final boolean LOOP_VIDEO = false; 41 | } 42 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/util/preferences/Keys.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.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 | 12 | public static final String FOLDER_COLUMNS_PORTRAIT = "folder_columns_portrait"; 13 | public static final String FOLDER_COLUMNS_LANDSCAPE = "folder_columns_landscape"; 14 | 15 | public static final String MEDIA_COLUMNS_PORTRAIT = "media_columns_portrait"; 16 | public static final String MEDIA_COLUMNS_LANDSCAPE = "media_columns_landscape"; 17 | 18 | public static final String ALBUM_SORTING_MODE = "album_sorting_mode"; 19 | public static final String ALBUM_SORTING_ORDER = "album_sorting_order"; 20 | 21 | public static final String SHOW_VIDEOS = "show_videos"; 22 | public static final String SHOW_MEDIA_COUNT = "show_media_count"; 23 | public static final String SHOW_ALBUM_PATH = "show_album_path"; 24 | 25 | public static final String CARD_STYLE = "card_style"; 26 | 27 | public static final String LAST_VERSION_CODE = "last_version_code"; 28 | public static final String SHOW_EASTER_EGG = "show_easter_egg"; 29 | 30 | public static final String ANIMATIONS_DISABLED = "disable_animations"; 31 | public static final String FORCE_ENGLISH = "force_english_locale"; 32 | public static final String LOOP_VIDEO = "loop_video"; 33 | 34 | // Feature flags 35 | public static final String TIMELINE_ENABLED = "enable_timeline"; 36 | } 37 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/util/preferences/SharedPrefs.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.util.preferences; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | 6 | import androidx.annotation.NonNull; 7 | 8 | 9 | /** 10 | * Android's SharedPreferences for storing key-value pairs. 11 | * DO NOT INSTANTIATE THIS CLASS - Use {@link Prefs} for your needs. 12 | */ 13 | /* package */ final class SharedPrefs { 14 | 15 | private static final String PREFERENCES_NAME = "com.alienpants.leafpicrevived.SHARED_PREFS"; 16 | private static final int PREFERENCES_MODE = Context.MODE_PRIVATE; 17 | 18 | private final SharedPreferences sharedPrefs; 19 | 20 | /* package */ SharedPrefs(@NonNull Context context) { 21 | sharedPrefs = context.getApplicationContext() 22 | .getSharedPreferences(PREFERENCES_NAME, PREFERENCES_MODE); 23 | } 24 | 25 | @NonNull 26 | private SharedPreferences.Editor getEditor() { 27 | return sharedPrefs.edit(); 28 | } 29 | 30 | /* package */ int get(@NonNull String key, int defaultValue) { 31 | return sharedPrefs.getInt(key, defaultValue); 32 | } 33 | 34 | /* package */ void put(@NonNull String key, int value) { 35 | getEditor().putInt(key, value).commit(); 36 | } 37 | 38 | /* package */ boolean get(@NonNull String key, boolean defaultValue) { 39 | return sharedPrefs.getBoolean(key, defaultValue); 40 | } 41 | 42 | /* package */ void put(@NonNull String key, boolean value) { 43 | getEditor().putBoolean(key, value).commit(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/views/FabScrollBehaviour.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.views; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.View; 6 | import android.view.animation.AccelerateInterpolator; 7 | import android.view.animation.DecelerateInterpolator; 8 | 9 | import androidx.coordinatorlayout.widget.CoordinatorLayout; 10 | import androidx.core.view.ViewCompat; 11 | 12 | import com.google.android.material.floatingactionbutton.FloatingActionButton; 13 | 14 | /** 15 | * Created by dnld on 06/03/16. 16 | */ 17 | public class FabScrollBehaviour extends FloatingActionButton.Behavior { 18 | 19 | public FabScrollBehaviour(Context context, AttributeSet attributeSet) { 20 | super(); 21 | } 22 | 23 | @Override 24 | public void onNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { 25 | super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed); 26 | if (dyConsumed > 0) 27 | child.animate().translationY(child.getHeight() * 4).setInterpolator(new AccelerateInterpolator(2)).start(); 28 | else 29 | child.animate().translationY(/*-Measure.getNavigationBarSize(coordinatorLayout 30 | .getContext()).y*/0).setInterpolator(new DecelerateInterpolator(2)).start(); 31 | } 32 | 33 | @Override 34 | public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View directTargetChild, View target, int nestedScrollAxes) { 35 | return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/views/GridSpacingItemDecoration.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.views; 2 | 3 | import android.graphics.Rect; 4 | import android.view.View; 5 | 6 | import androidx.recyclerview.widget.RecyclerView; 7 | 8 | /** 9 | * Created by Jibo on 10/03/2016. 10 | */ 11 | public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration { 12 | 13 | private int spanCount; 14 | private int spacing; 15 | private boolean includeEdge; 16 | 17 | public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) { 18 | this.spanCount = spanCount; 19 | this.spacing = spacing; 20 | this.includeEdge = includeEdge; 21 | } 22 | 23 | @Override 24 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { 25 | int position = parent.getChildAdapterPosition(view); // item position 26 | int column = position % spanCount; // item column 27 | 28 | if (includeEdge) { 29 | outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing) 30 | outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing) 31 | if (position < spanCount) { // top edge 32 | outRect.top = spacing; 33 | } 34 | outRect.bottom = spacing; // item bottom 35 | } else { 36 | outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing) 37 | outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing) 38 | if (position >= spanCount) { 39 | outRect.top = spacing; // item top 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/views/HackyViewPager.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.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.util.AttributeSet; 12 | import android.view.MotionEvent; 13 | 14 | import androidx.viewpager.widget.ViewPager; 15 | 16 | /** 17 | * Hacky fix for Issue #4 and 18 | * http://code.google.com/p/android/issues/detail?id=18990 19 | *

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

24 | * There's not much I can do in my code for now, but we can mask the result by 25 | * just catching the problem and ignoring it. 26 | * 27 | * @author Chris Banes 28 | */ 29 | public class HackyViewPager extends ViewPager { 30 | 31 | private boolean isLocked; 32 | 33 | public HackyViewPager(Context context) { 34 | super(context); 35 | isLocked = false; 36 | } 37 | 38 | public HackyViewPager(Context context, AttributeSet attrs) { 39 | super(context, attrs); 40 | isLocked = false; 41 | } 42 | 43 | @Override 44 | public boolean onInterceptTouchEvent(MotionEvent ev) { 45 | if (!isLocked) { 46 | try { 47 | return super.onInterceptTouchEvent(ev); 48 | } catch (IllegalArgumentException e) { 49 | e.printStackTrace(); 50 | return false; 51 | } 52 | } 53 | return false; 54 | } 55 | 56 | @Override 57 | public boolean onTouchEvent(MotionEvent event) { 58 | return !isLocked && super.onTouchEvent(event); 59 | } 60 | 61 | public void toggleLock() { 62 | isLocked = !isLocked; 63 | } 64 | 65 | public boolean isLocked() { 66 | return isLocked; 67 | } 68 | 69 | public void setLocked(boolean isLocked) { 70 | this.isLocked = isLocked; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/views/SquareImageView.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.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 | } -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/views/SquareRelativeLayout.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.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 | } -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/views/ZoomImageView.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.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 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/views/themeable/ThemedCardView.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.views.themeable; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | 6 | import androidx.annotation.Nullable; 7 | import androidx.cardview.widget.CardView; 8 | 9 | import org.horaapps.liz.ThemeHelper; 10 | import org.horaapps.liz.Themed; 11 | 12 | /** 13 | * Created by darken (darken@darken.eu) on 04.03.2017. 14 | */ 15 | public class ThemedCardView extends CardView implements Themed { 16 | 17 | public ThemedCardView(Context context) { 18 | this(context, null); 19 | } 20 | 21 | public ThemedCardView(Context context, @Nullable AttributeSet attrs) { 22 | this(context, attrs, 0); 23 | } 24 | 25 | public ThemedCardView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 26 | super(context, attrs, defStyleAttr); 27 | } 28 | 29 | @Override 30 | public void refreshTheme(ThemeHelper themeHelper) { 31 | setCardBackgroundColor(themeHelper.getCardBackgroundColor()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/views/themeable/ThemedSettingsCaption.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.views.themeable; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | 6 | import androidx.annotation.Nullable; 7 | import androidx.appcompat.widget.AppCompatTextView; 8 | 9 | import org.horaapps.liz.ThemeHelper; 10 | import org.horaapps.liz.Themed; 11 | 12 | /** 13 | * Created by darken (darken@darken.eu) on 04.03.2017. 14 | */ 15 | public class ThemedSettingsCaption extends AppCompatTextView implements Themed { 16 | 17 | public ThemedSettingsCaption(Context context) { 18 | this(context, null); 19 | } 20 | 21 | public ThemedSettingsCaption(Context context, @Nullable AttributeSet attrs) { 22 | this(context, attrs, 0); 23 | } 24 | 25 | public ThemedSettingsCaption(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 26 | super(context, attrs, defStyleAttr); 27 | } 28 | 29 | @Override 30 | public void refreshTheme(ThemeHelper themeHelper) { 31 | setTextColor(themeHelper.getSubTextColor()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/views/themeable/ThemedSettingsCategory.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.views.themeable; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | 6 | import androidx.annotation.Nullable; 7 | import androidx.appcompat.widget.AppCompatTextView; 8 | 9 | import org.horaapps.liz.ThemeHelper; 10 | import org.horaapps.liz.Themed; 11 | 12 | /** 13 | * Created by darken (darken@darken.eu) on 04.03.2017. 14 | */ 15 | public class ThemedSettingsCategory extends AppCompatTextView implements Themed { 16 | 17 | public ThemedSettingsCategory(Context context) { 18 | this(context, null); 19 | } 20 | 21 | public ThemedSettingsCategory(Context context, @Nullable AttributeSet attrs) { 22 | this(context, attrs, 0); 23 | } 24 | 25 | public ThemedSettingsCategory(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 26 | super(context, attrs, defStyleAttr); 27 | } 28 | 29 | @Override 30 | public void refreshTheme(ThemeHelper themeHelper) { 31 | themeHelper.setTextViewColor(this, themeHelper.getAccentColor()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/views/themeable/ThemedSettingsIcon.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.views.themeable; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | 6 | import androidx.annotation.Nullable; 7 | 8 | import org.horaapps.liz.ThemeHelper; 9 | import org.horaapps.liz.Themed; 10 | import org.horaapps.liz.ui.ThemedIcon; 11 | 12 | /** 13 | * Created by darken (darken@darken.eu) on 04.03.2017. 14 | */ 15 | public class ThemedSettingsIcon extends ThemedIcon implements Themed { 16 | 17 | public ThemedSettingsIcon(Context context) { 18 | this(context, null); 19 | } 20 | 21 | public ThemedSettingsIcon(Context context, @Nullable AttributeSet attrs) { 22 | this(context, attrs, 0); 23 | } 24 | 25 | public ThemedSettingsIcon(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 26 | super(context, attrs, defStyleAttr); 27 | } 28 | 29 | @Override 30 | public void refreshTheme(ThemeHelper themeHelper) { 31 | setColor(themeHelper.getIconColor()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/java/com/alienpants/leafpicrevived/views/themeable/ThemedSettingsTitle.java: -------------------------------------------------------------------------------- 1 | package com.alienpants.leafpicrevived.views.themeable; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | 6 | import androidx.annotation.Nullable; 7 | import androidx.appcompat.widget.AppCompatTextView; 8 | 9 | import org.horaapps.liz.ThemeHelper; 10 | import org.horaapps.liz.Themed; 11 | 12 | /** 13 | * Created by darken (darken@darken.eu) on 04.03.2017. 14 | */ 15 | public class ThemedSettingsTitle extends AppCompatTextView implements Themed { 16 | 17 | public ThemedSettingsTitle(Context context) { 18 | this(context, null); 19 | } 20 | 21 | public ThemedSettingsTitle(Context context, @Nullable AttributeSet attrs) { 22 | this(context, attrs, 0); 23 | } 24 | 25 | public ThemedSettingsTitle(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 26 | super(context, attrs, defStyleAttr); 27 | } 28 | 29 | @Override 30 | public void refreshTheme(ThemeHelper themeHelper) { 31 | setTextColor(themeHelper.getTextColor()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/anim/fade_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 15 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/anim/fade_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/anim/grid_layout_animation.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/anim/slide_bottom_in.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/anim/slide_down.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 11 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/anim/slide_fade_card.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/anim/slide_fade_photos.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/anim/zoom_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable-v19/ripple.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable-v21/ripple.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 16 | 18 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/alienpants_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apcro/leafpicrevived/b5b3a0f82da11eddbc7edb12d3acc4987a950c9b/leafpicrevived/src/main/res/drawable/alienpants_logo.png -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/calvin_header.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apcro/leafpicrevived/b5b3a0f82da11eddbc7edb12d3acc4987a950c9b/leafpicrevived/src/main/res/drawable/calvin_header.jpg -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/calvin_profile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apcro/leafpicrevived/b5b3a0f82da11eddbc7edb12d3acc4987a950c9b/leafpicrevived/src/main/res/drawable/calvin_profile.jpg -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/donald_header.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apcro/leafpicrevived/b5b3a0f82da11eddbc7edb12d3acc4987a950c9b/leafpicrevived/src/main/res/drawable/donald_header.jpg -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/donald_profile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apcro/leafpicrevived/b5b3a0f82da11eddbc7edb12d3acc4987a950c9b/leafpicrevived/src/main/res/drawable/donald_profile.jpg -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/gilbert_header.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apcro/leafpicrevived/b5b3a0f82da11eddbc7edb12d3acc4987a950c9b/leafpicrevived/src/main/res/drawable/gilbert_header.jpg -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/gilbert_profile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apcro/leafpicrevived/b5b3a0f82da11eddbc7edb12d3acc4987a950c9b/leafpicrevived/src/main/res/drawable/gilbert_profile.jpg -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/ic_close.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/ic_delete.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/ic_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apcro/leafpicrevived/b5b3a0f82da11eddbc7edb12d3acc4987a950c9b/leafpicrevived/src/main/res/drawable/ic_empty.png -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/ic_empty_amoled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apcro/leafpicrevived/b5b3a0f82da11eddbc7edb12d3acc4987a950c9b/leafpicrevived/src/main/res/drawable/ic_empty_amoled.png -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/ic_empty_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apcro/leafpicrevived/b5b3a0f82da11eddbc7edb12d3acc4987a950c9b/leafpicrevived/src/main/res/drawable/ic_empty_white.png -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/ic_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apcro/leafpicrevived/b5b3a0f82da11eddbc7edb12d3acc4987a950c9b/leafpicrevived/src/main/res/drawable/ic_error.png -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/ic_filter.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/ic_gif_white_18dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apcro/leafpicrevived/b5b3a0f82da11eddbc7edb12d3acc4987a950c9b/leafpicrevived/src/main/res/drawable/ic_gif_white_18dp.png -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/ic_group.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/ic_holder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apcro/leafpicrevived/b5b3a0f82da11eddbc7edb12d3acc4987a950c9b/leafpicrevived/src/main/res/drawable/ic_holder.png -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/ic_scrollbar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apcro/leafpicrevived/b5b3a0f82da11eddbc7edb12d3acc4987a950c9b/leafpicrevived/src/main/res/drawable/ic_scrollbar.png -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/ic_select_all.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/ic_share.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/leaf_pic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apcro/leafpicrevived/b5b3a0f82da11eddbc7edb12d3acc4987a950c9b/leafpicrevived/src/main/res/drawable/leaf_pic.png -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/leaf_pic_featureimage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apcro/leafpicrevived/b5b3a0f82da11eddbc7edb12d3acc4987a950c9b/leafpicrevived/src/main/res/drawable/leaf_pic_featureimage.png -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/line.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/line_drawable.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/thumb.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 | 14 | 15 | 16 | 17 | 20 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/thumb_drawable.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/drawable/tom_profile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apcro/leafpicrevived/b5b3a0f82da11eddbc7edb12d3acc4987a950c9b/leafpicrevived/src/main/res/drawable/tom_profile.jpg -------------------------------------------------------------------------------- /leafpicrevived/src/main/res/layout/activity_affix.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 20 | 21 |