├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── vi │ │ └── filepicker │ │ ├── ApplicationTest.java │ │ ├── MainActivityTest.java │ │ └── RecyclerViewItemCountAssertion.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── vi │ │ │ └── filepicker │ │ │ ├── AppDelegate.java │ │ │ ├── CallerFragment.java │ │ │ ├── FragmentActivity.java │ │ │ ├── ImageAdapter.java │ │ │ └── MainActivity.java │ └── res │ │ ├── drawable-xxhdpi │ │ ├── custom_camera.png │ │ └── pdf_blue.png │ │ ├── layout │ │ ├── activity_fragment.xml │ │ ├── activity_main.xml │ │ ├── fragment_caller.xml │ │ └── item_layout.xml │ │ ├── menu │ │ └── main_menu.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-it │ │ └── strings.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ └── file_paths.xml │ └── test │ └── java │ └── vi │ └── filepicker │ └── ExampleUnitTest.java ├── build.gradle ├── filepicker ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── droidninja │ │ └── filepicker │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── droidninja │ │ │ └── filepicker │ │ │ ├── BaseFilePickerActivity.kt │ │ │ ├── FilePickerActivity.kt │ │ │ ├── FilePickerBuilder.kt │ │ │ ├── FilePickerConst.kt │ │ │ ├── MediaDetailsActivity.kt │ │ │ ├── PickerManager.kt │ │ │ ├── adapters │ │ │ ├── FileAdapterListener.kt │ │ │ ├── FileListAdapter.kt │ │ │ ├── FolderGridAdapter.kt │ │ │ ├── PhotoGridAdapter.kt │ │ │ ├── SectionsPagerAdapter.kt │ │ │ ├── Selectable.kt │ │ │ └── SelectableAdapter.kt │ │ │ ├── fragments │ │ │ ├── BaseFragment.kt │ │ │ ├── DocFragment.kt │ │ │ ├── DocPickerFragment.kt │ │ │ ├── MediaDetailPickerFragment.kt │ │ │ ├── MediaFolderPickerFragment.kt │ │ │ ├── MediaPickerFragment.kt │ │ │ └── PhotoPickerFragmentListener.kt │ │ │ ├── models │ │ │ ├── BaseFile.kt │ │ │ ├── Document.kt │ │ │ ├── FileType.kt │ │ │ ├── Media.kt │ │ │ ├── PhotoDirectory.kt │ │ │ └── sort │ │ │ │ ├── NameComparator.kt │ │ │ │ └── SortingTypes.kt │ │ │ ├── utils │ │ │ ├── AndroidLifecycleUtils.kt │ │ │ ├── ContentUriUtils.kt │ │ │ ├── FilePickerProvider.kt │ │ │ ├── FilePickerUtils.kt │ │ │ ├── FileUtils.kt │ │ │ ├── FragmentUtil.kt │ │ │ ├── GridSpacingItemDecoration.kt │ │ │ ├── ImageCaptureManager.kt │ │ │ └── TabLayoutHelper.java │ │ │ ├── viewmodels │ │ │ ├── BaseViewModel.kt │ │ │ ├── VMDocPicker.kt │ │ │ └── VMMediaPicker.kt │ │ │ └── views │ │ │ ├── SmoothCheckBox.kt │ │ │ └── SquareRelativeLayout.kt │ └── res │ │ ├── anim │ │ ├── slide_left_in.xml │ │ └── slide_left_out.xml │ │ ├── color │ │ ├── selector_tab_text_color.xml │ │ └── selector_tab_text_color_dark.xml │ │ ├── drawable-xxhdpi │ │ ├── gallery_album_overlay.9.png │ │ ├── ic_camera.png │ │ ├── ic_deselect_all.png │ │ ├── ic_play_icon.png │ │ ├── ic_select_all.png │ │ ├── icon_file_doc.png │ │ ├── icon_file_pdf.png │ │ ├── icon_file_ppt.png │ │ ├── icon_file_unknown.png │ │ ├── icon_file_xls.png │ │ └── image_placeholder.png │ │ ├── drawable │ │ └── ic_search.xml │ │ ├── layout │ │ ├── activity_file_picker.xml │ │ ├── activity_media_details.xml │ │ ├── fragment_doc_picker.xml │ │ ├── fragment_media_folder_picker.xml │ │ ├── fragment_media_picker.xml │ │ ├── fragment_photo_picker.xml │ │ ├── item_doc_layout.xml │ │ ├── item_folder_layout.xml │ │ └── item_photo_layout.xml │ │ ├── menu │ │ ├── doc_picker_menu.xml │ │ ├── media_detail_menu.xml │ │ ├── picker_menu.xml │ │ └── select_menu.xml │ │ ├── values-es │ │ └── strings.xml │ │ ├── values-fa │ │ └── strings.xml │ │ ├── values-hi │ │ └── strings.xml │ │ ├── values-pt-rBR │ │ └── strings.xml │ │ ├── values-zh │ │ └── strings.xml │ │ ├── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ └── provider_paths.xml │ └── test │ └── java │ └── droidninja │ └── filepicker │ └── ExampleUnitTest.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | /.idea -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | 5 | android { 6 | compileSdkVersion 30 7 | buildToolsVersion '30.0.2' 8 | 9 | defaultConfig { 10 | applicationId "vi.filepicker" 11 | minSdkVersion 17 12 | targetSdkVersion 30 13 | versionCode 1 14 | versionName "1.0" 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | } 24 | 25 | dependencies { 26 | implementation fileTree(include: ['*.jar'], dir: 'libs') 27 | testImplementation 'junit:junit:4.12' 28 | implementation project(':filepicker') 29 | implementation 'androidx.appcompat:appcompat:1.2.0' 30 | implementation 'androidx.legacy:legacy-support-v4:1.0.0' 31 | implementation 'androidx.recyclerview:recyclerview:1.1.0' 32 | implementation "com.github.xinyuez:easypermissions:2.0.1" 33 | implementation 'com.google.android.material:material:1.2.1' 34 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 35 | // Required for instrumented tests 36 | androidTestImplementation 'androidx.annotation:annotation:1.1.0' 37 | androidTestImplementation 'androidx.test:runner:1.3.0' 38 | androidTestImplementation 'androidx.test:rules:1.3.0' 39 | // Optional -- UI testing with Espresso 40 | androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0-beta02', { 41 | exclude group: 'com.android.support', module: 'support-annotations' 42 | }) 43 | androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.3.0' 44 | androidTestImplementation 'androidx.test.espresso:espresso-intents:3.3.0' 45 | 46 | // compile 'com.squareup.leakcanary:leakcanary-android:1.5.1' 47 | } 48 | -------------------------------------------------------------------------------- /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 /Users/droidNinja/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep 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 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/vi/filepicker/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package vi.filepicker; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/vi/filepicker/MainActivityTest.java: -------------------------------------------------------------------------------- 1 | package vi.filepicker; 2 | 3 | import android.app.Instrumentation; 4 | import android.os.Build; 5 | import androidx.test.espresso.contrib.RecyclerViewActions; 6 | import androidx.test.rule.ActivityTestRule; 7 | import androidx.test.runner.AndroidJUnit4; 8 | import droidninja.filepicker.FilePickerActivity; 9 | import org.junit.After; 10 | import org.junit.Before; 11 | import org.junit.Rule; 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | 15 | import static androidx.test.InstrumentationRegistry.getInstrumentation; 16 | import static androidx.test.InstrumentationRegistry.getTargetContext; 17 | import static androidx.test.espresso.Espresso.onView; 18 | import static androidx.test.espresso.action.ViewActions.click; 19 | import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; 20 | import static androidx.test.espresso.matcher.ViewMatchers.withId; 21 | import static org.hamcrest.Matchers.allOf; 22 | import static org.junit.Assert.assertNotNull; 23 | import static vi.filepicker.RecyclerViewItemCountAssertion.withItemCount; 24 | 25 | /** 26 | * Created by droidNinja on 23/02/18. 27 | */ 28 | @RunWith(AndroidJUnit4.class) 29 | public class MainActivityTest { 30 | 31 | @Rule public ActivityTestRule activityTestRule = new ActivityTestRule(MainActivity.class); 32 | 33 | private MainActivity mainActivity = null; 34 | 35 | public Instrumentation.ActivityMonitor monitor = getInstrumentation().addMonitor(FilePickerActivity.class.getName(), null,false); 36 | 37 | @Before 38 | public void setUp() throws Exception { 39 | mainActivity = activityTestRule.getActivity(); 40 | 41 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 42 | getInstrumentation().getUiAutomation().executeShellCommand( 43 | "pm grant " + getTargetContext().getPackageName() 44 | + " android.permission.WRITE_EXTERNAL_STORAGE"); 45 | } 46 | } 47 | 48 | @Test 49 | public void testFilePicker(){ 50 | assertNotNull(mainActivity.findViewById(R.id.pick_photo)); 51 | 52 | onView(withId(R.id.pick_photo)).perform(click()); 53 | 54 | FilePickerActivity filePickerActivity = 55 | (FilePickerActivity) getInstrumentation().waitForMonitorWithTimeout(monitor, 5000); 56 | 57 | assertNotNull(filePickerActivity); 58 | 59 | onView(allOf(isDisplayed(), withId(R.id.recyclerview))).perform( 60 | RecyclerViewActions.actionOnItemAtPosition(1, click())); 61 | 62 | onView(allOf(isDisplayed(), withId(R.id.recyclerview))).perform( 63 | RecyclerViewActions.actionOnItemAtPosition(2, click())); 64 | 65 | onView(withId(R.id.action_done)).perform(click()); 66 | 67 | onView(allOf(isDisplayed(), withId(R.id.recyclerview))).check(withItemCount(2)); 68 | 69 | } 70 | 71 | @After 72 | public void tearDown(){ 73 | mainActivity = null; 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/vi/filepicker/RecyclerViewItemCountAssertion.java: -------------------------------------------------------------------------------- 1 | package vi.filepicker; 2 | 3 | import androidx.test.espresso.NoMatchingViewException; 4 | import androidx.test.espresso.ViewAssertion; 5 | import androidx.recyclerview.widget.RecyclerView; 6 | import android.view.View; 7 | import org.hamcrest.Matcher; 8 | 9 | import static androidx.test.espresso.matcher.ViewMatchers.assertThat; 10 | import static org.hamcrest.Matchers.is; 11 | 12 | public class RecyclerViewItemCountAssertion implements ViewAssertion { 13 | private final Matcher matcher; 14 | 15 | public static RecyclerViewItemCountAssertion withItemCount(int expectedCount) { 16 | return withItemCount(is(expectedCount)); 17 | } 18 | 19 | public static RecyclerViewItemCountAssertion withItemCount(Matcher matcher) { 20 | return new RecyclerViewItemCountAssertion(matcher); 21 | } 22 | 23 | private RecyclerViewItemCountAssertion(Matcher matcher) { 24 | this.matcher = matcher; 25 | } 26 | 27 | @Override 28 | public void check(View view, NoMatchingViewException noViewFoundException) { 29 | if (noViewFoundException != null) { 30 | throw noViewFoundException; 31 | } 32 | 33 | RecyclerView recyclerView = (RecyclerView) view; 34 | RecyclerView.Adapter adapter = recyclerView.getAdapter(); 35 | assertThat(adapter.getItemCount(), matcher); 36 | } 37 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 29 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/java/vi/filepicker/AppDelegate.java: -------------------------------------------------------------------------------- 1 | package vi.filepicker; 2 | 3 | import android.app.Application; 4 | 5 | /** 6 | * Created by droidNinja on 03/06/17. 7 | */ 8 | 9 | public class AppDelegate extends Application { 10 | 11 | @Override 12 | public void onCreate() { 13 | super.onCreate(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/vi/filepicker/CallerFragment.java: -------------------------------------------------------------------------------- 1 | package vi.filepicker; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.os.Bundle; 7 | import androidx.annotation.NonNull; 8 | import androidx.fragment.app.Fragment; 9 | import androidx.recyclerview.widget.DefaultItemAnimator; 10 | import androidx.recyclerview.widget.OrientationHelper; 11 | import androidx.recyclerview.widget.RecyclerView; 12 | import androidx.recyclerview.widget.StaggeredGridLayoutManager; 13 | import android.view.LayoutInflater; 14 | import android.view.View; 15 | import android.view.ViewGroup; 16 | import android.widget.Button; 17 | import android.widget.Toast; 18 | 19 | import droidninja.filepicker.FilePickerBuilder; 20 | import droidninja.filepicker.FilePickerConst; 21 | import droidninja.filepicker.fragments.BaseFragment; 22 | 23 | import java.net.URISyntaxException; 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | 27 | import droidninja.filepicker.utils.ContentUriUtils; 28 | import pub.devrel.easypermissions.AfterPermissionGranted; 29 | import pub.devrel.easypermissions.AppSettingsDialog; 30 | import pub.devrel.easypermissions.EasyPermissions; 31 | 32 | import static vi.filepicker.MainActivity.RC_FILE_PICKER_PERM; 33 | import static vi.filepicker.MainActivity.RC_PHOTO_PICKER_PERM; 34 | 35 | /** 36 | * A simple {@link Fragment} subclass. 37 | */ 38 | public class CallerFragment extends BaseFragment implements EasyPermissions.PermissionCallbacks { 39 | private int MAX_ATTACHMENT_COUNT = 10; 40 | private ArrayList photoPaths = new ArrayList<>(); 41 | private ArrayList docPaths = new ArrayList<>(); 42 | 43 | public CallerFragment() { 44 | // Required empty public constructor 45 | } 46 | 47 | @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, 48 | Bundle savedInstanceState) { 49 | // Inflate the layout for this fragment 50 | View view = inflater.inflate(R.layout.activity_main, container, false); 51 | Button openFragmentBtn = view.findViewById(R.id.open_fragment); 52 | openFragmentBtn.setVisibility(View.GONE); 53 | view.findViewById(R.id.pick_photo).setOnClickListener(new View.OnClickListener() { 54 | @Override 55 | public void onClick(View view) { 56 | pickPhoto(); 57 | } 58 | }); 59 | view.findViewById(R.id.pick_doc).setOnClickListener(new View.OnClickListener() { 60 | @Override 61 | public void onClick(View view) { 62 | pickDoc(); 63 | } 64 | }); 65 | return view; 66 | } 67 | 68 | @AfterPermissionGranted(RC_PHOTO_PICKER_PERM) 69 | public void pickPhoto() { 70 | if (EasyPermissions.hasPermissions(getContext(), FilePickerConst.PERMISSIONS_FILE_PICKER)) { 71 | onPickPhoto(); 72 | } else { 73 | // Ask for one permission 74 | EasyPermissions.requestPermissions( 75 | this, 76 | getString(R.string.rationale_photo_picker), 77 | RC_PHOTO_PICKER_PERM, 78 | FilePickerConst.PERMISSIONS_FILE_PICKER); 79 | } 80 | } 81 | 82 | @AfterPermissionGranted(RC_FILE_PICKER_PERM) 83 | public void pickDoc() { 84 | if (EasyPermissions.hasPermissions(getContext(), FilePickerConst.PERMISSIONS_FILE_PICKER)) { 85 | onPickDoc(); 86 | } else { 87 | // Ask for one permission 88 | EasyPermissions.requestPermissions( 89 | this, 90 | getString(R.string.rationale_doc_picker), 91 | RC_FILE_PICKER_PERM, 92 | FilePickerConst.PERMISSIONS_FILE_PICKER); 93 | } 94 | } 95 | 96 | @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { 97 | switch (requestCode) { 98 | case FilePickerConst.REQUEST_CODE_PHOTO: 99 | if (resultCode == Activity.RESULT_OK && data != null) { 100 | ArrayList dataList = data.getParcelableArrayListExtra(FilePickerConst.KEY_SELECTED_MEDIA); 101 | if(dataList != null) { 102 | photoPaths = new ArrayList(); 103 | photoPaths.addAll(dataList); 104 | } 105 | } 106 | break; 107 | 108 | case FilePickerConst.REQUEST_CODE_DOC: 109 | if (resultCode == Activity.RESULT_OK && data != null) { 110 | ArrayList dataList = data.getParcelableArrayListExtra(FilePickerConst.KEY_SELECTED_MEDIA); 111 | if(dataList != null) { 112 | docPaths = new ArrayList<>(); 113 | docPaths.addAll(dataList); 114 | } 115 | } 116 | break; 117 | } 118 | 119 | addThemToView(photoPaths, docPaths); 120 | } 121 | 122 | private void addThemToView(ArrayList imagePaths, ArrayList docPaths) { 123 | ArrayList filePaths = new ArrayList<>(); 124 | if (imagePaths != null) filePaths.addAll(imagePaths); 125 | 126 | if (docPaths != null) filePaths.addAll(docPaths); 127 | 128 | final RecyclerView recyclerView = (RecyclerView) getView().findViewById(R.id.recyclerview); 129 | if (recyclerView != null) { 130 | StaggeredGridLayoutManager layoutManager = 131 | new StaggeredGridLayoutManager(3, OrientationHelper.VERTICAL); 132 | layoutManager.setGapStrategy( 133 | StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS); 134 | recyclerView.setLayoutManager(layoutManager); 135 | 136 | ImageAdapter imageAdapter = new ImageAdapter(getActivity(), filePaths, new ImageAdapter.ImageAdapterListener() { 137 | @Override 138 | public void onItemClick(Uri uri) { 139 | try { 140 | //make sure to use this getFilePath method from worker thread 141 | String path = ContentUriUtils.INSTANCE.getFilePath(recyclerView.getContext(), uri); 142 | if (path != null) { 143 | Toast.makeText(recyclerView.getContext(), path, Toast.LENGTH_SHORT).show(); 144 | } 145 | } catch (URISyntaxException e) { 146 | e.printStackTrace(); 147 | } 148 | } 149 | }); 150 | 151 | recyclerView.setAdapter(imageAdapter); 152 | recyclerView.setItemAnimator(new DefaultItemAnimator()); 153 | } 154 | 155 | Toast.makeText(getActivity(), "Num of files selected: " + filePaths.size(), Toast.LENGTH_SHORT) 156 | .show(); 157 | } 158 | 159 | public void onPickPhoto() { 160 | int maxCount = MAX_ATTACHMENT_COUNT - docPaths.size(); 161 | if ((docPaths.size() + photoPaths.size()) == MAX_ATTACHMENT_COUNT) { 162 | Toast.makeText(getActivity(), "Cannot select more than " + MAX_ATTACHMENT_COUNT + " items", 163 | Toast.LENGTH_SHORT).show(); 164 | } else { 165 | FilePickerBuilder.Companion.getInstance() 166 | .setMaxCount(maxCount) 167 | .setSelectedFiles(photoPaths) 168 | .setActivityTheme(R.style.FilePickerTheme) 169 | .pickPhoto(this); 170 | } 171 | } 172 | 173 | public void onPickDoc() { 174 | int maxCount = MAX_ATTACHMENT_COUNT - photoPaths.size(); 175 | if ((docPaths.size() + photoPaths.size()) == MAX_ATTACHMENT_COUNT) { 176 | Toast.makeText(getActivity(), "Cannot select more than " + MAX_ATTACHMENT_COUNT + " items", 177 | Toast.LENGTH_SHORT).show(); 178 | } else { 179 | FilePickerBuilder.Companion.getInstance() 180 | .setMaxCount(maxCount) 181 | .setSelectedFiles(docPaths) 182 | .enableDocSupport(true) 183 | .setActivityTheme(R.style.FilePickerTheme) 184 | .pickFile(this); 185 | } 186 | } 187 | 188 | @Override public void onPermissionsGranted(int requestCode, @NonNull List perms) { 189 | } 190 | 191 | @Override public void onPermissionsDenied(int requestCode, @NonNull List perms) { 192 | 193 | if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) { 194 | new AppSettingsDialog.Builder(this).build().show(); 195 | } 196 | } 197 | 198 | @Override 199 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 200 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 201 | 202 | EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this); 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /app/src/main/java/vi/filepicker/FragmentActivity.java: -------------------------------------------------------------------------------- 1 | package vi.filepicker; 2 | 3 | import android.os.Bundle; 4 | import androidx.appcompat.app.AppCompatActivity; 5 | import droidninja.filepicker.utils.FragmentUtil; 6 | 7 | public class FragmentActivity extends AppCompatActivity { 8 | 9 | @Override 10 | protected void onCreate(Bundle savedInstanceState) { 11 | super.onCreate(savedInstanceState); 12 | setContentView(R.layout.activity_fragment); 13 | 14 | initView(); 15 | } 16 | 17 | private void initView() { 18 | CallerFragment callerFragment = new CallerFragment(); 19 | FragmentUtil.INSTANCE.addFragment(this, R.id.container,callerFragment); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/vi/filepicker/ImageAdapter.java: -------------------------------------------------------------------------------- 1 | package vi.filepicker; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.appcompat.widget.AppCompatImageView; 6 | import androidx.recyclerview.widget.RecyclerView; 7 | 8 | import android.net.Uri; 9 | import android.util.DisplayMetrics; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | import android.view.WindowManager; 14 | 15 | import com.bumptech.glide.Glide; 16 | import com.bumptech.glide.request.RequestOptions; 17 | 18 | import java.io.File; 19 | import java.util.ArrayList; 20 | 21 | /** 22 | * Created by droidNinja on 29/07/16. 23 | */ 24 | public class ImageAdapter extends RecyclerView.Adapter { 25 | 26 | private final ImageAdapterListener imageAdapterListener; 27 | 28 | public interface ImageAdapterListener { 29 | void onItemClick(Uri uri); 30 | } 31 | 32 | private final ArrayList paths; 33 | private final Context context; 34 | private int imageSize; 35 | 36 | public ImageAdapter(Context context, ArrayList paths, ImageAdapterListener imageAdapterListener) { 37 | this.context = context; 38 | this.paths = paths; 39 | this.imageAdapterListener = imageAdapterListener; 40 | setColumnNumber(context, 3); 41 | } 42 | 43 | private void setColumnNumber(Context context, int columnNum) { 44 | WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 45 | DisplayMetrics metrics = new DisplayMetrics(); 46 | wm.getDefaultDisplay().getMetrics(metrics); 47 | int widthPixels = metrics.widthPixels; 48 | imageSize = widthPixels / columnNum; 49 | } 50 | 51 | @Override 52 | public FileViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 53 | View itemView = 54 | LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false); 55 | 56 | return new FileViewHolder(itemView); 57 | } 58 | 59 | @Override 60 | public void onBindViewHolder(FileViewHolder holder, int position) { 61 | final Uri path = paths.get(position); 62 | Glide.with(context) 63 | .load(path) 64 | .apply(RequestOptions.centerCropTransform() 65 | .dontAnimate() 66 | .override(imageSize, imageSize) 67 | .placeholder(droidninja.filepicker.R.drawable.image_placeholder)) 68 | .thumbnail(0.5f) 69 | .into(holder.imageView); 70 | holder.itemView.setOnClickListener(new View.OnClickListener() { 71 | 72 | @Override 73 | public void onClick(View v) { 74 | imageAdapterListener.onItemClick(path); 75 | } 76 | }); 77 | } 78 | 79 | @Override 80 | public int getItemCount() { 81 | return paths.size(); 82 | } 83 | 84 | public static class FileViewHolder extends RecyclerView.ViewHolder { 85 | 86 | AppCompatImageView imageView; 87 | 88 | public FileViewHolder(View itemView) { 89 | super(itemView); 90 | imageView = itemView.findViewById(R.id.iv_photo); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /app/src/main/java/vi/filepicker/MainActivity.java: -------------------------------------------------------------------------------- 1 | package vi.filepicker; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.content.pm.ActivityInfo; 6 | import android.net.Uri; 7 | import android.os.Bundle; 8 | import android.view.View; 9 | import android.widget.Toast; 10 | 11 | import androidx.annotation.NonNull; 12 | import androidx.appcompat.app.AppCompatActivity; 13 | import androidx.recyclerview.widget.DefaultItemAnimator; 14 | import androidx.recyclerview.widget.OrientationHelper; 15 | import androidx.recyclerview.widget.RecyclerView; 16 | import androidx.recyclerview.widget.StaggeredGridLayoutManager; 17 | 18 | import java.net.URISyntaxException; 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | import droidninja.filepicker.FilePickerBuilder; 23 | import droidninja.filepicker.FilePickerConst; 24 | import droidninja.filepicker.models.sort.SortingTypes; 25 | import droidninja.filepicker.utils.ContentUriUtils; 26 | import pub.devrel.easypermissions.AfterPermissionGranted; 27 | import pub.devrel.easypermissions.AppSettingsDialog; 28 | import pub.devrel.easypermissions.EasyPermissions; 29 | 30 | public class MainActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks { 31 | 32 | public static final int RC_PHOTO_PICKER_PERM = 123; 33 | public static final int RC_FILE_PICKER_PERM = 321; 34 | private static final int CUSTOM_REQUEST_CODE = 532; 35 | private int MAX_ATTACHMENT_COUNT = 10; 36 | private ArrayList photoPaths = new ArrayList<>(); 37 | private ArrayList docPaths = new ArrayList<>(); 38 | 39 | @Override 40 | protected void onCreate(Bundle savedInstanceState) { 41 | super.onCreate(savedInstanceState); 42 | setContentView(R.layout.activity_main); 43 | findViewById(R.id.pick_photo).setOnClickListener(new View.OnClickListener() { 44 | @Override 45 | public void onClick(View view) { 46 | pickPhotoClicked(); 47 | } 48 | }); 49 | findViewById(R.id.pick_doc).setOnClickListener(new View.OnClickListener() { 50 | @Override 51 | public void onClick(View view) { 52 | pickDocClicked(); 53 | } 54 | }); 55 | } 56 | 57 | @AfterPermissionGranted(RC_PHOTO_PICKER_PERM) 58 | public void pickPhotoClicked() { 59 | if (EasyPermissions.hasPermissions(this, FilePickerConst.PERMISSIONS_FILE_PICKER)) { 60 | onPickPhoto(); 61 | } else { 62 | // Ask for one permission 63 | EasyPermissions.requestPermissions(this, getString(R.string.rationale_photo_picker), 64 | RC_PHOTO_PICKER_PERM, FilePickerConst.PERMISSIONS_FILE_PICKER); 65 | } 66 | } 67 | 68 | @AfterPermissionGranted(RC_FILE_PICKER_PERM) 69 | public void pickDocClicked() { 70 | if (EasyPermissions.hasPermissions(this, FilePickerConst.PERMISSIONS_FILE_PICKER)) { 71 | onPickDoc(); 72 | } else { 73 | // Ask for one permission 74 | EasyPermissions.requestPermissions(this, getString(R.string.rationale_doc_picker), 75 | RC_FILE_PICKER_PERM, FilePickerConst.PERMISSIONS_FILE_PICKER); 76 | } 77 | } 78 | 79 | @Override 80 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 81 | super.onActivityResult(requestCode, resultCode, data); 82 | switch (requestCode) { 83 | case CUSTOM_REQUEST_CODE: 84 | if (resultCode == Activity.RESULT_OK && data != null) { 85 | ArrayList dataList = data.getParcelableArrayListExtra(FilePickerConst.KEY_SELECTED_MEDIA); 86 | if (dataList != null) { 87 | photoPaths = new ArrayList(); 88 | photoPaths.addAll(dataList); 89 | } 90 | } 91 | break; 92 | 93 | case FilePickerConst.REQUEST_CODE_DOC: 94 | if (resultCode == Activity.RESULT_OK && data != null) { 95 | ArrayList dataList = data.getParcelableArrayListExtra(FilePickerConst.KEY_SELECTED_DOCS); 96 | if (dataList != null) { 97 | docPaths = new ArrayList<>(); 98 | docPaths.addAll(dataList); 99 | } 100 | } 101 | break; 102 | } 103 | 104 | addThemToView(photoPaths, docPaths); 105 | } 106 | 107 | private void addThemToView(ArrayList imagePaths, ArrayList docPaths) { 108 | ArrayList filePaths = new ArrayList<>(); 109 | if (imagePaths != null) filePaths.addAll(imagePaths); 110 | 111 | if (docPaths != null) filePaths.addAll(docPaths); 112 | 113 | final RecyclerView recyclerView = findViewById(R.id.recyclerview); 114 | if (recyclerView != null) { 115 | StaggeredGridLayoutManager layoutManager = 116 | new StaggeredGridLayoutManager(3, OrientationHelper.VERTICAL); 117 | layoutManager.setGapStrategy( 118 | StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS); 119 | recyclerView.setLayoutManager(layoutManager); 120 | 121 | ImageAdapter imageAdapter = new ImageAdapter(this, filePaths, new ImageAdapter.ImageAdapterListener() { 122 | @Override 123 | public void onItemClick(Uri uri) { 124 | try { 125 | //make sure to use this getFilePath method from worker thread 126 | String path = ContentUriUtils.INSTANCE.getFilePath(recyclerView.getContext(), uri); 127 | if (path != null) { 128 | Toast.makeText(recyclerView.getContext(), path, Toast.LENGTH_SHORT).show(); 129 | } 130 | } catch (URISyntaxException e) { 131 | e.printStackTrace(); 132 | } 133 | } 134 | }); 135 | 136 | recyclerView.setAdapter(imageAdapter); 137 | recyclerView.setItemAnimator(new DefaultItemAnimator()); 138 | } 139 | 140 | Toast.makeText(this, "Num of files selected: " + filePaths.size(), Toast.LENGTH_SHORT).show(); 141 | } 142 | 143 | public void onPickPhoto() { 144 | int maxCount = MAX_ATTACHMENT_COUNT - docPaths.size(); 145 | if ((docPaths.size() + photoPaths.size()) == MAX_ATTACHMENT_COUNT) { 146 | Toast.makeText(this, "Cannot select more than " + MAX_ATTACHMENT_COUNT + " items", 147 | Toast.LENGTH_SHORT).show(); 148 | } else { 149 | FilePickerBuilder.getInstance() 150 | .setMaxCount(10) 151 | .setSelectedFiles(photoPaths) //this is optional 152 | .setActivityTheme(R.style.FilePickerTheme) 153 | .setActivityTitle("Please select media") 154 | .setImageSizeLimit(5) 155 | .setVideoSizeLimit(10) 156 | .setSpan(FilePickerConst.SPAN_TYPE.FOLDER_SPAN, 3) 157 | .setSpan(FilePickerConst.SPAN_TYPE.DETAIL_SPAN, 4) 158 | .enableVideoPicker(true) 159 | .enableCameraSupport(true) 160 | .showGifs(true) 161 | .showFolderView(false) 162 | .enableSelectAll(true) 163 | .enableImagePicker(true) 164 | .setCameraPlaceholder(R.drawable.custom_camera) 165 | .withOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) 166 | .pickPhoto(this, CUSTOM_REQUEST_CODE); 167 | } 168 | } 169 | 170 | public void onPickDoc() { 171 | String[] zips = {"zip", "rar"}; 172 | String[] pdfs = {"aac"}; 173 | int maxCount = MAX_ATTACHMENT_COUNT - photoPaths.size(); 174 | if ((docPaths.size() + photoPaths.size()) == MAX_ATTACHMENT_COUNT) { 175 | Toast.makeText(this, "Cannot select more than " + MAX_ATTACHMENT_COUNT + " items", 176 | Toast.LENGTH_SHORT).show(); 177 | } else { 178 | FilePickerBuilder.getInstance() 179 | .setMaxCount(1) 180 | .setSelectedFiles(docPaths) 181 | .setActivityTheme(R.style.FilePickerTheme) 182 | .setActivityTitle("Please select doc") 183 | .setImageSizeLimit(5) //Provide Size in MB 184 | .setVideoSizeLimit(20) 185 | // .addFileSupport("ZIP", zips) 186 | // .addFileSupport("AAC", pdfs, R.drawable.pdf_blue) 187 | .enableDocSupport(true) 188 | .enableSelectAll(true) 189 | .sortDocumentsBy(SortingTypes.NAME) 190 | .withOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) 191 | .pickFile(this); 192 | } 193 | } 194 | 195 | @Override 196 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, 197 | @NonNull int[] grantResults) { 198 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 199 | 200 | EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this); 201 | } 202 | 203 | public void onOpenFragmentClicked(View view) { 204 | Intent intent = new Intent(this, FragmentActivity.class); 205 | startActivity(intent); 206 | } 207 | 208 | @Override 209 | public void onPermissionsGranted(int requestCode, @NonNull List perms) { 210 | } 211 | 212 | @Override 213 | public void onPermissionsDenied(int requestCode, @NonNull List perms) { 214 | 215 | if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) { 216 | new AppSettingsDialog.Builder(this).build().show(); 217 | } 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/custom_camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DroidNinja/Android-FilePicker/fe12cc36b20945d48ff4555149377e7f4e183493/app/src/main/res/drawable-xxhdpi/custom_camera.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/pdf_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DroidNinja/Android-FilePicker/fe12cc36b20945d48ff4555149377e7f4e183493/app/src/main/res/drawable-xxhdpi/pdf_blue.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 19 |