├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── gun0912 │ │ └── tedbottompickerdemo │ │ └── MainActivity.java │ └── res │ ├── layout │ ├── activity_main.xml │ └── image_item.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-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── demo.gif ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── screenshot1.jpeg ├── screenshot_multi_select.jpeg ├── settings.gradle └── tedbottompicker ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src └── main ├── AndroidManifest.xml ├── java └── gun0912 │ └── tedbottompicker │ ├── GridSpacingItemDecoration.java │ ├── TedBottomPicker.java │ ├── adapter │ └── ImageGalleryAdapter.java │ ├── util │ └── RealPathUtil.java │ └── view │ ├── TedEmptyRecyclerView.java │ ├── TedSquareFrameLayout.java │ └── TedSquareImageView.java └── res ├── drawable-xhdpi └── ic_clear.png ├── drawable-xxxhdpi ├── gallery_photo_selected.xml ├── ic_camera.png ├── ic_gallery.png └── img_error.png ├── layout ├── tedbottompicker_content_view.xml ├── tedbottompicker_grid_item.xml └── tedbottompicker_selected_item.xml ├── values ├── attrs.xml ├── colors.xml ├── dimens.xml ├── strings.xml └── style.xml └── xml └── provider_paths.xml /.gitignore: -------------------------------------------------------------------------------- 1 | ###Android### 2 | 3 | # Built application files 4 | *.apk 5 | *.ap_ 6 | 7 | # Files for the Dalvik VM 8 | *.dex 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | 17 | # Gradle files 18 | .gradle/ 19 | build/ 20 | 21 | # Local configuration file (sdk path, etc) 22 | local.properties 23 | 24 | # Proguard folder generated by Eclipse 25 | proguard/ 26 | 27 | # Log Files 28 | *.log 29 | 30 | 31 | ###OSX### 32 | 33 | .DS_Store 34 | .AppleDouble 35 | .LSOverride 36 | 37 | # Icon must end with two \r 38 | Icon 39 | 40 | # Thumbnails 41 | ._* 42 | 43 | # Files that might appear on external disk 44 | .Spotlight-V100 45 | .Trashes 46 | 47 | # Directories potentially created on remote AFP share 48 | .AppleDB 49 | .AppleDesktop 50 | Network Trash Folder 51 | Temporary Items 52 | .apdisk 53 | 54 | 55 | ###Linux### 56 | 57 | *~ 58 | 59 | # KDE directory preferences 60 | .directory 61 | 62 | 63 | ###Windows### 64 | 65 | # Windows image file caches 66 | Thumbs.db 67 | ehthumbs.db 68 | 69 | # Folder config file 70 | Desktop.ini 71 | 72 | # Recycle Bin used on file shares 73 | $RECYCLE.BIN/ 74 | 75 | # Windows Installer files 76 | *.cab 77 | *.msi 78 | *.msm 79 | *.msp 80 | 81 | # Windows shortcuts 82 | *.lnk 83 | 84 | 85 | ###IntelliJ### 86 | 87 | *.iml 88 | *.ipr 89 | *.iws 90 | .idea/ 91 | 92 | 93 | ###Gradle### 94 | 95 | .gradle 96 | build/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # What is TedBottomPicker? 3 | In Google's Material Design, Google introduce **Bottom sheets**.([Components – Bottom sheets](https://material.google.com/components/bottom-sheets.html))
4 | **Bottom sheets** slide up from the bottom of the screen to reveal more content. 5 | 6 | If you want pick image from gallery or take picture, this library can help easily.
7 | **TedBottomPicker** provide 3 options:
8 | 9 | 1. Take a picture by camera(using `MediaStore.ACTION_IMAGE_CAPTURE` intent) 10 | 2. Get image from gallery(using `Intent.ACTION_PICK` intent) 11 | 3. Get image from recent image(using `MediaStore.Images.Media.EXTERNAL_CONTENT_URI` cursor) 12 | 13 | 14 | **TedBottomPicker** is simple image picker using bottom sheet. 15 | 16 |

17 | 18 | 19 | 20 | ## Demo 21 | 22 | 1. Show Bottom Sheet. 23 | 2. Pick Image 24 | 25 | ### Single/Multi Select 26 | 27 | ![Screenshot](https://github.com/ParkSangGwon/TedBottomPicker/blob/master/screenshot1.jpeg?raw=true) ![Screenshot](https://github.com/ParkSangGwon/TedBottomPicker/blob/master/demo.gif?raw=true) 28 | ![Screenshot](https://github.com/ParkSangGwon/TedBottomPicker/blob/master/screenshot_multi_select.jpeg?raw=true) 29 | 30 | 31 | 32 | 33 |

34 | 35 | 36 | ## Setup 37 | 38 | 39 | ### Gradle 40 | ```javascript 41 | 42 | dependencies { 43 | compile 'gun0912.ted:tedbottompicker:1.0.12' 44 | } 45 | 46 | ``` 47 | 48 | If you think this library is useful, please press star button at upside. 49 |
50 | 51 |

52 | 53 | 54 | 55 | ## How to use 56 | ### 1. Check Permission 57 | You have to grant `WRITE_EXTERNAL_STORAGE` permission from user.
58 | If your targetSDK version is 23+, you have to check permission and request permission to user.
59 | Because after Marshmallow(6.0), you have to not only decalare permisions in `AndroidManifest.xml` but also request permissions at runtime.
60 | There are so many permission check library in [Android-Arsenal](http://android-arsenal.com/tag/235?sort=rating)
61 | I recommend [TedPermission](https://github.com/ParkSangGwon/TedPermission)
62 | **TedPermission** is super simple and smart permission check library.
63 |
64 | 65 | 66 | ### 2. Start TedBottomPicker 67 | **TedBottomPicker** class extend `BottomSheetDialogFragment`.
68 | `TedBottomPicker.Builder` make `new TedBottomPicker()`.
69 | After then, you can show TedBottomPicker
70 | 71 | 72 | ```javascript 73 | 74 | TedBottomPicker tedBottomPicker = new TedBottomPicker.Builder(MainActivity.this) 75 | .setOnImageSelectedListener(new TedBottomPicker.OnImageSelectedListener() { 76 | @Override 77 | public void onImageSelected(Uri uri) { 78 | // here is selected uri 79 | } 80 | }) 81 | .create(); 82 | 83 | tedBottomPicker.show(getSupportFragmentManager()); 84 | ``` 85 | 86 | If you want select multi image, you can use `OnMultiImageSelectedListener` 87 | ```javascript 88 | TedBottomPicker bottomSheetDialogFragment = new TedBottomPicker.Builder(MainActivity.this) 89 | .setOnMultiImageSelectedListener(new TedBottomPicker.OnMultiImageSelectedListener() { 90 | @Override 91 | public void onImagesSelected(ArrayList uriList) { 92 | // here is selected uri list 93 | } 94 | }) 95 | .setPeekHeight(1600) 96 | .showTitle(false) 97 | .setCompleteButtonText("Done") 98 | .setEmptySelectionText("No Select") 99 | .create(); 100 | 101 | bottomSheetDialogFragment.show(getSupportFragmentManager()); 102 | ``` 103 | 104 | **Don't forget!!**
105 | You have to declare `setOnImageSelectedListener()` or `OnMultiImageSelectedListener()` in Builder.
106 | This listener will pass selected Uri/UriList.
107 | 108 | 109 | 110 | 111 |
112 | 113 | ## Customize 114 | You can customize something ...
115 | 116 | ### Function 117 | 118 | #### Common 119 | 120 | * `setPreviewMaxCount(Int) (default: 25)` 121 | * `setPeekHeight(Int)` 122 | * `setPeekHeightResId(R.dimen.xxx)` 123 | * `showCameraTile(Boolean) (default: true)` 124 | * `setCameraTile(R.drawable.xxx or Drawable)` 125 | * `setCameraTileBackgroundResId(R.color.xxx)` 126 | * `setGalleryTile(R.drawable.xxx or Drawable)` 127 | * `showGalleryTile(Boolean) (default: true)` 128 | * `setGalleryTileBackgroundResId(R.color.xxx)` 129 | * `setSpacing(Int)` 130 | * `setSpacingResId(R.dimen.xxx)` 131 | * `setOnErrorListener(OnErrorListener)` 132 | * `setTitle(String or R.string.xxx) (default: 'Select Image','사진 선택')` 133 | * `showTitle(Boolean) (default: true)` 134 | * `setTitleBackgroundResId(R.color.xxx)` 135 | * `setImageProvider(ImageProvider)` 136 | : If you want load grid image yourself, you can use your ImageProvider 137 | 138 | #### Single Select 139 | * `setSelectedUri(Uri)` 140 | 141 | #### Multi Select 142 | * `setDeSelectIcon(R.drawable.xxx or Drawable)` 143 | * `setSelectedForeground(R.drawable.xxx or Drawable)` 144 | * `setSelectMaxCount(Int)` 145 | * `setSelectMinCount(Int)` 146 | * `setCompleteButtonText(String or R.string.xxx) (default: 'Done','완료')` 147 | * `setEmptySelectionText(String or R.string.xxx) (default: 'No Image','이미지가 선택되지 않았습니다')` 148 | * `setSelectMaxCountErrorText(String or R.string.xxx)` 149 | * `setSelectMinCountErrorText(String or R.string.xxx)` 150 | * `setSelectedUriList(ArrayList)` 151 | 152 |

153 | 154 | 155 | 156 | ## Thanks 157 | * [Flipboard-bottomsheet](https://github.com/Flipboard/bottomsheet) - Android component which presents a dismissible view from the bottom of the screen 158 | 159 | 160 | 161 | 162 |

163 | 164 | 165 | ## License 166 | ```code 167 | Copyright 2017 Ted Park 168 | 169 | Licensed under the Apache License, Version 2.0 (the "License"); 170 | you may not use this file except in compliance with the License. 171 | You may obtain a copy of the License at 172 | 173 | http://www.apache.org/licenses/LICENSE-2.0 174 | 175 | Unless required by applicable law or agreed to in writing, software 176 | distributed under the License is distributed on an "AS IS" BASIS, 177 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 178 | See the License for the specific language governing permissions and 179 | limitations under the License.``` 180 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | 5 | compileSdkVersion 25 6 | buildToolsVersion '25.0.0' 7 | 8 | 9 | defaultConfig { 10 | applicationId "gun0912.tedbottompickerdemo" 11 | minSdkVersion 16 12 | targetSdkVersion 24 13 | versionCode 1 14 | versionName "1.0" 15 | } 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | } 23 | 24 | 25 | repositories { 26 | 27 | 28 | } 29 | 30 | dependencies { 31 | compile fileTree(include: ['*.jar'], dir: 'libs') 32 | testCompile 'junit:junit:4.12' 33 | compile 'com.android.support:appcompat-v7:25.1.0' 34 | compile project(':tedbottompicker') 35 | 36 | compile 'gun0912.ted:tedpermission:1.0.0' 37 | } 38 | -------------------------------------------------------------------------------- /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/TedPark/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/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/gun0912/tedbottompickerdemo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package gun0912.tedbottompickerdemo; 2 | 3 | import android.Manifest; 4 | import android.net.Uri; 5 | import android.os.Bundle; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.util.Log; 8 | import android.util.TypedValue; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | import android.widget.Button; 13 | import android.widget.FrameLayout; 14 | import android.widget.ImageView; 15 | import android.widget.Toast; 16 | 17 | import com.bumptech.glide.Glide; 18 | import com.bumptech.glide.RequestManager; 19 | import com.gun0912.tedpermission.PermissionListener; 20 | import com.gun0912.tedpermission.TedPermission; 21 | 22 | import java.util.ArrayList; 23 | 24 | import gun0912.tedbottompicker.TedBottomPicker; 25 | 26 | public class MainActivity extends AppCompatActivity { 27 | 28 | 29 | public RequestManager mGlideRequestManager; 30 | ImageView iv_image; 31 | ArrayList selectedUriList; 32 | Uri selectedUri; 33 | private ViewGroup mSelectedImagesContainer; 34 | 35 | @Override 36 | protected void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | setContentView(R.layout.activity_main); 39 | mGlideRequestManager = Glide.with(this); 40 | 41 | iv_image = (ImageView) findViewById(R.id.iv_image); 42 | mSelectedImagesContainer = (ViewGroup) findViewById(R.id.selected_photos_container); 43 | 44 | setSingleShowButton(); 45 | setMultiShowButton(); 46 | 47 | 48 | } 49 | 50 | private void setSingleShowButton() { 51 | 52 | 53 | Button btn_single_show = (Button) findViewById(R.id.btn_single_show); 54 | btn_single_show.setOnClickListener(new View.OnClickListener() { 55 | @Override 56 | public void onClick(View view) { 57 | 58 | 59 | PermissionListener permissionlistener = new PermissionListener() { 60 | @Override 61 | public void onPermissionGranted() { 62 | 63 | TedBottomPicker bottomSheetDialogFragment = new TedBottomPicker.Builder(MainActivity.this) 64 | .setOnImageSelectedListener(new TedBottomPicker.OnImageSelectedListener() { 65 | @Override 66 | public void onImageSelected(final Uri uri) { 67 | Log.d("ted", "uri: " + uri); 68 | Log.d("ted", "uri.getPath(): " + uri.getPath()); 69 | selectedUri = uri; 70 | 71 | iv_image.setVisibility(View.VISIBLE); 72 | mSelectedImagesContainer.setVisibility(View.GONE); 73 | iv_image.post(new Runnable() { 74 | @Override 75 | public void run() { 76 | mGlideRequestManager 77 | .load(uri) 78 | .into(iv_image); 79 | } 80 | }); 81 | /* 82 | Glide.with(MainActivity.this) 83 | //.load(uri.toString()) 84 | .load(uri) 85 | .into(iv_image); 86 | */ 87 | } 88 | }) 89 | //.setPeekHeight(getResources().getDisplayMetrics().heightPixels/2) 90 | .setSelectedUri(selectedUri) 91 | .setPeekHeight(1200) 92 | .create(); 93 | 94 | bottomSheetDialogFragment.show(getSupportFragmentManager()); 95 | 96 | 97 | } 98 | 99 | @Override 100 | public void onPermissionDenied(ArrayList deniedPermissions) { 101 | Toast.makeText(MainActivity.this, "Permission Denied\n" + deniedPermissions.toString(), Toast.LENGTH_SHORT).show(); 102 | } 103 | 104 | 105 | }; 106 | 107 | new TedPermission(MainActivity.this) 108 | .setPermissionListener(permissionlistener) 109 | .setDeniedMessage("If you reject permission,you can not use this service\n\nPlease turn on permissions at [Setting] > [Permission]") 110 | .setPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE) 111 | .check(); 112 | 113 | } 114 | }); 115 | } 116 | 117 | private void setMultiShowButton() { 118 | 119 | Button btn_multi_show = (Button) findViewById(R.id.btn_multi_show); 120 | btn_multi_show.setOnClickListener(new View.OnClickListener() { 121 | @Override 122 | public void onClick(View view) { 123 | 124 | 125 | PermissionListener permissionlistener = new PermissionListener() { 126 | @Override 127 | public void onPermissionGranted() { 128 | 129 | TedBottomPicker bottomSheetDialogFragment = new TedBottomPicker.Builder(MainActivity.this) 130 | .setOnMultiImageSelectedListener(new TedBottomPicker.OnMultiImageSelectedListener() { 131 | @Override 132 | public void onImagesSelected(ArrayList uriList) { 133 | selectedUriList = uriList; 134 | showUriList(uriList); 135 | } 136 | }) 137 | //.setPeekHeight(getResources().getDisplayMetrics().heightPixels/2) 138 | .setPeekHeight(1600) 139 | .showTitle(false) 140 | .setCompleteButtonText("Done") 141 | .setEmptySelectionText("No Select") 142 | .setSelectedUriList(selectedUriList) 143 | .create(); 144 | 145 | bottomSheetDialogFragment.show(getSupportFragmentManager()); 146 | 147 | 148 | } 149 | 150 | @Override 151 | public void onPermissionDenied(ArrayList deniedPermissions) { 152 | Toast.makeText(MainActivity.this, "Permission Denied\n" + deniedPermissions.toString(), Toast.LENGTH_SHORT).show(); 153 | } 154 | 155 | 156 | }; 157 | 158 | new TedPermission(MainActivity.this) 159 | .setPermissionListener(permissionlistener) 160 | .setDeniedMessage("If you reject permission,you can not use this service\n\nPlease turn on permissions at [Setting] > [Permission]") 161 | .setPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE) 162 | .check(); 163 | 164 | } 165 | }); 166 | 167 | } 168 | 169 | 170 | private void showUriList(ArrayList uriList) { 171 | // Remove all views before 172 | // adding the new ones. 173 | mSelectedImagesContainer.removeAllViews(); 174 | 175 | iv_image.setVisibility(View.GONE); 176 | mSelectedImagesContainer.setVisibility(View.VISIBLE); 177 | 178 | int wdpx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, getResources().getDisplayMetrics()); 179 | int htpx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, getResources().getDisplayMetrics()); 180 | 181 | 182 | for (Uri uri : uriList) { 183 | 184 | View imageHolder = LayoutInflater.from(this).inflate(R.layout.image_item, null); 185 | ImageView thumbnail = (ImageView) imageHolder.findViewById(R.id.media_image); 186 | 187 | Glide.with(this) 188 | .load(uri.toString()) 189 | .fitCenter() 190 | .into(thumbnail); 191 | 192 | mSelectedImagesContainer.addView(imageHolder); 193 | 194 | thumbnail.setLayoutParams(new FrameLayout.LayoutParams(wdpx, htpx)); 195 | 196 | 197 | } 198 | 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 |