├── app ├── .gitignore ├── version.properties ├── src │ ├── main │ │ ├── res │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_background.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_background.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_background.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── ic_launcher_background.png │ │ │ │ └── ic_launcher_foreground.png │ │ │ ├── drawable-xxxhdpi │ │ │ │ ├── ic_item_caret.png │ │ │ │ ├── ic_stat_notify_paste_0.png │ │ │ │ ├── ic_stat_notify_paste_1.png │ │ │ │ ├── ic_stat_notify_paste_2.png │ │ │ │ ├── ic_stat_notify_paste_3.png │ │ │ │ ├── ic_stat_notify_paste_4.png │ │ │ │ ├── ic_stat_notify_paste_5.png │ │ │ │ ├── bg_accent_line_bottom.9.png │ │ │ │ ├── ic_stat_notify_compress_0.png │ │ │ │ ├── ic_stat_notify_compress_1.png │ │ │ │ ├── ic_stat_notify_compress_2.png │ │ │ │ ├── ic_stat_notify_compress_3.png │ │ │ │ ├── ic_stat_notify_compress_4.png │ │ │ │ └── ic_stat_notify_compress_5.png │ │ │ ├── values-sw600dp │ │ │ │ ├── integers.xml │ │ │ │ └── dimens.xml │ │ │ ├── values-sw720dp │ │ │ │ └── integers.xml │ │ │ ├── drawable-xxhdpi │ │ │ │ └── list_divider_mtrl_alpha.9.png │ │ │ ├── values │ │ │ │ ├── keep.xml │ │ │ │ ├── ids.xml │ │ │ │ ├── integers.xml │ │ │ │ ├── styles_dir.xml │ │ │ │ ├── attrs.xml │ │ │ │ ├── dimens.xml │ │ │ │ ├── strings_not_for_translation.xml │ │ │ │ ├── colors.xml │ │ │ │ └── styles.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ └── ic_launcher.xml │ │ │ ├── layout │ │ │ │ ├── item_details.xml │ │ │ │ ├── activity_generic.xml │ │ │ │ ├── widget_toolbar.xml │ │ │ │ ├── dialog_details.xml │ │ │ │ ├── dialog_text_input.xml │ │ │ │ ├── activity_filemanager.xml │ │ │ │ ├── fragment_filelist_simple.xml │ │ │ │ └── fragment_filelist_pick.xml │ │ │ ├── drawable │ │ │ │ ├── ic_dialog_delete.xml │ │ │ │ ├── ic_action_cab_delete.xml │ │ │ │ ├── ic_action_delete.xml │ │ │ │ ├── ic_action_box.xml │ │ │ │ ├── ic_dialog_folder.xml │ │ │ │ ├── ic_action_cab_folder.xml │ │ │ │ ├── ic_action_contact.xml │ │ │ │ ├── ic_action_undo.xml │ │ │ │ ├── ic_item_file.xml │ │ │ │ ├── ic_action_create_folder.xml │ │ │ │ ├── ic_item_audio.xml │ │ │ │ ├── ic_item_folder.xml │ │ │ │ ├── ic_action_copy.xml │ │ │ │ ├── ic_item_image.xml │ │ │ │ ├── ic_action_overflow.xml │ │ │ │ ├── ic_item_text.xml │ │ │ │ ├── ic_item_sdcard.xml │ │ │ │ ├── ic_action_review.xml │ │ │ │ ├── ic_action_rename.xml │ │ │ │ ├── ic_action_search.xml │ │ │ │ ├── ic_item_video.xml │ │ │ │ ├── ic_hint_search.xml │ │ │ │ ├── ic_item_archive.xml │ │ │ │ ├── ic_about_translate.xml │ │ │ │ ├── ic_action_paste_seven.xml │ │ │ │ ├── ic_action_paste_one.xml │ │ │ │ ├── ic_action_share.xml │ │ │ │ ├── ic_pref_sort_ascending.xml │ │ │ │ ├── ic_action_paste_four.xml │ │ │ │ ├── ic_action_cut.xml │ │ │ │ ├── ic_about_donate.xml │ │ │ │ ├── bg_item_circle.xml │ │ │ │ ├── divider.xml │ │ │ │ ├── ic_button_permission.xml │ │ │ │ ├── ic_item_android_package.xml │ │ │ │ ├── ic_pref_file_hidden.xml │ │ │ │ ├── bg_empty_state_circle.xml │ │ │ │ ├── ic_menu.xml │ │ │ │ ├── ic_navbar_accept.xml │ │ │ │ ├── ic_pref_sort.xml │ │ │ │ ├── ic_navbar_close.xml │ │ │ │ ├── ic_sidenav_title.xml │ │ │ │ ├── ic_button_back.xml │ │ │ │ ├── ic_sidenav_about.xml │ │ │ │ ├── bg_activated.xml │ │ │ │ ├── bg_actionmode.xml │ │ │ │ ├── ic_pref_media_library.xml │ │ │ │ ├── ic_action_paste_two.xml │ │ │ │ ├── ic_stat_notify_paste.xml │ │ │ │ ├── ic_logo.xml │ │ │ │ ├── ic_stat_notify_compress.xml │ │ │ │ ├── ic_stat_notify_extract.xml │ │ │ │ ├── ic_action_paste_five.xml │ │ │ │ ├── ic_pref_palette.xml │ │ │ │ ├── ic_action_paste.xml │ │ │ │ ├── ic_action_paste_six.xml │ │ │ │ ├── ic_action_paste_nine.xml │ │ │ │ ├── ic_navbar_edit.xml │ │ │ │ ├── ic_action_paste_three.xml │ │ │ │ ├── ic_sidenav_settings.xml │ │ │ │ ├── ic_action_paste_eight.xml │ │ │ │ ├── ic_action_paste_more.xml │ │ │ │ └── ic_about_github.xml │ │ │ ├── menu │ │ │ │ ├── options_filemanager.xml │ │ │ │ ├── cab_bookmarks.xml │ │ │ │ ├── options_about.xml │ │ │ │ ├── cab_multi.xml │ │ │ │ └── options_filelist_simple.xml │ │ │ ├── drawable-v23 │ │ │ │ └── ic_button_permission.xml │ │ │ ├── anim │ │ │ │ └── state_list_anim_material.xml │ │ │ └── xml │ │ │ │ └── mimetypes.xml │ │ └── java │ │ │ └── com │ │ │ └── veniosg │ │ │ └── dir │ │ │ ├── android │ │ │ ├── adapter │ │ │ │ ├── SearchListViewHolder.java │ │ │ │ ├── FileListViewHolder.java │ │ │ │ ├── SearchListAdapter.java │ │ │ │ └── BookmarkListAdapter.java │ │ │ ├── ui │ │ │ │ ├── ViewHolder.java │ │ │ │ ├── toast │ │ │ │ │ └── ToastDisplayer.java │ │ │ │ ├── AnimatorSynchroniser.java │ │ │ │ └── widget │ │ │ │ │ ├── WaitingViewFlipper.java │ │ │ │ │ └── PathContainerLayout.java │ │ │ ├── dialog │ │ │ │ ├── OverwriteFileDialog.java │ │ │ │ ├── DeleteAsyncTask.java │ │ │ │ ├── SingleDeleteDialog.java │ │ │ │ └── MultiDeleteDialog.java │ │ │ ├── activity │ │ │ │ ├── PreferenceActivity.java │ │ │ │ ├── SearchActivity.java │ │ │ │ └── BaseActivity.java │ │ │ └── util │ │ │ │ └── Logger.java │ │ │ ├── mvvm │ │ │ ├── model │ │ │ │ ├── iab │ │ │ │ │ ├── Purchase.java │ │ │ │ │ └── BillingManager.java │ │ │ │ ├── storage │ │ │ │ │ ├── operation │ │ │ │ │ │ ├── ui │ │ │ │ │ │ │ ├── OperationStatusDisplayer.java │ │ │ │ │ │ │ └── OperationStatusDisplayerInjector.java │ │ │ │ │ │ ├── FileOperationRunnerInjector.java │ │ │ │ │ │ ├── argument │ │ │ │ │ │ │ ├── CreateDirectoryArguments.java │ │ │ │ │ │ │ ├── RenameArguments.java │ │ │ │ │ │ │ ├── DeleteArguments.java │ │ │ │ │ │ │ ├── ExtractArguments.java │ │ │ │ │ │ │ ├── CompressArguments.java │ │ │ │ │ │ │ ├── MoveArguments.java │ │ │ │ │ │ │ └── CopyArguments.java │ │ │ │ │ │ ├── FileOperation.java │ │ │ │ │ │ └── CreateDirectoryOperation.java │ │ │ │ │ └── access │ │ │ │ │ │ ├── StorageAccessManager.java │ │ │ │ │ │ ├── ExternalStorageAccessManager.java │ │ │ │ │ │ └── SdaStorageAccessManager.java │ │ │ │ ├── DirectoryHolder.java │ │ │ │ └── search │ │ │ │ │ └── SearchState.java │ │ │ └── viewmodel │ │ │ │ └── search │ │ │ │ └── SearchViewModel.java │ │ │ └── AnimationConstants.java │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── veniosg │ │ │ └── dir │ │ │ └── mvvm │ │ │ ├── model │ │ │ └── storage │ │ │ │ └── operation │ │ │ │ ├── FakeArguments.java │ │ │ │ └── FakeStorageAccessManager.java │ │ │ └── viewmodel │ │ │ └── search │ │ │ └── SearchViewModelTest.java │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── veniosg │ │ │ └── dir │ │ │ └── test │ │ │ ├── acceptance │ │ │ ├── SettingsActivityTest.java │ │ │ ├── InternalStorageOperationsTest.java │ │ │ └── SdCardOperationsTest.java │ │ │ ├── injector │ │ │ └── ActorInjector.java │ │ │ ├── actor │ │ │ ├── Android.java │ │ │ ├── User.java │ │ │ ├── assertion │ │ │ │ ├── LaunchedAssertions.java │ │ │ │ └── CannotSeeAssertions.java │ │ │ └── action │ │ │ │ └── TypesActions.java │ │ │ └── matcher │ │ │ └── FileHolderHasName.java │ ├── play │ │ ├── java │ │ │ └── com.veniosg.dir.mvvm.model.iab │ │ │ │ └── BillingManagerInjector.java │ │ └── AndroidManifest.xml │ └── fdroid │ │ └── java │ │ └── com.veniosg.dir.mvvm.model.iab │ │ ├── BillingManagerInjector.java │ │ └── FDroidBillingManager.java └── proguard-rules.pro ├── settings.gradle ├── debug.keystore ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── keystore.properties.example ├── .gitignore ├── gradle.properties ├── README.md └── gradlew.bat /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /app/version.properties: -------------------------------------------------------------------------------- 1 | code = 1560 2 | name = 1.5.6 3 | -------------------------------------------------------------------------------- /debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/debug.keystore -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /keystore.properties.example: -------------------------------------------------------------------------------- 1 | store=../debug.keystore 2 | alias=androiddebugkey 3 | pass=android 4 | storePass=android 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_item_caret.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/app/src/main/res/drawable-xxxhdpi/ic_item_caret.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_stat_notify_paste_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/app/src/main/res/drawable-xxxhdpi/ic_stat_notify_paste_0.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_stat_notify_paste_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/app/src/main/res/drawable-xxxhdpi/ic_stat_notify_paste_1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_stat_notify_paste_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/app/src/main/res/drawable-xxxhdpi/ic_stat_notify_paste_2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_stat_notify_paste_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/app/src/main/res/drawable-xxxhdpi/ic_stat_notify_paste_3.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_stat_notify_paste_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/app/src/main/res/drawable-xxxhdpi/ic_stat_notify_paste_4.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_stat_notify_paste_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/app/src/main/res/drawable-xxxhdpi/ic_stat_notify_paste_5.png -------------------------------------------------------------------------------- /app/src/main/res/values-sw600dp/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2 4 | -------------------------------------------------------------------------------- /app/src/main/res/values-sw720dp/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 3 4 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/list_divider_mtrl_alpha.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/app/src/main/res/drawable-xxhdpi/list_divider_mtrl_alpha.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/bg_accent_line_bottom.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/app/src/main/res/drawable-xxxhdpi/bg_accent_line_bottom.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_stat_notify_compress_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/app/src/main/res/drawable-xxxhdpi/ic_stat_notify_compress_0.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_stat_notify_compress_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/app/src/main/res/drawable-xxxhdpi/ic_stat_notify_compress_1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_stat_notify_compress_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/app/src/main/res/drawable-xxxhdpi/ic_stat_notify_compress_2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_stat_notify_compress_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/app/src/main/res/drawable-xxxhdpi/ic_stat_notify_compress_3.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_stat_notify_compress_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/app/src/main/res/drawable-xxxhdpi/ic_stat_notify_compress_4.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_stat_notify_compress_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/veniosg/Dir/HEAD/app/src/main/res/drawable-xxxhdpi/ic_stat_notify_compress_5.png -------------------------------------------------------------------------------- /app/src/main/res/values/keep.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Oct 12 13:37:12 BST 2017 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-4.1-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_details.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/test/java/com/veniosg/dir/mvvm/model/storage/operation/FakeArguments.java: -------------------------------------------------------------------------------- 1 | package com.veniosg.dir.mvvm.model.storage.operation; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | import java.io.File; 6 | 7 | public class FakeArguments extends FileOperation.Arguments { 8 | protected FakeArguments(@NonNull File target) { 9 | super(target); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/res/values-sw600dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | @dimen/item_height_tablet 3 | @dimen/item_icon_margin_left_tablet 4 | @dimen/item_text_margin_left_tablet 5 | @dimen/dialog_content_padding_tablet 6 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/veniosg/dir/test/acceptance/SettingsActivityTest.java: -------------------------------------------------------------------------------- 1 | package com.veniosg.dir.test.acceptance; 2 | 3 | import android.support.test.filters.LargeTest; 4 | import android.support.test.runner.AndroidJUnit4; 5 | 6 | import org.junit.Ignore; 7 | import org.junit.runner.RunWith; 8 | 9 | @RunWith(AndroidJUnit4.class) 10 | @LargeTest 11 | @Ignore 12 | public class SettingsActivityTest { 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_dialog_delete.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_cab_delete.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_delete.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_box.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_dialog_folder.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_cab_folder.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_contact.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_undo.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_item_file.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_create_folder.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/android/adapter/SearchListViewHolder.java: -------------------------------------------------------------------------------- 1 | package com.veniosg.dir.android.adapter; 2 | 3 | import android.view.ViewGroup; 4 | 5 | public class SearchListViewHolder extends FileListViewHolder { 6 | SearchListViewHolder(ViewGroup parent) { 7 | super(parent); 8 | } 9 | 10 | @Override 11 | void bind(String filePath, OnItemClickListener listener) { 12 | super.bind(filePath, listener); 13 | secondaryInfo.setText(filePath); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_item_audio.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_item_folder.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_copy.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_item_image.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_overflow.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_item_text.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_item_sdcard.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_review.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/mvvm/model/iab/Purchase.java: -------------------------------------------------------------------------------- 1 | package com.veniosg.dir.mvvm.model.iab; 2 | 3 | /** 4 | * Store-agnostic version of play billing's Purchase class. 5 | */ 6 | @SuppressWarnings("WeakerAccess") 7 | public class Purchase { 8 | private String sku; 9 | private String purchaseToken; 10 | 11 | Purchase(String sku, String purchaseToken) { 12 | } 13 | 14 | public String getSku() { 15 | return sku; 16 | } 17 | 18 | public String getPurchaseToken() { 19 | return purchaseToken; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_rename.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_search.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_item_video.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | keystore.properties 3 | .idea 4 | *.iml 5 | 6 | # Built application files 7 | *.apk 8 | *.ap_ 9 | 10 | # Files for the Dalvik VM 11 | *.dex 12 | 13 | # Java class files 14 | *.class 15 | 16 | # Generated files 17 | bin/ 18 | gen/ 19 | 20 | # Gradle files 21 | .gradle/ 22 | build/ 23 | 24 | # Local configuration file (sdk path, etc) 25 | local.properties 26 | 27 | # Proguard folder generated by Eclipse 28 | proguard/ 29 | 30 | # Log Files 31 | *.log 32 | 33 | # Android Studio Navigation editor temp files 34 | .navigation/ 35 | 36 | # Android Studio captures folder 37 | captures/ 38 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_hint_search.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/veniosg/dir/test/injector/ActorInjector.java: -------------------------------------------------------------------------------- 1 | package com.veniosg.dir.test.injector; 2 | 3 | import android.app.Activity; 4 | import android.support.test.rule.ActivityTestRule; 5 | 6 | import com.veniosg.dir.test.actor.Android; 7 | import com.veniosg.dir.test.actor.User; 8 | 9 | public class ActorInjector { 10 | private ActorInjector() {} 11 | 12 | public static User user(ActivityTestRule activityRule) { 13 | return new User(activityRule); 14 | } 15 | 16 | public static Android android(ActivityTestRule activityRule) { 17 | return new Android(activityRule); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_item_archive.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_about_translate.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/mvvm/model/storage/operation/ui/OperationStatusDisplayer.java: -------------------------------------------------------------------------------- 1 | package com.veniosg.dir.mvvm.model.storage.operation.ui; 2 | 3 | import java.io.File; 4 | 5 | public interface OperationStatusDisplayer { 6 | void initChannels(); 7 | 8 | void showCopyProgress(int operationId, File destDir, File copying, int progress, int max); 9 | void showCopySuccess(int operationId, File destDir); 10 | void showCopyFailure(int operationId, File destDir); 11 | 12 | void showMoveProgress(int operationId, File destDir, File moving, int progress, int max); 13 | void showMoveSuccess(int operationId, File destDir); 14 | void showMoveFailure(int operationId, File destDir); 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_paste_seven.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_paste_one.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/mvvm/model/storage/operation/FileOperationRunnerInjector.java: -------------------------------------------------------------------------------- 1 | package com.veniosg.dir.mvvm.model.storage.operation; 2 | 3 | import android.content.Context; 4 | 5 | import com.veniosg.dir.android.ui.toast.ToastDisplayer; 6 | import com.veniosg.dir.mvvm.model.storage.access.ExternalStorageAccessManager; 7 | 8 | public abstract class FileOperationRunnerInjector { 9 | private FileOperationRunnerInjector() { 10 | } 11 | 12 | /** 13 | * Builds a default instance of {@link FileOperationRunner}. 14 | */ 15 | public static FileOperationRunner operationRunner(Context c) { 16 | return new FileOperationRunner(new ExternalStorageAccessManager(c), new ToastDisplayer(c)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_share.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:/Users/George/AppData/Local/Android/android-studio/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the ProGuard 5 | # include property in project.properties. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options 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 | #} -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_pref_sort_ascending.xml: -------------------------------------------------------------------------------- 1 | 9 | 14 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/values/ids.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_paste_four.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/values/integers.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 1 19 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/veniosg/dir/test/actor/Android.java: -------------------------------------------------------------------------------- 1 | package com.veniosg.dir.test.actor; 2 | 3 | import android.app.Activity; 4 | import android.support.test.rule.ActivityTestRule; 5 | 6 | import com.veniosg.dir.test.actor.action.LaunchesActions; 7 | import com.veniosg.dir.test.actor.assertion.LaunchedAssertions; 8 | 9 | public class Android { 10 | private final LaunchesActions launches; 11 | private final LaunchedAssertions launched; 12 | 13 | public Android(ActivityTestRule activityRule) { 14 | launches = new LaunchesActions(activityRule); 15 | launched = new LaunchedAssertions(); 16 | } 17 | 18 | public LaunchesActions launches() { 19 | return launches; 20 | } 21 | 22 | public LaunchedAssertions launched() { 23 | return launched; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_cut.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_about_donate.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_item_circle.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Settings specified in this file will override any Gradle settings 5 | # configured through the IDE. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /app/src/main/res/drawable/divider.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | -------------------------------------------------------------------------------- /app/src/play/java/com.veniosg.dir.mvvm.model.iab/BillingManagerInjector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.veniosg.dir.mvvm.model.iab; 17 | 18 | public abstract class BillingManagerInjector { 19 | public static BillingManager billingManager() { 20 | return new PlayBillingManager(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/fdroid/java/com.veniosg.dir.mvvm.model.iab/BillingManagerInjector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.veniosg.dir.mvvm.model.iab; 17 | 18 | public abstract class BillingManagerInjector { 19 | public static BillingManager billingManager() { 20 | return new FDroidBillingManager(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_button_permission.xml: -------------------------------------------------------------------------------- 1 | 17 | 19 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/android/ui/ViewHolder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 OpenIntents.org 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir.android.ui; 18 | 19 | import android.widget.ImageView; 20 | import android.widget.TextView; 21 | 22 | public class ViewHolder { 23 | public ImageView icon; 24 | public TextView primaryInfo; 25 | public TextView secondaryInfo; 26 | public TextView tertiaryInfo; 27 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_item_android_package.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_pref_file_hidden.xml: -------------------------------------------------------------------------------- 1 | 9 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_empty_state_circle.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/mvvm/model/DirectoryHolder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 OpenIntents.org 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir.mvvm.model; 18 | 19 | import java.util.List; 20 | 21 | public class DirectoryHolder { 22 | public List listDir; 23 | public List listFile; 24 | public List listSdCard; 25 | 26 | // If true, there's a ".nomedia" file in this directory. 27 | public boolean noMedia; 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_menu.xml: -------------------------------------------------------------------------------- 1 | 16 | 22 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_navbar_accept.xml: -------------------------------------------------------------------------------- 1 | 16 | 21 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_pref_sort.xml: -------------------------------------------------------------------------------- 1 | 16 | 21 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/play/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 20 | 21 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_navbar_close.xml: -------------------------------------------------------------------------------- 1 | 16 | 22 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sidenav_title.xml: -------------------------------------------------------------------------------- 1 | 16 | 22 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_button_back.xml: -------------------------------------------------------------------------------- 1 | 17 | 22 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/menu/options_filemanager.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_generic.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 21 | 22 | 23 | 24 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sidenav_about.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 23 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_activated.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/AnimationConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir; 18 | 19 | import android.view.animation.Interpolator; 20 | import android.view.animation.PathInterpolator; 21 | 22 | public class AnimationConstants { 23 | public static final int ANIM_DURATION = 300; 24 | public static final int ANIM_START_DELAY = 0; 25 | 26 | public static final Interpolator IN_INTERPOLATOR = new PathInterpolator(0.8F, 0, 0.2F, 1); // slow_in_slow_out 27 | public static final Interpolator OUT_INTERPOLATOR = new PathInterpolator(0.8F, 0, 0.2F, 1); 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles_dir.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 23 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/mvvm/model/search/SearchState.java: -------------------------------------------------------------------------------- 1 | package com.veniosg.dir.mvvm.model.search; 2 | 3 | import java.util.Collections; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | 7 | import static java.util.Collections.unmodifiableList; 8 | 9 | public class SearchState { 10 | private boolean finished = false; 11 | private final List results = new LinkedList<>(); 12 | 13 | SearchState() {} 14 | 15 | SearchState(SearchState from) { 16 | finished = from.finished; 17 | results.addAll(from.results); 18 | } 19 | 20 | void addResult(String path) { 21 | results.add(path); 22 | } 23 | 24 | void setFinished() { 25 | finished = true; 26 | } 27 | 28 | void reset() { 29 | finished = false; 30 | results.clear(); 31 | } 32 | 33 | public List results() { 34 | return unmodifiableList(results); 35 | } 36 | 37 | public boolean isFinished() { 38 | return finished; 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return "SearchState{" + 44 | "finished=" + finished + 45 | ", results=" + results + 46 | '}'; 47 | } 48 | } -------------------------------------------------------------------------------- /app/src/main/res/menu/cab_bookmarks.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 23 | 24 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/widget_toolbar.xml: -------------------------------------------------------------------------------- 1 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_actionmode.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_pref_media_library.xml: -------------------------------------------------------------------------------- 1 | 16 | 21 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_details.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 23 | 24 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/menu/options_about.xml: -------------------------------------------------------------------------------- 1 | 16 | 19 | 24 | 29 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v23/ic_button_permission.xml: -------------------------------------------------------------------------------- 1 | 17 | 23 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/veniosg/dir/test/matcher/FileHolderHasName.java: -------------------------------------------------------------------------------- 1 | package com.veniosg.dir.test.matcher; 2 | 3 | import com.veniosg.dir.mvvm.model.FileHolder; 4 | 5 | import org.hamcrest.Factory; 6 | import org.hamcrest.FeatureMatcher; 7 | import org.hamcrest.Matcher; 8 | 9 | import static org.hamcrest.core.IsEqual.equalTo; 10 | 11 | public class FileHolderHasName extends FeatureMatcher { 12 | public FileHolderHasName(Matcher hasFileHolderMatcher) { 13 | super(hasFileHolderMatcher, "with getName()", "getName()"); 14 | } 15 | 16 | @Override 17 | protected String featureValueOf(T actual) { 18 | if (actual instanceof FileHolder) { 19 | return ((FileHolder) actual).getName(); 20 | } else { 21 | return null; 22 | } 23 | } 24 | 25 | /** 26 | * Creates a matcher that matches any examined object whose toString method 27 | * returns a value equalTo the specified string. 28 | *

29 | * For example: 30 | *

assertThat(true, hasToString("TRUE"))
31 | * 32 | * @param expectedName the expected toString result 33 | */ 34 | @Factory 35 | public static Matcher hasName(String expectedName) { 36 | return new FileHolderHasName(equalTo(expectedName)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/mvvm/model/storage/operation/argument/CreateDirectoryArguments.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir.mvvm.model.storage.operation.argument; 18 | 19 | import android.support.annotation.NonNull; 20 | 21 | import com.veniosg.dir.mvvm.model.storage.operation.FileOperation; 22 | 23 | import java.io.File; 24 | 25 | public class CreateDirectoryArguments extends FileOperation.Arguments { 26 | private CreateDirectoryArguments(@NonNull File newDirectory) { 27 | super(newDirectory); 28 | } 29 | 30 | public static CreateDirectoryArguments createDirectoryArguments(@NonNull File newDirectory) { 31 | return new CreateDirectoryArguments(newDirectory); 32 | } 33 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_text_input.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 20 | 21 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_paste_two.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_stat_notify_paste.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_logo.xml: -------------------------------------------------------------------------------- 1 | 16 | 22 | 27 | 28 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_stat_notify_compress.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_stat_notify_extract.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/veniosg/dir/test/actor/User.java: -------------------------------------------------------------------------------- 1 | package com.veniosg.dir.test.actor; 2 | 3 | import android.app.Activity; 4 | import android.support.test.rule.ActivityTestRule; 5 | 6 | import com.veniosg.dir.test.actor.action.LaunchesActions; 7 | import com.veniosg.dir.test.actor.action.SelectsActions; 8 | import com.veniosg.dir.test.actor.action.TypesActions; 9 | import com.veniosg.dir.test.actor.assertion.CannotSeeAssertions; 10 | import com.veniosg.dir.test.actor.assertion.SeesAssertions; 11 | 12 | public class User { 13 | private final SeesAssertions sees = new SeesAssertions(); 14 | private final CannotSeeAssertions cannotSeeAssertions = new CannotSeeAssertions(); 15 | private final SelectsActions selects = new SelectsActions(); 16 | private final TypesActions types = new TypesActions(); 17 | private final LaunchesActions launches; 18 | 19 | public User(ActivityTestRule activityRule) { 20 | launches = new LaunchesActions(activityRule); 21 | } 22 | 23 | public SeesAssertions sees() { 24 | return sees; 25 | } 26 | 27 | public SelectsActions selects() { 28 | return selects; 29 | } 30 | 31 | public CannotSeeAssertions cannotSee() { 32 | return cannotSeeAssertions; 33 | } 34 | 35 | public TypesActions types() { 36 | return types; 37 | } 38 | 39 | public LaunchesActions launches() { 40 | return launches; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/mvvm/model/storage/access/StorageAccessManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir.mvvm.model.storage.access; 18 | 19 | import android.support.annotation.NonNull; 20 | 21 | import java.io.File; 22 | 23 | public interface StorageAccessManager { 24 | boolean hasWriteAccess(@NonNull File fileInStorage); 25 | void requestWriteAccess(@NonNull File fileInStorage, @NonNull AccessPermissionListener listener); 26 | boolean isSafBased(); 27 | 28 | interface AccessPermissionListener { 29 | void granted(); 30 | void denied(); 31 | 32 | /** 33 | * Called when the grant succeeded but the file is still not writable. 34 | * This can mean that the grant happened on the wrong directory. 35 | */ 36 | void error(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/android/ui/toast/ToastDisplayer.java: -------------------------------------------------------------------------------- 1 | package com.veniosg.dir.android.ui.toast; 2 | 3 | import android.content.Context; 4 | 5 | import com.veniosg.dir.R; 6 | 7 | import static android.widget.Toast.LENGTH_LONG; 8 | import static android.widget.Toast.LENGTH_SHORT; 9 | import static android.widget.Toast.makeText; 10 | 11 | public class ToastDisplayer { 12 | private Context context; 13 | 14 | public ToastDisplayer(Context context) { 15 | this.context = context; 16 | } 17 | 18 | public void renameSuccess() { 19 | makeText(context, R.string.rename_success, LENGTH_SHORT).show(); 20 | } 21 | 22 | public void renameFailure() { 23 | makeText(context, R.string.rename_failure, LENGTH_SHORT).show(); 24 | } 25 | 26 | public void createDirectorySuccess() { 27 | makeText(context, R.string.create_dir_success, LENGTH_SHORT).show(); 28 | } 29 | 30 | public void createDirectoryFailure() { 31 | makeText(context, R.string.create_dir_failure, LENGTH_SHORT).show(); 32 | } 33 | 34 | public void deleteSuccess() { 35 | makeText(context, R.string.delete_success, LENGTH_SHORT).show(); 36 | } 37 | 38 | public void deleteFailure() { 39 | makeText(context, R.string.delete_failure, LENGTH_SHORT).show(); 40 | } 41 | 42 | public void grantAccessWrongDirectory() { 43 | makeText(context, R.string.select_sd_root, LENGTH_LONG).show(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/fdroid/java/com.veniosg.dir.mvvm.model.iab/FDroidBillingManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.veniosg.dir.mvvm.model.iab; 17 | 18 | import android.app.Activity; 19 | import android.content.Context; 20 | 21 | public class FDroidBillingManager implements BillingManager { 22 | @Override 23 | public void init(Context context, OnPurchasedListener onPurchasedListener, 24 | OnBillingUnavailableListener onUnavailableListener) { 25 | onUnavailableListener.onBillingUnavailable(); 26 | } 27 | 28 | @Override 29 | public boolean hasPurchasedDonation() { 30 | return false; 31 | } 32 | 33 | @Override 34 | public void purchaseDonation(Activity activity) { 35 | } 36 | 37 | @Override 38 | public void consumePurchase(Purchase p, OnConsumedListener onConsumedListener) { 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/mvvm/model/iab/BillingManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.veniosg.dir.mvvm.model.iab; 17 | 18 | import android.app.Activity; 19 | import android.content.Context; 20 | 21 | public interface BillingManager { 22 | void init(Context context, 23 | OnPurchasedListener onPurchasedListener, 24 | OnBillingUnavailableListener onUnavailableListener); 25 | boolean hasPurchasedDonation(); 26 | void purchaseDonation(Activity activity); 27 | void consumePurchase(Purchase p, OnConsumedListener onConsumedListener); 28 | 29 | interface OnPurchasedListener { 30 | void onPurchased(Purchase p); 31 | } 32 | 33 | interface OnConsumedListener { 34 | void onConsumed(); 35 | } 36 | 37 | interface OnBillingUnavailableListener { 38 | void onBillingUnavailable(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_paste_five.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 4dp 18 | 19 | 72dp 20 | 88dp 21 | 22 | 16dp 23 | 72dp 24 | 25 | 24dp 26 | 80dp 27 | 28 | 16dp 29 | 24dp 30 | 31 | 32 | 33 | 34 | 48dp 35 | 48dp 36 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_pref_palette.xml: -------------------------------------------------------------------------------- 1 | 16 | 21 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/mvvm/model/storage/operation/argument/RenameArguments.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir.mvvm.model.storage.operation.argument; 18 | 19 | import android.support.annotation.NonNull; 20 | 21 | import com.veniosg.dir.mvvm.model.storage.operation.FileOperation; 22 | 23 | import java.io.File; 24 | 25 | public class RenameArguments extends FileOperation.Arguments { 26 | @NonNull 27 | private final File fileToRename; 28 | 29 | private RenameArguments(@NonNull File fileToRename, @NonNull String newName) { 30 | super(new File(fileToRename.getParent(), newName)); 31 | this.fileToRename = fileToRename; 32 | } 33 | 34 | public static RenameArguments renameArguments(@NonNull File fileToRename, @NonNull String newName) { 35 | return new RenameArguments(fileToRename, newName); 36 | } 37 | 38 | @NonNull 39 | public File getFileToRename() { 40 | return fileToRename; 41 | } 42 | } -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/mvvm/model/storage/operation/ui/OperationStatusDisplayerInjector.java: -------------------------------------------------------------------------------- 1 | package com.veniosg.dir.mvvm.model.storage.operation.ui; 2 | 3 | import android.content.Context; 4 | 5 | import java.io.File; 6 | 7 | public abstract class OperationStatusDisplayerInjector { 8 | private static final OperationStatusDisplayer NO_OP_DISPLAYER = new OperationStatusDisplayer() { 9 | @Override 10 | public void initChannels() { 11 | } 12 | 13 | @Override 14 | public void showCopySuccess(int operationId, File destDir) { 15 | } 16 | 17 | @Override 18 | public void showCopyFailure(int operationId, File destDir) { 19 | } 20 | 21 | @Override 22 | public void showMoveProgress(int operationId, File destDir, File moving, int progress, int max) { 23 | } 24 | 25 | @Override 26 | public void showMoveSuccess(int operationId, File destDir) { 27 | } 28 | 29 | @Override 30 | public void showMoveFailure(int operationId, File destDir) { 31 | } 32 | 33 | @Override 34 | public void showCopyProgress(int operationId, File destDir, File copying, int progress, int max) { 35 | } 36 | }; 37 | 38 | private OperationStatusDisplayerInjector() { 39 | } 40 | 41 | public static OperationStatusDisplayer operationStatusDisplayer(Context context) { 42 | return new NotificationOperationStatusDisplayer(context); 43 | } 44 | 45 | public static OperationStatusDisplayer noOpStatusDisplayer() { 46 | return NO_OP_DISPLAYER; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/mvvm/model/storage/operation/argument/DeleteArguments.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir.mvvm.model.storage.operation.argument; 18 | 19 | import android.support.annotation.NonNull; 20 | 21 | import com.veniosg.dir.mvvm.model.FileHolder; 22 | import com.veniosg.dir.mvvm.model.storage.operation.FileOperation; 23 | 24 | import java.io.File; 25 | 26 | public class DeleteArguments extends FileOperation.Arguments { 27 | @NonNull 28 | private final FileHolder[] victims; 29 | 30 | private DeleteArguments(@NonNull File parentDirectory, @NonNull FileHolder... victims) { 31 | super(parentDirectory); 32 | this.victims = victims; 33 | } 34 | 35 | public static DeleteArguments deleteArgs(@NonNull File parentDirectory, @NonNull FileHolder... victims) { 36 | return new DeleteArguments(parentDirectory, victims); 37 | } 38 | 39 | @NonNull 40 | public FileHolder[] getVictims() { 41 | return victims; 42 | } 43 | } -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/mvvm/model/storage/operation/argument/ExtractArguments.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir.mvvm.model.storage.operation.argument; 18 | 19 | import android.support.annotation.NonNull; 20 | 21 | import com.veniosg.dir.mvvm.model.FileHolder; 22 | import com.veniosg.dir.mvvm.model.storage.operation.FileOperation; 23 | 24 | import java.io.File; 25 | import java.util.List; 26 | 27 | public class ExtractArguments extends FileOperation.Arguments { 28 | @NonNull 29 | private final List zipFiles; 30 | 31 | private ExtractArguments(@NonNull File target, @NonNull List zipFiles) { 32 | super(target); 33 | this.zipFiles = zipFiles; 34 | } 35 | 36 | public static ExtractArguments extractArgs(@NonNull File target, @NonNull List zipFiles) { 37 | return new ExtractArguments(target, zipFiles); 38 | } 39 | 40 | @NonNull 41 | public List getZipFiles() { 42 | return zipFiles; 43 | } 44 | } -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/android/ui/AnimatorSynchroniser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir.android.ui; 18 | 19 | import android.animation.Animator; 20 | import android.animation.AnimatorSet; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | public class AnimatorSynchroniser { 26 | private static final int DEFAULT_NUM_COMPONENTS_TO_SYNCHRONISE = 2; 27 | 28 | private List mAnimators = new ArrayList<>(DEFAULT_NUM_COMPONENTS_TO_SYNCHRONISE); 29 | 30 | /** 31 | * Add an animation and fire it as soon as the number of waiting animators reaches the max. 32 | */ 33 | public synchronized void addWaitingAnimation(Animator anim) { 34 | mAnimators.add(anim); 35 | 36 | if (mAnimators.size() >= DEFAULT_NUM_COMPONENTS_TO_SYNCHRONISE) { 37 | AnimatorSet animSet = new AnimatorSet(); 38 | animSet.playTogether(mAnimators); 39 | animSet.start(); 40 | 41 | mAnimators.clear(); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/mvvm/model/storage/operation/argument/CompressArguments.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir.mvvm.model.storage.operation.argument; 18 | 19 | import android.support.annotation.NonNull; 20 | 21 | import com.veniosg.dir.mvvm.model.FileHolder; 22 | import com.veniosg.dir.mvvm.model.storage.operation.FileOperation; 23 | 24 | import java.io.File; 25 | import java.util.List; 26 | 27 | public class CompressArguments extends FileOperation.Arguments { 28 | @NonNull 29 | private final List toCompress; 30 | 31 | private CompressArguments(@NonNull File target, @NonNull List toCompress) { 32 | super(target); 33 | this.toCompress = toCompress; 34 | } 35 | 36 | public static CompressArguments compressArgs(@NonNull File target, @NonNull List toCompress) { 37 | return new CompressArguments(target, toCompress); 38 | } 39 | 40 | @NonNull 41 | public List getToCompress() { 42 | return toCompress; 43 | } 44 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/veniosg/dir/test/actor/assertion/LaunchedAssertions.java: -------------------------------------------------------------------------------- 1 | package com.veniosg.dir.test.actor.assertion; 2 | 3 | import android.content.ComponentName; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | 7 | import com.veniosg.dir.android.activity.SearchActivity; 8 | import com.veniosg.dir.android.util.FileUtils; 9 | import com.veniosg.dir.mvvm.model.FileHolder; 10 | 11 | import java.io.File; 12 | 13 | import static android.content.Intent.ACTION_SEARCH; 14 | import static android.support.test.espresso.intent.Intents.intended; 15 | import static android.support.test.espresso.intent.matcher.IntentMatchers.hasAction; 16 | import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent; 17 | import static android.support.test.espresso.intent.matcher.IntentMatchers.hasData; 18 | import static android.support.test.espresso.intent.matcher.IntentMatchers.hasType; 19 | import static org.hamcrest.Matchers.any; 20 | import static org.hamcrest.core.AllOf.allOf; 21 | 22 | public class LaunchedAssertions { 23 | public void viewFileIntent(File file) { 24 | intended(allOf( 25 | hasAction(Intent.ACTION_VIEW), 26 | hasData(FileUtils.getUri(new FileHolder(file, null, null))), 27 | hasType(any(String.class)) 28 | )); 29 | } 30 | 31 | public void searchIntentFor(File file) { 32 | intended(allOf( 33 | hasAction(ACTION_SEARCH), 34 | hasComponent(new ComponentName("com.veniosg.dir", SearchActivity.class.getName())), 35 | hasData(new Uri.Builder().path(file.getAbsolutePath()).build()) 36 | )); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/mvvm/model/storage/access/ExternalStorageAccessManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir.mvvm.model.storage.access; 18 | 19 | import android.content.Context; 20 | import android.support.annotation.NonNull; 21 | 22 | import java.io.File; 23 | 24 | public class ExternalStorageAccessManager implements StorageAccessManager { 25 | private final StorageAccessManager delegate; 26 | 27 | public ExternalStorageAccessManager(Context context) { 28 | delegate = new SafStorageAccessManager(context); 29 | } 30 | 31 | @Override 32 | public boolean hasWriteAccess(@NonNull File fileInStorage) { 33 | return delegate.hasWriteAccess(fileInStorage); 34 | } 35 | 36 | @Override 37 | public void requestWriteAccess(@NonNull File fileInStorage, @NonNull AccessPermissionListener listener) { 38 | delegate.requestWriteAccess(fileInStorage, listener); 39 | } 40 | 41 | @Override 42 | public boolean isSafBased() { 43 | return delegate.isSafBased(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/mvvm/model/storage/operation/argument/MoveArguments.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir.mvvm.model.storage.operation.argument; 18 | 19 | import android.support.annotation.NonNull; 20 | 21 | import com.veniosg.dir.mvvm.model.FileHolder; 22 | import com.veniosg.dir.mvvm.model.storage.operation.FileOperation; 23 | 24 | import java.io.File; 25 | import java.util.List; 26 | 27 | import static java.util.Collections.unmodifiableList; 28 | 29 | public class MoveArguments extends FileOperation.Arguments { 30 | @NonNull 31 | private final List filesToMove; 32 | 33 | private MoveArguments(@NonNull List toMove, @NonNull File to) { 34 | super(to); 35 | this.filesToMove = toMove; 36 | } 37 | 38 | public static MoveArguments moveArgs(List toMove, File to) { 39 | return new MoveArguments(toMove, to); 40 | } 41 | 42 | @NonNull 43 | public List getFilesToMove() { 44 | return unmodifiableList(filesToMove); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_paste.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/mvvm/model/storage/operation/argument/CopyArguments.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir.mvvm.model.storage.operation.argument; 18 | 19 | import android.support.annotation.NonNull; 20 | 21 | import com.veniosg.dir.mvvm.model.FileHolder; 22 | import com.veniosg.dir.mvvm.model.storage.operation.FileOperation; 23 | 24 | import java.io.File; 25 | import java.util.List; 26 | 27 | import static java.util.Collections.unmodifiableList; 28 | 29 | public class CopyArguments extends FileOperation.Arguments { 30 | @NonNull 31 | private final List filesToCopy; 32 | 33 | private CopyArguments(@NonNull List toCopy, @NonNull File to) { 34 | super(to); 35 | this.filesToCopy = toCopy; 36 | } 37 | 38 | public static CopyArguments copyArgs(@NonNull List toCopy, @NonNull File to) { 39 | return new CopyArguments(toCopy, to); 40 | } 41 | 42 | @NonNull 43 | public List getFilesToCopy() { 44 | return unmodifiableList(filesToCopy); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings_not_for_translation.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | Dir 19 | 20 | 21 | 1 22 | 2 23 | 3 24 | 25 | 26 | 0 27 | 1 28 | 2 29 | 30 | 31 | @string/preference_sortby_name 32 | @string/preference_sortby_size 33 | @string/preference_sortby_last_modified 34 | 35 | 36 | @string/app_name 37 | @string/preference_theme_grayscale 38 | @string/preference_theme_dark 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_filemanager.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 21 | 22 | 26 | 27 | 28 | 29 | 33 | 34 | 35 | 42 | 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/android/dialog/OverwriteFileDialog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir.android.dialog; 18 | 19 | import android.app.AlertDialog; 20 | import android.app.Dialog; 21 | import android.content.DialogInterface; 22 | import android.os.Bundle; 23 | import android.support.annotation.NonNull; 24 | import android.support.v4.app.DialogFragment; 25 | 26 | import com.veniosg.dir.R; 27 | 28 | public class OverwriteFileDialog extends DialogFragment { 29 | @NonNull 30 | @Override 31 | public Dialog onCreateDialog(Bundle savedInstanceState) { 32 | return new AlertDialog.Builder(getActivity()) 33 | .setTitle(R.string.file_exists) 34 | .setMessage(R.string.overwrite_question) 35 | .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { 36 | @Override 37 | public void onClick(DialogInterface dialog, int which) { 38 | ((Overwritable) getTargetFragment()).overwrite(); 39 | } 40 | }) 41 | .setNegativeButton(R.string.no, null) 42 | .create(); 43 | } 44 | 45 | public interface Overwritable { 46 | void overwrite(); 47 | } 48 | } -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/mvvm/model/storage/access/SdaStorageAccessManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir.mvvm.model.storage.access; 18 | 19 | import android.content.Context; 20 | import android.support.annotation.NonNull; 21 | 22 | import java.io.File; 23 | 24 | /** 25 | * Uses the Scoped Directory Access to handle external storage permissions. 26 | * This provides a much improved UX and interface compared to the Storage Access Framework. 27 | */ 28 | class SdaStorageAccessManager implements StorageAccessManager { 29 | // To be implemented as part of #77 30 | private final Context context; 31 | 32 | SdaStorageAccessManager(Context context) { 33 | this.context = context.getApplicationContext(); 34 | } 35 | 36 | @Override 37 | public boolean hasWriteAccess(@NonNull File fileInStorage) { 38 | return false; 39 | } 40 | 41 | @Override 42 | public void requestWriteAccess(@NonNull File fileInStorage, @NonNull AccessPermissionListener listener) { 43 | 44 | } 45 | 46 | @Override 47 | public boolean isSafBased() { 48 | return false; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_paste_six.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_filelist_simple.xml: -------------------------------------------------------------------------------- 1 | 16 | 21 | 22 | 29 | 30 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_paste_nine.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_navbar_edit.xml: -------------------------------------------------------------------------------- 1 | 16 | 23 | 27 | 31 | 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/android/dialog/DeleteAsyncTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir.android.dialog; 18 | 19 | import android.content.Context; 20 | import android.os.AsyncTask; 21 | import android.support.annotation.NonNull; 22 | 23 | import com.veniosg.dir.mvvm.model.FileHolder; 24 | import com.veniosg.dir.mvvm.model.storage.operation.DeleteOperation; 25 | 26 | import static com.veniosg.dir.mvvm.model.storage.operation.FileOperationRunnerInjector.operationRunner; 27 | import static com.veniosg.dir.mvvm.model.storage.operation.argument.DeleteArguments.deleteArgs; 28 | 29 | class DeleteAsyncTask extends AsyncTask { 30 | private final Context context; 31 | 32 | DeleteAsyncTask(@NonNull Context context) { 33 | this.context = context; 34 | } 35 | 36 | @Override 37 | protected void onPreExecute() { 38 | } 39 | 40 | @Override 41 | protected Void doInBackground(FileHolder... params) { 42 | operationRunner(context).run(new DeleteOperation(context), deleteArgs(params[0].getFile().getParentFile(), params)); 43 | return null; 44 | } 45 | 46 | @Override 47 | protected void onPostExecute(Void ignored) { 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_paste_three.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sidenav_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 23 | 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/android/activity/PreferenceActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008 OpenIntents.org 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir.android.activity; 18 | 19 | import android.os.Bundle; 20 | import android.view.MenuItem; 21 | 22 | import com.veniosg.dir.R; 23 | import com.veniosg.dir.android.fragment.PreferenceFragment; 24 | import com.veniosg.dir.android.util.Utils; 25 | 26 | public class PreferenceActivity extends BaseActivity { 27 | private PreferenceFragment mFragment; 28 | 29 | @SuppressWarnings("ConstantConditions") 30 | @Override 31 | protected void onCreate(Bundle icicle) { 32 | super.onCreate(icicle); 33 | 34 | setContentView(R.layout.activity_generic); 35 | setupToolbar(); 36 | 37 | mFragment = (PreferenceFragment) getFragmentManager().findFragmentByTag(FRAGMENT_TAG); 38 | if(mFragment == null){ 39 | mFragment = new PreferenceFragment(); 40 | getFragmentManager() 41 | .beginTransaction() 42 | .add(R.id.fragment, mFragment, FRAGMENT_TAG) 43 | .commit(); 44 | } 45 | } 46 | 47 | @Override 48 | public boolean onOptionsItemSelected(MenuItem item) { 49 | switch (item.getItemId()) { 50 | case android.R.id.home: 51 | Utils.showHome(this); 52 | return true; 53 | default: 54 | return super.onOptionsItemSelected(item); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/veniosg/dir/test/actor/action/TypesActions.java: -------------------------------------------------------------------------------- 1 | package com.veniosg.dir.test.actor.action; 2 | 3 | import android.widget.EditText; 4 | 5 | import com.veniosg.dir.R; 6 | 7 | import static android.support.test.espresso.Espresso.onView; 8 | import static android.support.test.espresso.action.ViewActions.clearText; 9 | import static android.support.test.espresso.action.ViewActions.pressImeActionButton; 10 | import static android.support.test.espresso.action.ViewActions.typeText; 11 | import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; 12 | import static android.support.test.espresso.matcher.ViewMatchers.withClassName; 13 | import static android.support.test.espresso.matcher.ViewMatchers.withId; 14 | import static android.support.test.espresso.matcher.ViewMatchers.withParent; 15 | import static org.hamcrest.Matchers.allOf; 16 | import static org.hamcrest.Matchers.equalTo; 17 | 18 | public class TypesActions { 19 | public void pickFileName(String fileName) { 20 | onView(allOf( 21 | withParent(withId(R.id.pickBar)), 22 | withClassName(equalTo(EditText.class.getName())), 23 | isDisplayed() 24 | )).perform(typeText(fileName)); 25 | } 26 | 27 | public void inputFileName(String fileName) { 28 | onView(allOf( 29 | withId(R.id.textinput), 30 | isDisplayed() 31 | )).perform(typeText(fileName)); 32 | } 33 | 34 | public void searchQuery(String query) { 35 | onView(allOf( 36 | withId(R.id.searchQuery), 37 | isDisplayed() 38 | )).perform(typeText(query)); 39 | } 40 | 41 | public void noSearchQuery() { 42 | onView(allOf( 43 | withId(R.id.searchQuery), 44 | isDisplayed() 45 | )).perform(clearText()); 46 | } 47 | 48 | public void imeAction() { 49 | onView(allOf( 50 | withId(R.id.searchQuery), 51 | isDisplayed() 52 | )).perform(pressImeActionButton()); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/android/dialog/SingleDeleteDialog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir.android.dialog; 18 | 19 | import android.app.AlertDialog; 20 | import android.app.Dialog; 21 | import android.os.Bundle; 22 | import android.support.annotation.NonNull; 23 | import android.support.v4.app.DialogFragment; 24 | 25 | import com.veniosg.dir.R; 26 | import com.veniosg.dir.mvvm.model.FileHolder; 27 | 28 | import static com.veniosg.dir.IntentConstants.EXTRA_DIALOG_FILE_HOLDER; 29 | 30 | public class SingleDeleteDialog extends DialogFragment { 31 | @NonNull 32 | @Override 33 | public Dialog onCreateDialog(Bundle savedInstanceState) { 34 | FileHolder holder = getArguments().getParcelable(EXTRA_DIALOG_FILE_HOLDER); 35 | 36 | if (holder != null) { 37 | AlertDialog dialog = new AlertDialog.Builder(getActivity()) 38 | .setTitle(getString(R.string.really_delete, holder.getName())) 39 | .setPositiveButton(R.string.yes, (dialog1, which) -> { 40 | new DeleteAsyncTask(getContext()).execute(holder); 41 | }) 42 | .setNegativeButton(R.string.no, null) 43 | .create(); 44 | dialog.setIcon(R.drawable.ic_dialog_delete); 45 | return dialog; 46 | } else { 47 | dismiss(); 48 | return new Dialog(getContext()); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > This project is no longer maintained. 2 | 3 | Dir 4 | =========== 5 | 6 | http://veniosg.github.io/Dir/ 7 | 8 | A modern Android file manager. 9 | 10 | [Get it on Google Play](https://play.google.com/store/apps/details?id=com.veniosg.dir) 11 | [Get it on F-Droid](https://f-droid.org/packages/com.veniosg.dir/) 12 | 13 | Contributors 14 | ---- 15 | Dir welcomes contributions from developers of all experience levels! 16 | 17 | A good place to start is [this list of issues](https://github.com/veniosg/Dir/labels/good%20first%20issue). Much more interesting (and harder) are [issues marked as high effort](https://github.com/veniosg/Dir/labels/higheffort). 18 | 19 | For direction, help and coordination feel free to comment on the issues themselves or send an email to [dev@pxhouse.co](mailto:dev@pxhouse.co). If you're planning to build a completely new feature please open an issue before starting to ensure that your efforts are not wasted. 20 | 21 | [Visit our Wiki](https://github.com/veniosg/Dir/wiki) for technical documentation. 22 | 23 | Not a developer? 24 | ---- 25 | [Join us on OneSky](http://dirapp.oneskyapp.com/collaboration/project?id=27347) and help improve Dir for your language! 26 | 27 | License 28 | -------- 29 | 30 | Copyright (C) 2014-2018 George Venios 31 | Copyright (C) 2007-2014 OpenIntents.org 32 | 33 | Licensed under the Apache License, Version 2.0 (the "License"); 34 | you may not use this file except in compliance with the License. 35 | You may obtain a copy of the License at 36 | 37 | http://www.apache.org/licenses/LICENSE-2.0 38 | 39 | Unless required by applicable law or agreed to in writing, software 40 | distributed under the License is distributed on an "AS IS" BASIS, 41 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 42 | See the License for the specific language governing permissions and 43 | limitations under the License. 44 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/mvvm/viewmodel/search/SearchViewModel.java: -------------------------------------------------------------------------------- 1 | package com.veniosg.dir.mvvm.viewmodel.search; 2 | 3 | import android.arch.lifecycle.LiveData; 4 | import android.arch.lifecycle.ViewModel; 5 | import android.support.annotation.NonNull; 6 | import android.support.annotation.VisibleForTesting; 7 | 8 | import com.veniosg.dir.mvvm.model.search.SearchState; 9 | import com.veniosg.dir.mvvm.model.search.Searcher; 10 | 11 | import java.io.File; 12 | 13 | import static com.veniosg.dir.mvvm.model.search.Searcher.SearchRequest.searchRequest; 14 | 15 | public class SearchViewModel extends ViewModel { 16 | private Searcher searcher; 17 | private LiveData liveResults; 18 | private File searchRoot; 19 | private String currentQuery; 20 | 21 | @SuppressWarnings("unused") 22 | public SearchViewModel() { 23 | searcher = new Searcher(); 24 | } 25 | 26 | @VisibleForTesting 27 | SearchViewModel(Searcher searcher) { 28 | this.searcher = searcher; 29 | } 30 | 31 | public void init(File searchIn) { 32 | if (liveResults != null) return; 33 | 34 | this.searchRoot = searchIn; 35 | liveResults = searcher.getResults(); 36 | } 37 | 38 | /** 39 | * Start a new search, using the passed query. If the current search uses the same query, this does nothing. 40 | * @param query The text to search filenames for. 41 | * @return True if this triggers a new search, false if not. 42 | */ 43 | public boolean updateQuery(@NonNull String query) { 44 | if (!query.equals(currentQuery)) { 45 | searcher.updateQuery(searchRequest(searchRoot, query)); 46 | currentQuery = query; 47 | return true; 48 | } else { 49 | return false; 50 | } 51 | } 52 | 53 | @Override 54 | protected void onCleared() { 55 | super.onCleared(); 56 | // Avoid stray search workers 57 | searcher.stopSearch(); 58 | } 59 | 60 | public LiveData getLiveResults() { 61 | return liveResults; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/android/util/Logger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir.android.util; 18 | 19 | import android.util.Log; 20 | 21 | import com.veniosg.dir.BuildConfig; 22 | 23 | public class Logger { 24 | private static final boolean LOG_ENABLED = BuildConfig.DEBUG; 25 | 26 | private static final String TAG_DEFAULT = "DIR_Default"; 27 | public static final String TAG_OBSERVER = "DIR_FileObserver"; 28 | public static final String TAG_DIRSCANNER = "DIR_DirectoryScanner"; 29 | public static final String TAG_MEDIASCANNER = "DIR_MediaScanner"; 30 | public static final String TAG_PATHBAR = "DIR_PathBar"; 31 | public static final String TAG_ANIMATION = "DIR_Animation"; 32 | public static final String TAG_SEARCH = "DIR_Search"; 33 | public static final String TAG_BILLING = "DIR_Billing"; 34 | 35 | private Logger(){} 36 | 37 | public static void log(Throwable t) { 38 | if (LOG_ENABLED) { 39 | t.printStackTrace(); 40 | } 41 | } 42 | 43 | public static void log(String msg) { 44 | logV(TAG_DEFAULT, msg); 45 | } 46 | 47 | public static void log(int priority, String msg) { 48 | log(priority, TAG_DEFAULT, msg); 49 | } 50 | 51 | public static void logV(String tag, String msg) { 52 | log(Log.VERBOSE, tag, msg); 53 | } 54 | 55 | public static void log(int priority, String tag, String msg) { 56 | if (LOG_ENABLED) { 57 | Log.println(priority, tag, msg); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_paste_eight.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/android/dialog/MultiDeleteDialog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir.android.dialog; 18 | 19 | import android.app.AlertDialog; 20 | import android.app.Dialog; 21 | import android.os.Bundle; 22 | import android.support.annotation.NonNull; 23 | import android.support.v4.app.DialogFragment; 24 | 25 | import com.veniosg.dir.R; 26 | import com.veniosg.dir.mvvm.model.FileHolder; 27 | 28 | import java.util.ArrayList; 29 | 30 | import static com.veniosg.dir.IntentConstants.EXTRA_DIALOG_FILE_HOLDER; 31 | 32 | public class MultiDeleteDialog extends DialogFragment { 33 | @NonNull 34 | @Override 35 | public Dialog onCreateDialog(Bundle savedInstanceState) { 36 | ArrayList holders = getArguments().getParcelableArrayList(EXTRA_DIALOG_FILE_HOLDER); 37 | 38 | if (holders != null) { 39 | AlertDialog dialog = new AlertDialog.Builder(getActivity()) 40 | .setTitle(getString(R.string.really_delete_multiselect, holders.size())) 41 | .setPositiveButton(R.string.yes, (dialog1, which) -> { 42 | FileHolder[] params = holders.toArray(new FileHolder[holders.size()]); 43 | new DeleteAsyncTask(getContext()).execute(params); 44 | }) 45 | .setNegativeButton(R.string.no, null) 46 | .create(); 47 | dialog.setIcon(R.drawable.ic_dialog_delete); 48 | return dialog; 49 | } else { 50 | dismiss(); 51 | return new Dialog(getContext()); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/veniosg/dir/test/acceptance/InternalStorageOperationsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir.test.acceptance; 18 | 19 | import android.content.Context; 20 | import android.os.Environment; 21 | 22 | import java.io.File; 23 | import java.io.IOException; 24 | 25 | import static com.veniosg.dir.test.TestUtils.cleanDirectory; 26 | 27 | public class InternalStorageOperationsTest extends FileOperationsTest { 28 | @Override 29 | protected File getStorageRoot(Context context) { 30 | return Environment.getExternalStorageDirectory(); 31 | } 32 | 33 | @SuppressWarnings("ResultOfMethodCallIgnored") 34 | @Override 35 | protected void setUpFiles(Context context, File testDirectory, File testChildDirectory, 36 | File testCopyDestination, File testChildFile) throws IOException { 37 | testDirectory.mkdir(); 38 | testChildDirectory.mkdir(); 39 | testCopyDestination.mkdir(); 40 | testChildFile.createNewFile(); 41 | } 42 | 43 | @SuppressWarnings("ResultOfMethodCallIgnored") 44 | @Override 45 | protected void tearDownFiles(Context context, File testExtractedDirectory, File testDirectory, File compressedFile) { 46 | if (testExtractedDirectory.exists()) { 47 | testExtractedDirectory.renameTo(new File(testDirectory, "extracted")); 48 | } 49 | if (compressedFile.exists()) { 50 | compressedFile.renameTo(new File(testDirectory, "compressed")); 51 | } 52 | cleanDirectory(testDirectory); 53 | testDirectory.delete(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_action_paste_more.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/android/adapter/FileListViewHolder.java: -------------------------------------------------------------------------------- 1 | package com.veniosg.dir.android.adapter; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.ImageView; 8 | import android.widget.TextView; 9 | 10 | import com.veniosg.dir.R; 11 | import com.veniosg.dir.mvvm.model.FileHolder; 12 | 13 | import java.io.File; 14 | 15 | import static android.view.LayoutInflater.from; 16 | import static com.veniosg.dir.android.misc.ThumbnailHelper.requestIcon; 17 | import static com.veniosg.dir.android.ui.Themer.getThemedResourceId; 18 | 19 | public class FileListViewHolder extends RecyclerView.ViewHolder { 20 | private ImageView icon; 21 | private TextView primaryInfo; 22 | TextView secondaryInfo; 23 | private TextView tertiaryInfo; 24 | 25 | FileListViewHolder(ViewGroup parent) { 26 | super(from(parent.getContext()).inflate(R.layout.item_filelist, parent, false)); 27 | 28 | icon = (ImageView) itemView.findViewById(R.id.icon); 29 | primaryInfo = (TextView) itemView.findViewById(R.id.primary_info); 30 | secondaryInfo = (TextView) itemView.findViewById(R.id.secondary_info); 31 | tertiaryInfo = (TextView) itemView.findViewById(R.id.tertiary_info); 32 | 33 | int selectorRes = getThemedResourceId(parent.getContext(), android.R.attr.listChoiceBackgroundIndicator); 34 | itemView.setBackgroundResource(selectorRes); 35 | } 36 | 37 | void bind(String filePath, OnItemClickListener listener) { 38 | Context context = itemView.getContext(); 39 | FileHolder item = new FileHolder(new File(filePath), context); 40 | boolean isDirectory = item.getFile().isDirectory(); 41 | 42 | primaryInfo.setText(item.getName()); 43 | secondaryInfo.setText(item.getFormattedModificationDate(context)); 44 | tertiaryInfo.setText(isDirectory ? "" : item.getFormattedSize(context, false)); 45 | icon.setImageDrawable(item.getBestIcon()); 46 | requestIcon(item, icon); 47 | 48 | itemView.setOnClickListener(view -> listener.onClick(itemView, item)); 49 | } 50 | 51 | public interface OnItemClickListener { 52 | public void onClick(View itemView, FileHolder item); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_about_github.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | #8888 22 | 23 | 24 | 25 | 26 | #8e24aa 27 | #6a1b9a 28 | #7b1fa2 29 | #00BFA5 30 | #EEEEEE 31 | #424242 32 | #212121 33 | #E0E0E0 34 | #757575 35 | 36 | #BB222222 37 | 38 | 39 | 40 | #000 41 | #181818 42 | 43 | 44 | 45 | 46 | #212121 47 | #000 48 | #424242 49 | #f5f5f5 50 | #303030 51 | #424242 52 | #212121 53 | #2B2B2B 54 | #9E9E9E 55 | 56 | #B000 57 | 58 | -------------------------------------------------------------------------------- /app/src/main/res/menu/cab_multi.xml: -------------------------------------------------------------------------------- 1 | 16 | 19 | 23 | 27 | 28 | 29 | 33 | 37 | 41 | 45 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/android/activity/SearchActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2017 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir.android.activity; 18 | 19 | import android.content.Intent; 20 | import android.os.Bundle; 21 | import android.view.MenuItem; 22 | 23 | import com.veniosg.dir.android.fragment.SearchListFragment; 24 | 25 | import static com.veniosg.dir.android.util.Utils.showHome; 26 | 27 | public class SearchActivity extends BaseActivity { 28 | private SearchListFragment mFragment; 29 | 30 | @Override 31 | protected void onNewIntent(Intent intent) { 32 | setIntent(intent); 33 | handleRequest(); 34 | } 35 | 36 | protected void onCreate(Bundle savedInstanceState) { 37 | // Presentation settings 38 | super.onCreate(savedInstanceState); 39 | 40 | setupToolbar(); 41 | 42 | // Handle the search request. 43 | handleRequest(); 44 | } 45 | 46 | private void handleRequest() { 47 | // Add fragment only if it hasn't already been added. 48 | mFragment = (SearchListFragment) getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG); 49 | if (mFragment == null) { 50 | mFragment = new SearchListFragment(); 51 | getSupportFragmentManager() 52 | .beginTransaction() 53 | .replace(android.R.id.content, mFragment, FRAGMENT_TAG) 54 | .commit(); 55 | } 56 | } 57 | 58 | @Override 59 | public boolean onOptionsItemSelected(MenuItem item) { 60 | switch (item.getItemId()) { 61 | case android.R.id.home: 62 | showHome(this); 63 | break; 64 | } 65 | return super.onOptionsItemSelected(item); 66 | } 67 | } -------------------------------------------------------------------------------- /app/src/main/res/anim/state_list_anim_material.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 17 | 18 | 19 | 23 | 27 | 28 | 29 | 30 | 31 | 32 | 37 | 41 | 42 | 43 | 44 | 45 | 49 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 22 | 25 | 28 | 29 | 33 | 34 | 35 | 40 | 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/mvvm/model/storage/operation/FileOperation.java: -------------------------------------------------------------------------------- 1 | package com.veniosg.dir.mvvm.model.storage.operation; 2 | 3 | import android.support.annotation.NonNull; 4 | 5 | import java.io.File; 6 | 7 | import static java.util.UUID.randomUUID; 8 | 9 | public abstract class FileOperation { 10 | protected final int id = randomUUID().hashCode(); 11 | 12 | /** 13 | * @return Whether the operation was successful. 14 | */ 15 | public abstract boolean operate(A args); 16 | 17 | /** 18 | * Try the operation using SAF facilities 19 | * Triggered if {@link #operate(Arguments)} returns false and we have write permissions. 20 | * 21 | * @return Whether the operation was successful. 22 | */ 23 | public abstract boolean operateSaf(A args); 24 | 25 | /** 26 | * Good place to show initial UI, or prepare any dialogs etc. 27 | * Called right before running the operation. Can be called multiple times. 28 | * 29 | * @param args Original arguments for the invocation that is getting started. 30 | */ 31 | abstract void onStartOperation(A args); 32 | 33 | /** 34 | * Good place to show final result (success/failure) UI. 35 | * No other callbacks will happen after this. 36 | * 37 | * @param success Whether the invocation was successful. 38 | * @param args Original arguments for the invocation that just finished. 39 | */ 40 | abstract void onResult(boolean success, A args); 41 | 42 | /** 43 | * Called if the user has denied storage write permissions on the parent volume of {@link Arguments#target}. 44 | * No other callbacks will happen after this. 45 | */ 46 | abstract void onAccessDenied(); 47 | 48 | /** 49 | * Good place to hide any progress UI. You may still get calls to onResult()/onAccessDenied() after this. 50 | */ 51 | abstract void onRequestingAccess(); 52 | 53 | /** 54 | * @return Whether this type of operation can fail because of lack of storage write permissions. 55 | */ 56 | public abstract boolean needsWriteAccess(); 57 | 58 | public static abstract class Arguments { 59 | @NonNull 60 | private final File target; 61 | 62 | protected Arguments(@NonNull File target) { 63 | this.target = target; 64 | } 65 | 66 | @NonNull 67 | protected File getTarget() { 68 | return target; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/android/ui/widget/WaitingViewFlipper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2015 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir.android.ui.widget; 18 | 19 | import android.content.Context; 20 | import android.os.Handler; 21 | import android.util.AttributeSet; 22 | import android.widget.ViewFlipper; 23 | 24 | import static com.veniosg.dir.AnimationConstants.ANIM_START_DELAY; 25 | 26 | /** 27 | * Children must always follow the PAGE_INDEX_* indexing. 28 | */ 29 | public class WaitingViewFlipper extends ViewFlipper { 30 | public static final int PAGE_INDEX_CONTENT = 0; 31 | public static final int PAGE_INDEX_LOADING = 1; 32 | public static final int PAGE_INDEX_PERMISSION_DENIED = 2; 33 | 34 | private Handler mWaiterHandler = new Handler(); 35 | private Runnable mWaiter; 36 | private int mWaitingChild = -1; 37 | 38 | public WaitingViewFlipper(Context context) { 39 | super(context); 40 | } 41 | 42 | public WaitingViewFlipper(Context context, AttributeSet attrs) { 43 | super(context, attrs); 44 | } 45 | 46 | public void setDisplayedChildDelayed(final int child) { 47 | if (mWaiter != null) { 48 | mWaiterHandler.removeCallbacks(mWaiter); 49 | } 50 | 51 | mWaiter = new Runnable() { 52 | @Override 53 | public void run() { 54 | WaitingViewFlipper.this.setDisplayedChild(child); 55 | } 56 | }; 57 | mWaiterHandler.postDelayed(mWaiter, ANIM_START_DELAY); 58 | mWaitingChild = child; 59 | } 60 | 61 | public void setDisplayedChild(int whichChild) { 62 | if (mWaitingChild != -1) { 63 | mWaitingChild = -1; 64 | mWaiterHandler.removeCallbacks(mWaiter); 65 | } 66 | 67 | if (getDisplayedChild() != whichChild) 68 | super.setDisplayedChild(whichChild); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/veniosg/dir/test/acceptance/SdCardOperationsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir.test.acceptance; 18 | 19 | import android.content.Context; 20 | 21 | import com.veniosg.dir.android.util.FileUtils; 22 | 23 | import java.io.File; 24 | import java.io.IOException; 25 | import java.util.List; 26 | 27 | import static com.veniosg.dir.mvvm.model.storage.DocumentFileUtils.createDirectory; 28 | import static com.veniosg.dir.mvvm.model.storage.DocumentFileUtils.createFile; 29 | import static com.veniosg.dir.mvvm.model.storage.DocumentFileUtils.safAwareDelete; 30 | import static com.veniosg.dir.test.TestUtils.cleanDirectorySaf; 31 | 32 | public class SdCardOperationsTest extends FileOperationsTest { 33 | @Override 34 | protected File getStorageRoot(Context context) { 35 | List extPaths = FileUtils.getExtSdCardPaths(context); 36 | if (extPaths.isEmpty()) { 37 | throw new IllegalStateException("Can't test SD Card operations without an SD Card attached."); 38 | } 39 | return new File(extPaths.get(0)); 40 | } 41 | 42 | @Override 43 | protected void setUpFiles(Context context, File testDirectory, File testChildDirectory, File testCopyDestination, File testChildFile) throws IOException { 44 | createDirectory(context, testDirectory); 45 | createDirectory(context, testChildDirectory); 46 | createDirectory(context, testCopyDestination); 47 | createFile(context, testChildFile, "*/*"); 48 | } 49 | 50 | @Override 51 | protected void tearDownFiles(Context context, File testExtractedDirectory, File testDirectory, File compressedFile) { 52 | safAwareDelete(context, testExtractedDirectory); 53 | safAwareDelete(context, compressedFile); 54 | cleanDirectorySaf(context, testDirectory); 55 | safAwareDelete(context, testDirectory); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/android/adapter/SearchListAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir.android.adapter; 18 | 19 | import android.support.annotation.NonNull; 20 | import android.support.v7.widget.RecyclerView; 21 | import android.view.ViewGroup; 22 | 23 | import com.veniosg.dir.android.adapter.FileListViewHolder.OnItemClickListener; 24 | 25 | import java.util.List; 26 | 27 | import static com.veniosg.dir.android.util.Utils.firstDifferentItemIndex; 28 | import static java.util.Collections.emptyList; 29 | 30 | public class SearchListAdapter extends RecyclerView.Adapter { 31 | private List data = emptyList(); 32 | private OnItemClickListener onItemClickListener; 33 | 34 | public SearchListAdapter(OnItemClickListener onItemClickListener) { 35 | this.onItemClickListener = onItemClickListener; 36 | } 37 | 38 | public void notifyDataUpdated(@NonNull List updatedData) { 39 | int firstUpdatedIndex = firstDifferentItemIndex(data, updatedData); 40 | if (firstUpdatedIndex != -1) { // Lists are not equal 41 | int oldCount = data.size(); 42 | int newCount = updatedData.size(); 43 | data = updatedData; 44 | 45 | if (firstUpdatedIndex == oldCount) { // Data appended 46 | notifyItemRangeInserted(firstUpdatedIndex, updatedData.size() - oldCount); 47 | } else { 48 | notifyDataSetChanged(); 49 | } 50 | } 51 | } 52 | 53 | @Override 54 | public SearchListViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 55 | return new SearchListViewHolder(parent); 56 | } 57 | 58 | @Override 59 | public void onBindViewHolder(SearchListViewHolder holder, int position) { 60 | holder.bind(data.get(position), onItemClickListener); 61 | } 62 | 63 | @Override 64 | public int getItemCount() { 65 | return data.size(); 66 | } 67 | } -------------------------------------------------------------------------------- /app/src/main/res/menu/options_filelist_simple.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 26 | 33 | 41 | 42 | 49 | 50 | 56 | 57 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/android/ui/widget/PathContainerLayout.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014-2016 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir.android.ui.widget; 18 | 19 | import android.content.Context; 20 | import android.util.AttributeSet; 21 | import android.view.View; 22 | 23 | import static android.view.View.MeasureSpec.AT_MOST; 24 | import static android.view.View.MeasureSpec.UNSPECIFIED; 25 | import static android.view.View.MeasureSpec.makeMeasureSpec; 26 | 27 | public class PathContainerLayout extends ChildrenChangedListeningLinearLayout { 28 | private int mMaxChildWidth = 0; 29 | 30 | public PathContainerLayout(Context context) { 31 | super(context); 32 | } 33 | 34 | public PathContainerLayout(Context context, AttributeSet attrs) { 35 | super(context, attrs); 36 | } 37 | 38 | public PathContainerLayout(Context context, AttributeSet attrs, int defStyleAttr) { 39 | super(context, attrs, defStyleAttr); 40 | } 41 | 42 | void setMeasuredWidth(int width) { 43 | setMeasuredDimension(width, getMeasuredHeight()); 44 | } 45 | 46 | @Override 47 | protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { 48 | int childWidthSpec = makeMeasureSpec(0, UNSPECIFIED); 49 | if (mMaxChildWidth > 0) { 50 | childWidthSpec = makeMeasureSpec(mMaxChildWidth, AT_MOST); 51 | } 52 | 53 | child.measure(childWidthSpec, parentHeightMeasureSpec); 54 | } 55 | 56 | /** 57 | * Set the maximum width that this layout's children are allowed to have. 58 | * 59 | * @param maxChildWidthPx Negative or zero to cancel, positive to set the max width. 60 | */ 61 | @SuppressWarnings("WeakerAccess") 62 | public void setMaxChildWidth(int maxChildWidthPx) { 63 | this.mMaxChildWidth = maxChildWidthPx; 64 | } 65 | 66 | @SuppressWarnings("unused") 67 | public int getMaxChildWidth() { 68 | return mMaxChildWidth; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/src/main/res/xml/mimetypes.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /app/src/test/java/com/veniosg/dir/mvvm/viewmodel/search/SearchViewModelTest.java: -------------------------------------------------------------------------------- 1 | package com.veniosg.dir.mvvm.viewmodel.search; 2 | 3 | import android.arch.lifecycle.LiveData; 4 | import android.arch.lifecycle.MutableLiveData; 5 | 6 | import com.veniosg.dir.mvvm.model.search.SearchState; 7 | import com.veniosg.dir.mvvm.model.search.Searcher; 8 | import com.veniosg.dir.mvvm.viewmodel.search.SearchViewModel; 9 | 10 | import org.junit.Before; 11 | import org.junit.Test; 12 | import org.mockito.Mock; 13 | 14 | import java.io.File; 15 | 16 | import static com.veniosg.dir.mvvm.model.search.Searcher.SearchRequest.searchRequest; 17 | import static junit.framework.Assert.assertEquals; 18 | import static junit.framework.Assert.assertFalse; 19 | import static junit.framework.Assert.assertTrue; 20 | import static org.mockito.ArgumentMatchers.refEq; 21 | import static org.mockito.Mockito.verify; 22 | import static org.mockito.Mockito.when; 23 | import static org.mockito.MockitoAnnotations.initMocks; 24 | 25 | public class SearchViewModelTest { 26 | @Mock 27 | private Searcher mockSearcher; 28 | private SearchViewModel viewModel; 29 | private final File searchRoot = new File("/"); 30 | 31 | @Before 32 | public void setUp() { 33 | initMocks(this); 34 | viewModel = new SearchViewModel(mockSearcher); 35 | } 36 | 37 | @Test 38 | public void delegatesSearchResultsToSearcher() { 39 | LiveData searcherLiveResults = new MutableLiveData<>(); 40 | when(mockSearcher.getResults()).thenReturn(searcherLiveResults); 41 | 42 | viewModel.init(searchRoot); 43 | 44 | assertEquals(searcherLiveResults, viewModel.getLiveResults()); 45 | } 46 | 47 | @Test 48 | public void onClearedCleansUp() { 49 | viewModel.onCleared(); 50 | 51 | verify(mockSearcher).stopSearch(); 52 | } 53 | 54 | @Test 55 | public void updateQueryDelegatesToSearcher() { 56 | String newQuery = "query2"; 57 | viewModel.init(searchRoot); 58 | 59 | viewModel.updateQuery(newQuery); 60 | 61 | verify(mockSearcher).updateQuery(refEq(searchRequest(searchRoot, newQuery))); 62 | } 63 | 64 | @Test 65 | public void onlyStartsOneSearchForSameQuery() { 66 | String query = "query"; 67 | viewModel.init(searchRoot); 68 | 69 | boolean firstSearchStarted = viewModel.updateQuery(query); 70 | boolean secondSearchStarted = viewModel.updateQuery(query); 71 | 72 | assertTrue(firstSearchStarted); 73 | assertFalse(secondSearchStarted); 74 | verify(mockSearcher).updateQuery(refEq(searchRequest(searchRoot, query))); 75 | } 76 | } -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /app/src/main/java/com/veniosg/dir/mvvm/model/storage/operation/CreateDirectoryOperation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 George Venios 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.veniosg.dir.mvvm.model.storage.operation; 18 | 19 | import android.content.Context; 20 | 21 | import com.veniosg.dir.android.fragment.FileListFragment; 22 | import com.veniosg.dir.android.ui.toast.ToastDisplayer; 23 | import com.veniosg.dir.mvvm.model.storage.operation.argument.CreateDirectoryArguments; 24 | 25 | import java.io.File; 26 | 27 | import static com.veniosg.dir.mvvm.model.storage.DocumentFileUtils.createDirectory; 28 | 29 | public class CreateDirectoryOperation extends FileOperation { 30 | private final Context context; 31 | private final ToastDisplayer toastDisplayer; 32 | 33 | public CreateDirectoryOperation(Context context) { 34 | this.context = context; 35 | this.toastDisplayer = new ToastDisplayer(context); 36 | } 37 | 38 | @Override 39 | public boolean operate(CreateDirectoryArguments args) { 40 | File dest = args.getTarget(); 41 | 42 | return dest.exists() || dest.mkdirs(); 43 | } 44 | 45 | @Override 46 | public boolean operateSaf(CreateDirectoryArguments args) { 47 | File dest = args.getTarget(); 48 | 49 | return dest.exists() || createDirectory(context, dest) != null; 50 | } 51 | 52 | @Override 53 | public void onStartOperation(CreateDirectoryArguments args) { 54 | } 55 | 56 | @Override 57 | public void onResult(boolean success, CreateDirectoryArguments args) { 58 | if (success) { 59 | toastDisplayer.createDirectorySuccess(); 60 | FileListFragment.refresh(context, args.getTarget().getParentFile()); 61 | } else { 62 | toastDisplayer.createDirectoryFailure(); 63 | } 64 | } 65 | 66 | @Override 67 | public void onAccessDenied() { 68 | } 69 | 70 | @Override 71 | public void onRequestingAccess() { 72 | } 73 | 74 | @Override 75 | public boolean needsWriteAccess() { 76 | return true; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_filelist_pick.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 20 | 21 | 22 | 29 | 30 | 35 | 36 |