├── .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 |  
28 | 
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 |
19 |
20 |
21 |
27 |
28 |
35 |
36 |
37 |
44 |
45 |
46 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/image_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iammert/TedBottomPicker/8003e3065f1ee9037ca5d1f33f25b3d6e171a638/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iammert/TedBottomPicker/8003e3065f1ee9037ca5d1f33f25b3d6e171a638/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iammert/TedBottomPicker/8003e3065f1ee9037ca5d1f33f25b3d6e171a638/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iammert/TedBottomPicker/8003e3065f1ee9037ca5d1f33f25b3d6e171a638/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iammert/TedBottomPicker/8003e3065f1ee9037ca5d1f33f25b3d6e171a638/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | TedBottomPickerDemo
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.3.0'
9 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4'
10 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1'
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | jcenter()
17 | }
18 | }
19 |
20 | task clean(type: Delete) {
21 | delete rootProject.buildDir
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iammert/TedBottomPicker/8003e3065f1ee9037ca5d1f33f25b3d6e171a638/demo.gif
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | ## Project-wide Gradle settings.
2 | #
3 | # For more details on how to configure your build environment visit
4 | # http://www.gradle.org/docs/current/userguide/build_environment.html
5 | #
6 | # Specifies the JVM arguments used for the daemon process.
7 | # The setting is particularly useful for tweaking memory settings.
8 | # Default value: -Xmx1024m -XX:MaxPermSize=256m
9 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
10 | #
11 | # When configured, Gradle will run in incubating parallel mode.
12 | # This option should only be used with decoupled projects. More details, visit
13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
14 | # org.gradle.parallel=true
15 | #Sat Mar 11 14:34:48 IRST 2017
16 |
17 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iammert/TedBottomPicker/8003e3065f1ee9037ca5d1f33f25b3d6e171a638/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Mar 11 14:37:09 IRST 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-3.3-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/screenshot1.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iammert/TedBottomPicker/8003e3065f1ee9037ca5d1f33f25b3d6e171a638/screenshot1.jpeg
--------------------------------------------------------------------------------
/screenshot_multi_select.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iammert/TedBottomPicker/8003e3065f1ee9037ca5d1f33f25b3d6e171a638/screenshot_multi_select.jpeg
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':tedbottompicker'
2 |
--------------------------------------------------------------------------------
/tedbottompicker/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/tedbottompicker/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | ext {
4 | bintrayRepo = 'maven'
5 | bintrayName = 'tedbottompicker'
6 |
7 | publishedGroupId = 'gun0912.ted'
8 | libraryName = 'TedBottomPicker'
9 | artifact = 'tedbottompicker'
10 |
11 | libraryDescription = 'Simple image picker using bottom sheet on Android'
12 |
13 | siteUrl = 'https://github.com/ParkSangGwon/TedBottomPicker'
14 | gitUrl = 'https://github.com/ParkSangGwon/TedBottomPicker.git'
15 |
16 | libraryVersion = '1.0.12'
17 |
18 | developerId = 'gun0912'
19 | developerName = 'Ted Park'
20 | developerEmail = 'gun0912@naver.com'
21 |
22 |
23 | licenseName = 'The Apache Software License, Version 2.0'
24 | licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
25 | allLicenses = ["Apache-2.0"]
26 | }
27 |
28 | android {
29 |
30 | compileSdkVersion 25
31 | buildToolsVersion '25.0.0'
32 |
33 |
34 | defaultConfig {
35 | minSdkVersion 16
36 | targetSdkVersion 24
37 | versionCode 1
38 | versionName "1.0"
39 | }
40 | buildTypes {
41 | release {
42 | minifyEnabled false
43 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
44 | }
45 | }
46 | }
47 |
48 |
49 |
50 | configurations {
51 | javadocDeps
52 | }
53 |
54 | dependencies {
55 | compile fileTree(include: ['*.jar'], dir: 'libs')
56 | testCompile 'junit:junit:4.12'
57 | compile 'com.android.support:appcompat-v7:25.1.0'
58 | compile 'com.android.support:design:25.1.0'
59 | compile 'com.android.support:support-annotations:25.1.0'
60 | javadocDeps 'com.android.support:support-annotations:25.1.0'
61 | compile 'com.github.bumptech.glide:glide:3.6.1'
62 | }
63 |
64 | // Place it at the end of the file
65 | apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle'
66 | apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle'
--------------------------------------------------------------------------------
/tedbottompicker/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 |
--------------------------------------------------------------------------------
/tedbottompicker/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/tedbottompicker/src/main/java/gun0912/tedbottompicker/GridSpacingItemDecoration.java:
--------------------------------------------------------------------------------
1 | package gun0912.tedbottompicker;
2 |
3 | import android.graphics.Rect;
4 | import android.support.v7.widget.RecyclerView;
5 | import android.view.View;
6 |
7 | /**
8 | * Created by TedPark on 2016. 8. 30..
9 | */
10 |
11 | /**
12 | *https://gist.github.com/liangzhitao/e57df3c3232ee446d464
13 | */
14 | public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
15 |
16 | private int spanCount;
17 | private int spacing;
18 | private boolean includeEdge;
19 |
20 |
21 | public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) {
22 | this.spanCount = spanCount;
23 | this.spacing = spacing;
24 | this.includeEdge = includeEdge;
25 |
26 | }
27 |
28 | @Override
29 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
30 | int position = parent.getChildAdapterPosition(view) ; // item position
31 |
32 | if (position >= 0) {
33 | int column = position % spanCount; // item column
34 |
35 | if (includeEdge) {
36 | outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing)
37 | outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing)
38 |
39 | if (position < spanCount) { // top edge
40 | outRect.top = spacing;
41 | }
42 | outRect.bottom = spacing; // item bottom
43 | } else {
44 | outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing)
45 | outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing)
46 | if (position >= spanCount) {
47 | outRect.top = spacing; // item top
48 | }
49 | }
50 | } else {
51 | outRect.left = 0;
52 | outRect.right = 0;
53 | outRect.top = 0;
54 | outRect.bottom = 0;
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/tedbottompicker/src/main/java/gun0912/tedbottompicker/TedBottomPicker.java:
--------------------------------------------------------------------------------
1 | package gun0912.tedbottompicker;
2 |
3 | import android.Manifest;
4 | import android.app.Activity;
5 | import android.app.Dialog;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.content.pm.PackageManager;
9 | import android.content.pm.ResolveInfo;
10 | import android.graphics.drawable.Drawable;
11 | import android.media.MediaScannerConnection;
12 | import android.net.Uri;
13 | import android.os.Build;
14 | import android.os.Bundle;
15 | import android.os.Environment;
16 | import android.provider.MediaStore;
17 | import android.support.annotation.ColorRes;
18 | import android.support.annotation.DimenRes;
19 | import android.support.annotation.DrawableRes;
20 | import android.support.annotation.NonNull;
21 | import android.support.annotation.Nullable;
22 | import android.support.annotation.StringRes;
23 | import android.support.design.widget.BottomSheetBehavior;
24 | import android.support.design.widget.BottomSheetDialogFragment;
25 | import android.support.design.widget.CoordinatorLayout;
26 | import android.support.v4.app.FragmentManager;
27 | import android.support.v4.app.FragmentTransaction;
28 | import android.support.v4.content.ContextCompat;
29 | import android.support.v4.content.FileProvider;
30 | import android.support.v7.widget.GridLayoutManager;
31 | import android.support.v7.widget.RecyclerView;
32 | import android.text.TextUtils;
33 | import android.util.Log;
34 | import android.view.LayoutInflater;
35 | import android.view.View;
36 | import android.widget.Button;
37 | import android.widget.FrameLayout;
38 | import android.widget.HorizontalScrollView;
39 | import android.widget.ImageView;
40 | import android.widget.LinearLayout;
41 | import android.widget.TextView;
42 | import android.widget.Toast;
43 |
44 | import com.bumptech.glide.Glide;
45 |
46 | import java.io.File;
47 | import java.io.IOException;
48 | import java.text.SimpleDateFormat;
49 | import java.util.ArrayList;
50 | import java.util.Date;
51 | import java.util.List;
52 | import java.util.Locale;
53 |
54 | import gun0912.tedbottompicker.adapter.ImageGalleryAdapter;
55 | import gun0912.tedbottompicker.util.RealPathUtil;
56 |
57 | public class TedBottomPicker extends BottomSheetDialogFragment {
58 |
59 | public static final String TAG = "TedBottomPicker";
60 | static final int REQ_CODE_CAMERA = 1;
61 | static final int REQ_CODE_GALLERY = 2;
62 | static final String EXTRA_CAMERA_IMAGE_URI = "camera_image_uri";
63 | static final String EXTRA_CAMERA_SELECTED_IMAGE_URI = "camera_selected_image_uri";
64 | public Builder builder;
65 | ImageGalleryAdapter imageGalleryAdapter;
66 | View view_title_container;
67 | TextView tv_title;
68 | Button btn_done;
69 |
70 | FrameLayout selected_photos_container_frame;
71 | HorizontalScrollView hsv_selected_photos;
72 | LinearLayout selected_photos_container;
73 |
74 | TextView selected_photos_empty;
75 | View contentView;
76 | ArrayList selectedUriList;
77 | ArrayList tempUriList;
78 | private Uri cameraImageUri;
79 | private RecyclerView rc_gallery;
80 | private BottomSheetBehavior.BottomSheetCallback mBottomSheetBehaviorCallback = new BottomSheetBehavior.BottomSheetCallback() {
81 |
82 |
83 | @Override
84 | public void onStateChanged(@NonNull View bottomSheet, int newState) {
85 | Log.d(TAG, "onStateChanged() newState: " + newState);
86 | if (newState == BottomSheetBehavior.STATE_HIDDEN) {
87 | dismissAllowingStateLoss();
88 | }
89 |
90 |
91 | }
92 |
93 | @Override
94 | public void onSlide(@NonNull View bottomSheet, float slideOffset) {
95 | Log.d(TAG, "onSlide() slideOffset: " + slideOffset);
96 | }
97 | };
98 |
99 | @Override
100 | public void onCreate(@Nullable Bundle savedInstanceState) {
101 | super.onCreate(savedInstanceState);
102 |
103 | setupSavedInstanceState(savedInstanceState);
104 |
105 | // setRetainInstance(true);
106 | }
107 |
108 | private void setupSavedInstanceState(Bundle savedInstanceState) {
109 |
110 |
111 | if (savedInstanceState == null) {
112 | cameraImageUri = builder.selectedUri;
113 | tempUriList = builder.selectedUriList;
114 | } else {
115 | cameraImageUri = savedInstanceState.getParcelable(EXTRA_CAMERA_IMAGE_URI);
116 | tempUriList = savedInstanceState.getParcelableArrayList(EXTRA_CAMERA_SELECTED_IMAGE_URI);
117 | }
118 |
119 |
120 | }
121 |
122 |
123 | @Override
124 | public void onSaveInstanceState(Bundle outState) {
125 | outState.putParcelable(EXTRA_CAMERA_IMAGE_URI, cameraImageUri);
126 | outState.putParcelableArrayList(EXTRA_CAMERA_SELECTED_IMAGE_URI, selectedUriList);
127 | super.onSaveInstanceState(outState);
128 |
129 | }
130 |
131 | public void show(FragmentManager fragmentManager) {
132 |
133 | FragmentTransaction ft = fragmentManager.beginTransaction();
134 | ft.add(this, getTag());
135 | ft.commitAllowingStateLoss();
136 | }
137 |
138 | @Override
139 | public Dialog onCreateDialog(Bundle savedInstanceState) {
140 | return super.onCreateDialog(savedInstanceState);
141 | }
142 |
143 | @Override
144 | public void onViewCreated(View contentView, @Nullable Bundle savedInstanceState) {
145 | super.onViewCreated(contentView, savedInstanceState);
146 |
147 |
148 | }
149 |
150 | @Override
151 | public void setupDialog(Dialog dialog, int style) {
152 | super.setupDialog(dialog, style);
153 | contentView = View.inflate(getContext(), R.layout.tedbottompicker_content_view, null);
154 | dialog.setContentView(contentView);
155 | CoordinatorLayout.LayoutParams layoutParams =
156 | (CoordinatorLayout.LayoutParams) ((View) contentView.getParent()).getLayoutParams();
157 | CoordinatorLayout.Behavior behavior = layoutParams.getBehavior();
158 | if (behavior != null && behavior instanceof BottomSheetBehavior) {
159 | ((BottomSheetBehavior) behavior).setBottomSheetCallback(mBottomSheetBehaviorCallback);
160 | if (builder != null && builder.peekHeight > 0) {
161 | ((BottomSheetBehavior) behavior).setPeekHeight(builder.peekHeight);
162 | }
163 |
164 | }
165 |
166 | initView(contentView);
167 |
168 | setTitle();
169 | setRecyclerView();
170 | setSelectionView();
171 |
172 | selectedUriList = new ArrayList<>();
173 |
174 |
175 | if (builder.onImageSelectedListener != null && cameraImageUri != null) {
176 | addUri(cameraImageUri);
177 | } else if (builder.onMultiImageSelectedListener != null && tempUriList != null) {
178 | for (Uri uri : tempUriList) {
179 | addUri(uri);
180 | }
181 | }
182 |
183 | setDoneButton();
184 | checkMultiMode();
185 | }
186 |
187 | private void setSelectionView() {
188 |
189 | if (builder.emptySelectionText != null) {
190 | selected_photos_empty.setText(builder.emptySelectionText);
191 | }
192 |
193 |
194 | }
195 |
196 | private void setDoneButton() {
197 |
198 | if (builder.completeButtonText != null) {
199 | btn_done.setText(builder.completeButtonText);
200 | }
201 |
202 | btn_done.setOnClickListener(new View.OnClickListener() {
203 | @Override
204 | public void onClick(View view) {
205 |
206 | onMultiSelectComplete();
207 |
208 |
209 | }
210 | });
211 | }
212 |
213 | private void onMultiSelectComplete() {
214 |
215 | if (selectedUriList.size() < builder.selectMinCount) {
216 | String message;
217 | if (builder.selectMinCountErrorText != null) {
218 | message = builder.selectMinCountErrorText;
219 | } else {
220 | message = String.format(getResources().getString(R.string.select_min_count), builder.selectMinCount);
221 | }
222 |
223 | Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show();
224 | return;
225 | }
226 |
227 |
228 | builder.onMultiImageSelectedListener.onImagesSelected(selectedUriList);
229 | dismissAllowingStateLoss();
230 | }
231 |
232 | private void checkMultiMode() {
233 | if (!isMultiSelect()) {
234 | btn_done.setVisibility(View.GONE);
235 | selected_photos_container_frame.setVisibility(View.GONE);
236 | }
237 |
238 | }
239 |
240 | private void initView(View contentView) {
241 |
242 | view_title_container = contentView.findViewById(R.id.view_title_container);
243 | rc_gallery = (RecyclerView) contentView.findViewById(R.id.rc_gallery);
244 | tv_title = (TextView) contentView.findViewById(R.id.tv_title);
245 | btn_done = (Button) contentView.findViewById(R.id.btn_done);
246 |
247 | selected_photos_container_frame = (FrameLayout) contentView.findViewById(R.id.selected_photos_container_frame);
248 | hsv_selected_photos = (HorizontalScrollView) contentView.findViewById(R.id.hsv_selected_photos);
249 | selected_photos_container = (LinearLayout) contentView.findViewById(R.id.selected_photos_container);
250 | selected_photos_empty = (TextView) contentView.findViewById(R.id.selected_photos_empty);
251 | }
252 |
253 | private void setRecyclerView() {
254 |
255 | GridLayoutManager gridLayoutManager = new GridLayoutManager(getActivity(), 3);
256 | rc_gallery.setLayoutManager(gridLayoutManager);
257 | rc_gallery.addItemDecoration(new GridSpacingItemDecoration(gridLayoutManager.getSpanCount(), builder.spacing, false));
258 | updateAdapter();
259 | }
260 |
261 | private void updateAdapter() {
262 |
263 | imageGalleryAdapter = new ImageGalleryAdapter(
264 | getActivity()
265 | , builder);
266 | rc_gallery.setAdapter(imageGalleryAdapter);
267 | imageGalleryAdapter.setOnItemClickListener(new ImageGalleryAdapter.OnItemClickListener() {
268 | @Override
269 | public void onItemClick(View view, int position) {
270 |
271 | ImageGalleryAdapter.PickerTile pickerTile = imageGalleryAdapter.getItem(position);
272 |
273 | switch (pickerTile.getTileType()) {
274 | case ImageGalleryAdapter.PickerTile.CAMERA:
275 | startCameraIntent();
276 | break;
277 | case ImageGalleryAdapter.PickerTile.GALLERY:
278 | startGalleryIntent();
279 | break;
280 | case ImageGalleryAdapter.PickerTile.IMAGE:
281 | complete(pickerTile.getImageUri());
282 |
283 | break;
284 |
285 | default:
286 | errorMessage();
287 | }
288 |
289 | }
290 | });
291 | }
292 |
293 | private void complete(final Uri uri) {
294 | Log.d(TAG, "selected uri: " + uri.toString());
295 | //uri = Uri.parse(uri.toString());
296 | if (isMultiSelect()) {
297 |
298 |
299 | if (selectedUriList.contains(uri)) {
300 | removeImage(uri);
301 | } else {
302 | addUri(uri);
303 | }
304 |
305 |
306 | } else {
307 | builder.onImageSelectedListener.onImageSelected(uri);
308 | dismissAllowingStateLoss();
309 | }
310 |
311 | }
312 |
313 | private boolean addUri(final Uri uri) {
314 |
315 |
316 | if (selectedUriList.size() == builder.selectMaxCount) {
317 | String message;
318 | if (builder.selectMaxCountErrorText != null) {
319 | message = builder.selectMaxCountErrorText;
320 | } else {
321 | message = String.format(getResources().getString(R.string.select_max_count), builder.selectMaxCount);
322 | }
323 |
324 | Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT).show();
325 | return false;
326 | }
327 |
328 |
329 | selectedUriList.add(uri);
330 |
331 | final View rootView = LayoutInflater.from(getActivity()).inflate(R.layout.tedbottompicker_selected_item, null);
332 | ImageView thumbnail = (ImageView) rootView.findViewById(R.id.selected_photo);
333 | ImageView iv_close = (ImageView) rootView.findViewById(R.id.iv_close);
334 | rootView.setTag(uri);
335 |
336 | selected_photos_container.addView(rootView, 0);
337 |
338 |
339 | int px = (int) getResources().getDimension(R.dimen.tedbottompicker_selected_image_height);
340 | thumbnail.setLayoutParams(new FrameLayout.LayoutParams(px, px));
341 |
342 | if (builder.imageProvider == null) {
343 | Glide.with(getActivity())
344 | .load(uri)
345 | .thumbnail(0.1f)
346 | .dontAnimate()
347 | .centerCrop()
348 | .placeholder(R.drawable.ic_gallery)
349 | .error(R.drawable.img_error)
350 | .into(thumbnail);
351 | } else {
352 | builder.imageProvider.onProvideImage(thumbnail, uri);
353 | }
354 |
355 |
356 | if (builder.deSelectIconDrawable != null) {
357 | iv_close.setImageDrawable(builder.deSelectIconDrawable);
358 | }
359 |
360 | iv_close.setOnClickListener(new View.OnClickListener() {
361 | @Override
362 | public void onClick(View v) {
363 | removeImage(uri);
364 |
365 | }
366 | });
367 |
368 |
369 | updateSelectedView();
370 | imageGalleryAdapter.setSelectedUriList(selectedUriList, uri);
371 | return true;
372 |
373 | }
374 |
375 | private void removeImage(Uri uri) {
376 |
377 | selectedUriList.remove(uri);
378 |
379 |
380 | for (int i = 0; i < selected_photos_container.getChildCount(); i++) {
381 | View childView = selected_photos_container.getChildAt(i);
382 |
383 |
384 | if (childView.getTag().equals(uri)) {
385 | selected_photos_container.removeViewAt(i);
386 | break;
387 | }
388 | }
389 |
390 | updateSelectedView();
391 | imageGalleryAdapter.setSelectedUriList(selectedUriList, uri);
392 | }
393 |
394 | private void updateSelectedView() {
395 |
396 | if (selectedUriList == null || selectedUriList.size() == 0) {
397 | selected_photos_empty.setVisibility(View.VISIBLE);
398 | selected_photos_container.setVisibility(View.GONE);
399 | } else {
400 | selected_photos_empty.setVisibility(View.GONE);
401 | selected_photos_container.setVisibility(View.VISIBLE);
402 | }
403 |
404 | }
405 |
406 | private void startCameraIntent() {
407 | Intent cameraInent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
408 | if (cameraInent.resolveActivity(getActivity().getPackageManager()) == null) {
409 | errorMessage("This Application do not have Camera Application");
410 | return;
411 | }
412 |
413 | File imageFile = getImageFile();
414 | Uri photoURI = FileProvider.getUriForFile(getContext(), getContext().getApplicationContext().getPackageName() + ".provider", imageFile);
415 |
416 | List resolvedIntentActivities = getContext().getPackageManager().queryIntentActivities(cameraInent, PackageManager.MATCH_DEFAULT_ONLY);
417 | for (ResolveInfo resolvedIntentInfo : resolvedIntentActivities) {
418 | String packageName = resolvedIntentInfo.activityInfo.packageName;
419 | getContext().grantUriPermission(packageName, photoURI, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
420 | }
421 |
422 | cameraInent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
423 | startActivityForResult(cameraInent, REQ_CODE_CAMERA);
424 |
425 | }
426 |
427 | private File getImageFile() {
428 | // Create an image file name
429 | File imageFile = null;
430 | try {
431 | String timeStamp = new SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).format(new Date());
432 | String imageFileName = "JPEG_" + timeStamp + "_";
433 | File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
434 |
435 | if (!storageDir.exists())
436 | storageDir.mkdirs();
437 |
438 | imageFile = File.createTempFile(
439 | imageFileName, /* prefix */
440 | ".jpg", /* suffix */
441 | storageDir /* directory */
442 | );
443 |
444 |
445 | // Save a file: path for use with ACTION_VIEW intents
446 | cameraImageUri = Uri.fromFile(imageFile);
447 | } catch (IOException e) {
448 | e.printStackTrace();
449 | errorMessage("Could not create imageFile for camera");
450 | }
451 |
452 |
453 | return imageFile;
454 | }
455 |
456 | private void errorMessage(String message) {
457 | String errorMessage = message == null ? "Something wrong." : message;
458 |
459 | if (builder.onErrorListener == null) {
460 | Toast.makeText(getActivity(), errorMessage, Toast.LENGTH_SHORT).show();
461 | } else {
462 | builder.onErrorListener.onError(errorMessage);
463 | }
464 | }
465 |
466 | private void startGalleryIntent() {
467 | Intent galleryIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
468 | if (galleryIntent.resolveActivity(getActivity().getPackageManager()) == null) {
469 | errorMessage("This Application do not have Gallery Application");
470 | return;
471 | }
472 |
473 | startActivityForResult(galleryIntent, REQ_CODE_GALLERY);
474 |
475 | }
476 |
477 | private void errorMessage() {
478 | errorMessage(null);
479 | }
480 |
481 | private void setTitle() {
482 |
483 | if (!builder.showTitle) {
484 | tv_title.setVisibility(View.GONE);
485 |
486 | if (!isMultiSelect()) {
487 | view_title_container.setVisibility(View.GONE);
488 | }
489 |
490 | return;
491 | }
492 |
493 | if (!TextUtils.isEmpty(builder.title)) {
494 | tv_title.setText(builder.title);
495 | }
496 |
497 | if (builder.titleBackgroundResId > 0) {
498 | tv_title.setBackgroundResource(builder.titleBackgroundResId);
499 | }
500 |
501 | }
502 |
503 | private boolean isMultiSelect() {
504 | return builder.onMultiImageSelectedListener != null;
505 | }
506 |
507 | @Override
508 | public void onActivityResult(int requestCode, int resultCode, Intent data) {
509 | super.onActivityResult(requestCode, resultCode, data);
510 | if (resultCode == Activity.RESULT_OK) {
511 |
512 |
513 | switch (requestCode) {
514 | case REQ_CODE_GALLERY:
515 | onActivityResultGallery(data);
516 | break;
517 | case REQ_CODE_CAMERA:
518 | onActivityResultCamera(cameraImageUri);
519 | break;
520 |
521 | default:
522 | errorMessage();
523 | }
524 |
525 |
526 | }
527 |
528 | }
529 |
530 | private void onActivityResultCamera(final Uri cameraImageUri) {
531 |
532 | MediaScannerConnection.scanFile(getContext(), new String[]{cameraImageUri.getPath()}, new String[]{"image/jpeg"}, new MediaScannerConnection.MediaScannerConnectionClient() {
533 | @Override
534 | public void onMediaScannerConnected() {
535 |
536 | }
537 |
538 | @Override
539 | public void onScanCompleted(String s, Uri uri) {
540 | getActivity().runOnUiThread(new Runnable() {
541 | @Override
542 | public void run() {
543 | updateAdapter();
544 | complete(cameraImageUri);
545 | }
546 | });
547 |
548 | }
549 | });
550 | }
551 |
552 |
553 | private void onActivityResultGallery(Intent data) {
554 | Uri temp = data.getData();
555 |
556 | if (temp == null) {
557 | errorMessage();
558 | }
559 |
560 | String realPath = RealPathUtil.getRealPath(getActivity(), temp);
561 |
562 | Uri selectedImageUri = null;
563 | try {
564 | selectedImageUri = Uri.fromFile(new File(realPath));
565 | } catch (Exception ex) {
566 | selectedImageUri = Uri.parse(realPath);
567 | }
568 |
569 | complete(selectedImageUri);
570 |
571 | }
572 |
573 |
574 | public interface OnMultiImageSelectedListener {
575 | void onImagesSelected(ArrayList uriList);
576 | }
577 |
578 | public interface OnImageSelectedListener {
579 | void onImageSelected(Uri uri);
580 | }
581 |
582 | public interface OnErrorListener {
583 | void onError(String message);
584 | }
585 |
586 | public interface ImageProvider {
587 | void onProvideImage(ImageView imageView, Uri imageUri);
588 | }
589 |
590 | public static class Builder {
591 |
592 | public Context context;
593 | public int previewMaxCount = 25;
594 | public Drawable cameraTileDrawable;
595 | public Drawable galleryTileDrawable;
596 |
597 | public Drawable deSelectIconDrawable;
598 | public Drawable selectedForegroundDrawable;
599 |
600 | public int spacing = 1;
601 | public OnImageSelectedListener onImageSelectedListener;
602 | public OnMultiImageSelectedListener onMultiImageSelectedListener;
603 | public OnErrorListener onErrorListener;
604 | public ImageProvider imageProvider;
605 | public boolean showCamera = true;
606 | public boolean showGallery = true;
607 | public int peekHeight = -1;
608 | public int cameraTileBackgroundResId = R.color.tedbottompicker_camera;
609 | public int galleryTileBackgroundResId = R.color.tedbottompicker_gallery;
610 |
611 | public String title;
612 | public boolean showTitle = true;
613 | public int titleBackgroundResId;
614 |
615 | public int selectMaxCount = Integer.MAX_VALUE;
616 | public int selectMinCount = 0;
617 |
618 |
619 | public String completeButtonText;
620 | public String emptySelectionText;
621 | public String selectMaxCountErrorText;
622 | public String selectMinCountErrorText;
623 |
624 |
625 | ArrayList selectedUriList;
626 | Uri selectedUri;
627 |
628 | public Builder(@NonNull Context context) {
629 |
630 | this.context = context;
631 |
632 | setCameraTile(R.drawable.ic_camera);
633 | setGalleryTile(R.drawable.ic_gallery);
634 | setSpacingResId(R.dimen.tedbottompicker_grid_layout_margin);
635 | }
636 |
637 | public Builder setCameraTile(@DrawableRes int cameraTileResId) {
638 | setCameraTile(ContextCompat.getDrawable(context, cameraTileResId));
639 | return this;
640 | }
641 |
642 | public Builder setCameraTile(Drawable cameraTileDrawable) {
643 | this.cameraTileDrawable = cameraTileDrawable;
644 | return this;
645 | }
646 |
647 | public Builder setGalleryTile(@DrawableRes int galleryTileResId) {
648 | setGalleryTile(ContextCompat.getDrawable(context, galleryTileResId));
649 | return this;
650 | }
651 |
652 | public Builder setGalleryTile(Drawable galleryTileDrawable) {
653 | this.galleryTileDrawable = galleryTileDrawable;
654 | return this;
655 | }
656 |
657 | public Builder setSpacingResId(@DimenRes int dimenResId) {
658 | this.spacing = context.getResources().getDimensionPixelSize(dimenResId);
659 | return this;
660 | }
661 |
662 | public Builder setDeSelectIcon(@DrawableRes int deSelectIconResId) {
663 | setDeSelectIcon(ContextCompat.getDrawable(context, deSelectIconResId));
664 | return this;
665 | }
666 |
667 | public Builder setDeSelectIcon(Drawable deSelectIconDrawable) {
668 | this.deSelectIconDrawable = deSelectIconDrawable;
669 | return this;
670 | }
671 |
672 | public Builder setSelectedForeground(@DrawableRes int selectedForegroundResId) {
673 | setSelectedForeground(ContextCompat.getDrawable(context, selectedForegroundResId));
674 | return this;
675 | }
676 |
677 | public Builder setSelectedForeground(Drawable selectedForegroundDrawable) {
678 | this.selectedForegroundDrawable = selectedForegroundDrawable;
679 | return this;
680 | }
681 |
682 | public Builder setPreviewMaxCount(int previewMaxCount) {
683 | this.previewMaxCount = previewMaxCount;
684 | return this;
685 | }
686 |
687 | public Builder setSelectMaxCount(int selectMaxCount) {
688 | this.selectMaxCount = selectMaxCount;
689 | return this;
690 | }
691 |
692 | public Builder setSelectMinCount(int selectMinCount) {
693 | this.selectMinCount = selectMinCount;
694 | return this;
695 | }
696 |
697 | public Builder setOnImageSelectedListener(OnImageSelectedListener onImageSelectedListener) {
698 | this.onImageSelectedListener = onImageSelectedListener;
699 | return this;
700 | }
701 |
702 | public Builder setOnMultiImageSelectedListener(OnMultiImageSelectedListener onMultiImageSelectedListener) {
703 | this.onMultiImageSelectedListener = onMultiImageSelectedListener;
704 | return this;
705 | }
706 |
707 |
708 | public Builder setOnErrorListener(OnErrorListener onErrorListener) {
709 | this.onErrorListener = onErrorListener;
710 | return this;
711 | }
712 |
713 | public Builder showCameraTile(boolean showCamera) {
714 | this.showCamera = showCamera;
715 | return this;
716 | }
717 |
718 | public Builder showGalleryTile(boolean showGallery) {
719 | this.showGallery = showGallery;
720 | return this;
721 | }
722 |
723 | public Builder setSpacing(int spacing) {
724 | this.spacing = spacing;
725 | return this;
726 | }
727 |
728 | public Builder setPeekHeight(int peekHeight) {
729 | this.peekHeight = peekHeight;
730 | return this;
731 | }
732 |
733 | public Builder setPeekHeightResId(@DimenRes int dimenResId) {
734 | this.peekHeight = context.getResources().getDimensionPixelSize(dimenResId);
735 | return this;
736 | }
737 |
738 | public Builder setCameraTileBackgroundResId(@ColorRes int colorResId) {
739 | this.cameraTileBackgroundResId = colorResId;
740 | return this;
741 | }
742 |
743 | public Builder setGalleryTileBackgroundResId(@ColorRes int colorResId) {
744 | this.galleryTileBackgroundResId = colorResId;
745 | return this;
746 | }
747 |
748 | public Builder setTitle(String title) {
749 | this.title = title;
750 | return this;
751 | }
752 |
753 | public Builder setTitle(@StringRes int stringResId) {
754 | this.title = context.getResources().getString(stringResId);
755 | return this;
756 | }
757 |
758 | public Builder showTitle(boolean showTitle) {
759 | this.showTitle = showTitle;
760 | return this;
761 | }
762 |
763 | public Builder setCompleteButtonText(String completeButtonText) {
764 | this.completeButtonText = completeButtonText;
765 | return this;
766 | }
767 |
768 | public Builder setCompleteButtonText(@StringRes int completeButtonResId) {
769 | this.completeButtonText = context.getResources().getString(completeButtonResId);
770 | return this;
771 | }
772 |
773 | public Builder setEmptySelectionText(String emptySelectionText) {
774 | this.emptySelectionText = emptySelectionText;
775 | return this;
776 | }
777 |
778 | public Builder setEmptySelectionText(@StringRes int emptySelectionResId) {
779 | this.emptySelectionText = context.getResources().getString(emptySelectionResId);
780 | return this;
781 | }
782 |
783 |
784 | public Builder setSelectMaxCountErrorText(String selectMaxCountErrorText) {
785 | this.selectMaxCountErrorText = selectMaxCountErrorText;
786 | return this;
787 | }
788 |
789 | public Builder setSelectMaxCountErrorText(@StringRes int selectMaxCountErrorResId) {
790 | this.selectMaxCountErrorText = context.getResources().getString(selectMaxCountErrorResId);
791 | return this;
792 | }
793 |
794 | public Builder setSelectMinCountErrorText(String selectMinCountErrorText) {
795 | this.selectMinCountErrorText = selectMinCountErrorText;
796 | return this;
797 | }
798 |
799 | public Builder setSelectMinCountErrorText(@StringRes int selectMinCountErrorResId) {
800 | this.selectMinCountErrorText = context.getResources().getString(selectMinCountErrorResId);
801 | return this;
802 | }
803 |
804 |
805 | public Builder setTitleBackgroundResId(@ColorRes int colorResId) {
806 | this.titleBackgroundResId = colorResId;
807 | return this;
808 | }
809 |
810 | public Builder setImageProvider(ImageProvider imageProvider) {
811 | this.imageProvider = imageProvider;
812 | return this;
813 | }
814 |
815 |
816 | public Builder setSelectedUriList(ArrayList selectedUriList) {
817 | this.selectedUriList = selectedUriList;
818 | return this;
819 | }
820 |
821 | public Builder setSelectedUri(Uri selectedUri) {
822 | this.selectedUri = selectedUri;
823 | return this;
824 | }
825 |
826 | public TedBottomPicker create() {
827 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
828 | && ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
829 | throw new RuntimeException("Missing required WRITE_EXTERNAL_STORAGE permission. Did you remember to request it first?");
830 | }
831 |
832 | if (onImageSelectedListener == null && onMultiImageSelectedListener == null) {
833 | throw new RuntimeException("You have to use setOnImageSelectedListener() or setOnMultiImageSelectedListener() for receive selected Uri");
834 | }
835 |
836 | TedBottomPicker customBottomSheetDialogFragment = new TedBottomPicker();
837 |
838 | customBottomSheetDialogFragment.builder = this;
839 | return customBottomSheetDialogFragment;
840 | }
841 |
842 |
843 | }
844 |
845 |
846 | }
847 |
--------------------------------------------------------------------------------
/tedbottompicker/src/main/java/gun0912/tedbottompicker/adapter/ImageGalleryAdapter.java:
--------------------------------------------------------------------------------
1 | package gun0912.tedbottompicker.adapter;
2 |
3 | import android.content.Context;
4 | import android.database.Cursor;
5 | import android.graphics.drawable.Drawable;
6 | import android.net.Uri;
7 | import android.provider.MediaStore;
8 | import android.support.annotation.IntDef;
9 | import android.support.annotation.NonNull;
10 | import android.support.annotation.Nullable;
11 | import android.support.v4.content.ContextCompat;
12 | import android.support.v7.widget.RecyclerView;
13 | import android.view.View;
14 | import android.view.ViewGroup;
15 | import android.widget.FrameLayout;
16 |
17 | import com.bumptech.glide.Glide;
18 |
19 | import java.io.File;
20 | import java.lang.annotation.Retention;
21 | import java.lang.annotation.RetentionPolicy;
22 | import java.util.ArrayList;
23 |
24 | import gun0912.tedbottompicker.R;
25 | import gun0912.tedbottompicker.TedBottomPicker;
26 | import gun0912.tedbottompicker.view.TedSquareFrameLayout;
27 | import gun0912.tedbottompicker.view.TedSquareImageView;
28 |
29 | /**
30 | * Created by TedPark on 2016. 8. 30..
31 | */
32 | public class ImageGalleryAdapter extends RecyclerView.Adapter {
33 |
34 |
35 | ArrayList pickerTiles;
36 | Context context;
37 | TedBottomPicker.Builder builder;
38 | OnItemClickListener onItemClickListener;
39 | ArrayList selectedUriList;
40 |
41 |
42 | public ImageGalleryAdapter(Context context, TedBottomPicker.Builder builder) {
43 |
44 | this.context = context;
45 | this.builder = builder;
46 |
47 | pickerTiles = new ArrayList<>();
48 | selectedUriList = new ArrayList<>();
49 |
50 | if (builder.showCamera) {
51 | pickerTiles.add(new PickerTile(PickerTile.CAMERA));
52 | }
53 |
54 | if (builder.showGallery) {
55 | pickerTiles.add(new PickerTile(PickerTile.GALLERY));
56 | }
57 |
58 | Cursor imageCursor = null;
59 | try {
60 | final String[] columns = {MediaStore.Images.Media.DATA, MediaStore.Images.ImageColumns.ORIENTATION};
61 | final String orderBy = MediaStore.Images.Media.DATE_ADDED + " DESC";
62 |
63 |
64 | imageCursor = context.getApplicationContext().getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, columns, null, null, orderBy);
65 | //imageCursor = sContext.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, columns, null, null, orderBy);
66 |
67 |
68 | if (imageCursor != null) {
69 |
70 | int count = 0;
71 | while (imageCursor.moveToNext() && count < builder.previewMaxCount) {
72 | String imageLocation = imageCursor.getString(imageCursor.getColumnIndex(MediaStore.Images.Media.DATA));
73 | File imageFile = new File(imageLocation);
74 | pickerTiles.add(new PickerTile(Uri.fromFile(imageFile)));
75 | count++;
76 |
77 | }
78 |
79 | }
80 |
81 | } catch (Exception e) {
82 | e.printStackTrace();
83 | } finally {
84 | if (imageCursor != null && !imageCursor.isClosed()) {
85 | imageCursor.close();
86 | }
87 | }
88 |
89 |
90 | }
91 |
92 | public void setSelectedUriList(ArrayList selectedUriList, Uri uri) {
93 | this.selectedUriList = selectedUriList;
94 |
95 | int position = -1;
96 |
97 |
98 | PickerTile pickerTile;
99 | for (int i = 0; i < pickerTiles.size(); i++) {
100 | pickerTile = pickerTiles.get(i);
101 | if (pickerTile.isImageTile() && pickerTile.getImageUri().equals(uri)) {
102 | position = i;
103 | break;
104 | }
105 |
106 | }
107 |
108 |
109 | if (position > 0) {
110 | notifyItemChanged(position);
111 | }
112 |
113 |
114 | }
115 |
116 | @Override
117 | public GalleryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
118 | View view = View.inflate(context, R.layout.tedbottompicker_grid_item, null);
119 | final GalleryViewHolder holder = new GalleryViewHolder(view);
120 |
121 |
122 | return holder;
123 | }
124 |
125 | @Override
126 | public void onBindViewHolder(final GalleryViewHolder holder, final int position) {
127 |
128 | PickerTile pickerTile = getItem(position);
129 |
130 |
131 | boolean isSelected = false;
132 |
133 | if (pickerTile.isCameraTile()) {
134 | holder.iv_thumbnail.setBackgroundResource(builder.cameraTileBackgroundResId);
135 | holder.iv_thumbnail.setImageDrawable(builder.cameraTileDrawable);
136 | } else if (pickerTile.isGalleryTile()) {
137 | holder.iv_thumbnail.setBackgroundResource(builder.galleryTileBackgroundResId);
138 | holder.iv_thumbnail.setImageDrawable(builder.galleryTileDrawable);
139 |
140 | } else {
141 | Uri uri = pickerTile.getImageUri();
142 | if (builder.imageProvider == null) {
143 | Glide.with(context)
144 | .load(uri)
145 | .thumbnail(0.1f)
146 | .dontAnimate()
147 | .centerCrop()
148 | .placeholder(R.drawable.ic_gallery)
149 | .error(R.drawable.img_error)
150 | .into(holder.iv_thumbnail);
151 | } else {
152 | builder.imageProvider.onProvideImage(holder.iv_thumbnail, uri);
153 | }
154 |
155 |
156 | isSelected = selectedUriList.contains(uri);
157 |
158 |
159 | }
160 |
161 |
162 | if (holder.root instanceof FrameLayout) {
163 |
164 | Drawable foregroundDrawable;
165 |
166 | if(builder.selectedForegroundDrawable!=null){
167 | foregroundDrawable = builder.selectedForegroundDrawable;
168 | }else{
169 | foregroundDrawable = ContextCompat.getDrawable(context,R.drawable.gallery_photo_selected);
170 | }
171 |
172 | ((FrameLayout) holder.root).setForeground(isSelected ? foregroundDrawable : null);
173 | }
174 |
175 |
176 | if (onItemClickListener != null) {
177 | holder.itemView.setOnClickListener(new View.OnClickListener() {
178 | @Override
179 | public void onClick(View view) {
180 | onItemClickListener.onItemClick(holder.itemView, position);
181 | }
182 | });
183 | }
184 | }
185 |
186 | public PickerTile getItem(int position) {
187 | return pickerTiles.get(position);
188 | }
189 |
190 | @Override
191 | public int getItemCount() {
192 | return pickerTiles.size();
193 | }
194 |
195 | public void setOnItemClickListener(
196 | OnItemClickListener onItemClickListener) {
197 | this.onItemClickListener = onItemClickListener;
198 | }
199 |
200 | public interface OnItemClickListener {
201 | public void onItemClick(View view, int position);
202 | }
203 |
204 |
205 | public static class PickerTile {
206 |
207 | public static final int IMAGE = 1;
208 | public static final int CAMERA = 2;
209 | public static final int GALLERY = 3;
210 | protected final Uri imageUri;
211 | protected final
212 | @TileType
213 | int tileType;
214 |
215 | PickerTile(@SpecialTileType int tileType) {
216 | this(null, tileType);
217 | }
218 |
219 | protected PickerTile(@Nullable Uri imageUri, @TileType int tileType) {
220 | this.imageUri = imageUri;
221 | this.tileType = tileType;
222 | }
223 |
224 | PickerTile(@NonNull Uri imageUri) {
225 | this(imageUri, IMAGE);
226 | }
227 |
228 | @Nullable
229 | public Uri getImageUri() {
230 | return imageUri;
231 | }
232 |
233 | @TileType
234 | public int getTileType() {
235 | return tileType;
236 | }
237 |
238 | @Override
239 | public String toString() {
240 | if (isImageTile()) {
241 | return "ImageTile: " + imageUri;
242 | } else if (isCameraTile()) {
243 | return "CameraTile";
244 | } else if (isGalleryTile()) {
245 | return "PickerTile";
246 | } else {
247 | return "Invalid item";
248 | }
249 | }
250 |
251 | public boolean isImageTile() {
252 | return tileType == IMAGE;
253 | }
254 |
255 | public boolean isCameraTile() {
256 | return tileType == CAMERA;
257 | }
258 |
259 | public boolean isGalleryTile() {
260 | return tileType == GALLERY;
261 | }
262 |
263 | @IntDef({IMAGE, CAMERA, GALLERY})
264 | @Retention(RetentionPolicy.SOURCE)
265 | public @interface TileType {
266 | }
267 |
268 | @IntDef({CAMERA, GALLERY})
269 | @Retention(RetentionPolicy.SOURCE)
270 | public @interface SpecialTileType {
271 | }
272 | }
273 |
274 | class GalleryViewHolder extends RecyclerView.ViewHolder {
275 |
276 | TedSquareFrameLayout root;
277 |
278 |
279 | TedSquareImageView iv_thumbnail;
280 |
281 | public GalleryViewHolder(View view) {
282 | super(view);
283 | root = (TedSquareFrameLayout) view.findViewById(R.id.root);
284 | iv_thumbnail = (TedSquareImageView) view.findViewById(R.id.iv_thumbnail);
285 |
286 | }
287 |
288 | }
289 |
290 |
291 | }
292 |
--------------------------------------------------------------------------------
/tedbottompicker/src/main/java/gun0912/tedbottompicker/util/RealPathUtil.java:
--------------------------------------------------------------------------------
1 | package gun0912.tedbottompicker.util;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.ContentUris;
5 | import android.content.Context;
6 | import android.content.CursorLoader;
7 | import android.database.Cursor;
8 | import android.net.Uri;
9 | import android.os.Build;
10 | import android.os.Environment;
11 | import android.provider.DocumentsContract;
12 | import android.provider.MediaStore;
13 |
14 | /**
15 | * Created by TedPark on 2016. 11. 5..
16 | */
17 |
18 | public class RealPathUtil {
19 |
20 | public static String getRealPath(Context context, Uri uri) {
21 | String realPath;
22 | // SDK < API11
23 | if (Build.VERSION.SDK_INT < 11) {
24 | realPath = RealPathUtil.getRealPathFromURI_BelowAPI11(context, uri);
25 | }
26 |
27 | // SDK >= 11 && SDK < 19
28 | else if (Build.VERSION.SDK_INT < 19) {
29 | realPath = RealPathUtil.getRealPathFromURI_API11to18(context, uri);
30 | }
31 |
32 | // SDK > 19 (Android 4.4)
33 | else {
34 | realPath = RealPathUtil.getRealPathFromURI_API19(context, uri);
35 | }
36 |
37 | return realPath;
38 | }
39 |
40 | @SuppressLint("NewApi")
41 | public static String getRealPathFromURI_API19(final Context context, final Uri uri) {
42 |
43 | // check here to KITKAT or new version
44 | final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
45 |
46 | // DocumentProvider
47 | if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
48 |
49 | // ExternalStorageProvider
50 | if (isExternalStorageDocument(uri)) {
51 | final String docId = DocumentsContract.getDocumentId(uri);
52 | final String[] split = docId.split(":");
53 | final String type = split[0];
54 |
55 | if ("primary".equalsIgnoreCase(type)) {
56 | return Environment.getExternalStorageDirectory() + "/"
57 | + split[1];
58 | }
59 | }
60 | // DownloadsProvider
61 | else if (isDownloadsDocument(uri)) {
62 |
63 | final String id = DocumentsContract.getDocumentId(uri);
64 | final Uri contentUri = ContentUris.withAppendedId(
65 | Uri.parse("content://downloads/public_downloads"),
66 | Long.valueOf(id));
67 |
68 | return getDataColumn(context, contentUri, null, null);
69 | }
70 | // MediaProvider
71 | else if (isMediaDocument(uri)) {
72 | final String docId = DocumentsContract.getDocumentId(uri);
73 | final String[] split = docId.split(":");
74 | final String type = split[0];
75 |
76 | Uri contentUri = null;
77 | if ("image".equals(type)) {
78 | contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
79 | } else if ("video".equals(type)) {
80 | contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
81 | } else if ("audio".equals(type)) {
82 | contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
83 | }
84 |
85 | final String selection = "_id=?";
86 | final String[] selectionArgs = new String[] { split[1] };
87 |
88 | return getDataColumn(context, contentUri, selection,
89 | selectionArgs);
90 | }
91 | }
92 | // MediaStore (and general)
93 | else if ("content".equalsIgnoreCase(uri.getScheme())) {
94 |
95 | // Return the remote address
96 | if (isGooglePhotosUri(uri))
97 | return uri.getLastPathSegment();
98 |
99 | return getDataColumn(context, uri, null, null);
100 | }
101 | // File
102 | else if ("file".equalsIgnoreCase(uri.getScheme())) {
103 | return uri.getPath();
104 | }
105 |
106 | return null;
107 | }
108 |
109 | /**
110 | * Get the value of the data column for this Uri. This is useful for
111 | * MediaStore Uris, and other file-based ContentProviders.
112 | *
113 | * @param context
114 | * The context.
115 | * @param uri
116 | * The Uri to query.
117 | * @param selection
118 | * (Optional) Filter used in the query.
119 | * @param selectionArgs
120 | * (Optional) Selection arguments used in the query.
121 | * @return The value of the _data column, which is typically a file path.
122 | */
123 | public static String getDataColumn(Context context, Uri uri,
124 | String selection, String[] selectionArgs) {
125 |
126 | Cursor cursor = null;
127 | final String column = "_data";
128 | final String[] projection = { column };
129 |
130 | try {
131 | cursor = context.getContentResolver().query(uri, projection,
132 | selection, selectionArgs, null);
133 | if (cursor != null && cursor.moveToFirst()) {
134 | final int index = cursor.getColumnIndexOrThrow(column);
135 | return cursor.getString(index);
136 | }
137 | } finally {
138 | if (cursor != null)
139 | cursor.close();
140 | }
141 | return null;
142 | }
143 |
144 | /**
145 | * @param uri
146 | * The Uri to check.
147 | * @return Whether the Uri authority is ExternalStorageProvider.
148 | */
149 | public static boolean isExternalStorageDocument(Uri uri) {
150 | return "com.android.externalstorage.documents".equals(uri
151 | .getAuthority());
152 | }
153 |
154 | /**
155 | * @param uri
156 | * The Uri to check.
157 | * @return Whether the Uri authority is DownloadsProvider.
158 | */
159 | public static boolean isDownloadsDocument(Uri uri) {
160 | return "com.android.providers.downloads.documents".equals(uri
161 | .getAuthority());
162 | }
163 |
164 | /**
165 | * @param uri
166 | * The Uri to check.
167 | * @return Whether the Uri authority is MediaProvider.
168 | */
169 | public static boolean isMediaDocument(Uri uri) {
170 | return "com.android.providers.media.documents".equals(uri
171 | .getAuthority());
172 | }
173 |
174 | /**
175 | * @param uri
176 | * The Uri to check.
177 | * @return Whether the Uri authority is Google Photos.
178 | */
179 | public static boolean isGooglePhotosUri(Uri uri) {
180 | return "com.google.android.apps.photos.content".equals(uri
181 | .getAuthority());
182 | }
183 |
184 | @SuppressLint("NewApi")
185 | public static String getRealPathFromURI_API11to18(Context context, Uri contentUri) {
186 | String[] proj = { MediaStore.Images.Media.DATA };
187 | String result = null;
188 |
189 | CursorLoader cursorLoader = new CursorLoader(
190 | context,
191 | contentUri, proj, null, null, null);
192 | Cursor cursor = cursorLoader.loadInBackground();
193 |
194 | if(cursor != null){
195 | int column_index =
196 | cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
197 | cursor.moveToFirst();
198 | result = cursor.getString(column_index);
199 | }
200 | return result;
201 | }
202 |
203 | public static String getRealPathFromURI_BelowAPI11(Context context, Uri contentUri){
204 | String[] proj = { MediaStore.Images.Media.DATA };
205 | Cursor cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
206 | int column_index
207 | = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
208 | cursor.moveToFirst();
209 | return cursor.getString(column_index);
210 | }
211 | }
--------------------------------------------------------------------------------
/tedbottompicker/src/main/java/gun0912/tedbottompicker/view/TedEmptyRecyclerView.java:
--------------------------------------------------------------------------------
1 | package gun0912.tedbottompicker.view;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.NonNull;
5 | import android.support.annotation.Nullable;
6 | import android.support.v7.widget.RecyclerView;
7 | import android.util.AttributeSet;
8 | import android.view.View;
9 |
10 | public class TedEmptyRecyclerView extends RecyclerView {
11 | @Nullable
12 | View emptyView;
13 |
14 | public TedEmptyRecyclerView(Context context) {
15 | super(context);
16 | }
17 |
18 | public TedEmptyRecyclerView(Context context, AttributeSet attrs) {
19 | super(context, attrs);
20 | }
21 |
22 | public TedEmptyRecyclerView(Context context, AttributeSet attrs, int defStyle) {
23 | super(context, attrs, defStyle);
24 | }
25 |
26 | void checkIfEmpty() {
27 | if (emptyView != null) {
28 |
29 | emptyView.setVisibility(getAdapter().getItemCount() > 0 ? GONE : VISIBLE);
30 | }
31 | }
32 |
33 | final @NonNull
34 | AdapterDataObserver observer = new AdapterDataObserver() {
35 | @Override
36 | public void onChanged() {
37 | super.onChanged();
38 | checkIfEmpty();
39 | }
40 | };
41 |
42 |
43 |
44 | @Override
45 | public void setAdapter(@Nullable Adapter adapter) {
46 | final Adapter oldAdapter = getAdapter();
47 | if (oldAdapter != null) {
48 | oldAdapter.unregisterAdapterDataObserver(observer);
49 | }
50 | super.setAdapter(adapter);
51 | if (adapter != null) {
52 | adapter.registerAdapterDataObserver(observer);
53 | }
54 | }
55 |
56 | public void setEmptyView(@Nullable View emptyView) {
57 | this.emptyView = emptyView;
58 | checkIfEmpty();
59 | }
60 | }
--------------------------------------------------------------------------------
/tedbottompicker/src/main/java/gun0912/tedbottompicker/view/TedSquareFrameLayout.java:
--------------------------------------------------------------------------------
1 | package gun0912.tedbottompicker.view;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.util.AttributeSet;
6 | import android.widget.FrameLayout;
7 |
8 | import gun0912.tedbottompicker.R;
9 |
10 | /**
11 | * Created by Gil on 09/06/2014.
12 | */
13 | public class TedSquareFrameLayout extends FrameLayout {
14 |
15 |
16 | private static boolean mMatchHeightToWidth;
17 | private static boolean mMatchWidthToHeight;
18 |
19 | public TedSquareFrameLayout(Context context) {
20 | super(context);
21 | }
22 |
23 | public TedSquareFrameLayout(Context context, AttributeSet attrs) {
24 | super(context, attrs);
25 |
26 | TypedArray a = context.getTheme().obtainStyledAttributes(
27 | attrs,
28 | R.styleable.TedBottomPickerSquareView,
29 | 0, 0);
30 |
31 | try {
32 | mMatchHeightToWidth = a.getBoolean(R.styleable.TedBottomPickerSquareView_matchHeightToWidth, false);
33 | mMatchWidthToHeight = a.getBoolean(R.styleable.TedBottomPickerSquareView_matchWidthToHeight, false);
34 | } finally {
35 | a.recycle();
36 | }
37 | }
38 |
39 |
40 |
41 | //Squares the thumbnail
42 | @Override
43 | protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec){
44 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
45 | // Dlog.w("start: "+widthMeasureSpec+"x"+heightMeasureSpec);
46 | if(mMatchHeightToWidth){
47 | setMeasuredDimension(widthMeasureSpec, widthMeasureSpec);
48 | } else if(mMatchWidthToHeight){
49 | setMeasuredDimension(heightMeasureSpec, heightMeasureSpec);
50 | }
51 | }
52 |
53 | @Override
54 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
55 |
56 | if(mMatchHeightToWidth){
57 | super.onSizeChanged(w, w,oldw,oldh);
58 | } else if(mMatchWidthToHeight){
59 | super.onSizeChanged(h, h,oldw,oldh);
60 | }
61 |
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/tedbottompicker/src/main/java/gun0912/tedbottompicker/view/TedSquareImageView.java:
--------------------------------------------------------------------------------
1 | package gun0912.tedbottompicker.view;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Canvas;
6 | import android.graphics.drawable.Drawable;
7 | import android.util.AttributeSet;
8 | import android.widget.ImageView;
9 |
10 | import gun0912.tedbottompicker.R;
11 |
12 |
13 | /**
14 | * Created by Gil on 09/06/2014.
15 | */
16 | public class TedSquareImageView extends ImageView {
17 |
18 | String fit_mode;
19 | private Drawable foreground;
20 |
21 | public TedSquareImageView(Context context) {
22 | super(context);
23 | }
24 |
25 | public TedSquareImageView(Context context, AttributeSet attrs) {
26 | super(context, attrs);
27 |
28 | TypedArray a = context.getTheme().obtainStyledAttributes(
29 | attrs,
30 | R.styleable.TedBottomPickerImageView,
31 | 0, 0);
32 |
33 | Drawable foreground = a.getDrawable(R.styleable.TedBottomPickerImageView_foreground);
34 | if (foreground != null) {
35 | setForeground(foreground);
36 | }
37 |
38 |
39 | try {
40 | fit_mode = a.getString(R.styleable.TedBottomPickerImageView_fit_mode);
41 |
42 | } finally {
43 | a.recycle();
44 | }
45 | }
46 |
47 |
48 | //Squares the thumbnail
49 | @Override
50 | protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
51 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
52 |
53 |
54 | if ("height".equals(fit_mode)) {
55 | setMeasuredDimension(heightMeasureSpec, heightMeasureSpec);
56 |
57 | } else {
58 | setMeasuredDimension(widthMeasureSpec, widthMeasureSpec);
59 |
60 | }
61 |
62 |
63 | if (foreground != null) {
64 | foreground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
65 | invalidate();
66 | }
67 |
68 |
69 | }
70 |
71 |
72 | /**
73 | * Supply a Drawable that is to be rendered on top of all of the child views
74 | * in the frame layout.
75 | *
76 | * @param drawable The Drawable to be drawn on top of the children.
77 | */
78 | public void setForeground(Drawable drawable) {
79 | if (foreground == drawable) {
80 | return;
81 | }
82 | if (foreground != null) {
83 | foreground.setCallback(null);
84 | unscheduleDrawable(foreground);
85 | }
86 |
87 | foreground = drawable;
88 |
89 | if (drawable != null) {
90 | drawable.setCallback(this);
91 | if (drawable.isStateful()) {
92 | drawable.setState(getDrawableState());
93 | }
94 | }
95 | requestLayout();
96 | invalidate();
97 | }
98 |
99 |
100 | @Override
101 | protected boolean verifyDrawable(Drawable who) {
102 | return super.verifyDrawable(who) || who == foreground;
103 | }
104 |
105 | @Override
106 | public void jumpDrawablesToCurrentState() {
107 | super.jumpDrawablesToCurrentState();
108 | if (foreground != null)
109 | foreground.jumpToCurrentState();
110 | }
111 |
112 | @Override
113 | protected void drawableStateChanged() {
114 | super.drawableStateChanged();
115 | if (foreground != null && foreground.isStateful()) {
116 | foreground.setState(getDrawableState());
117 | }
118 | }
119 |
120 | @Override
121 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
122 | super.onSizeChanged(w, h, oldw, oldh);
123 | if (foreground != null) {
124 | foreground.setBounds(0, 0, w, h);
125 | invalidate();
126 | }
127 | }
128 |
129 | @Override
130 | public void draw(Canvas canvas) {
131 | super.draw(canvas);
132 |
133 | if (foreground != null) {
134 | foreground.draw(canvas);
135 | }
136 | }
137 |
138 |
139 | }
140 |
--------------------------------------------------------------------------------
/tedbottompicker/src/main/res/drawable-xhdpi/ic_clear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iammert/TedBottomPicker/8003e3065f1ee9037ca5d1f33f25b3d6e171a638/tedbottompicker/src/main/res/drawable-xhdpi/ic_clear.png
--------------------------------------------------------------------------------
/tedbottompicker/src/main/res/drawable-xxxhdpi/gallery_photo_selected.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
9 |
10 |
11 |
12 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/tedbottompicker/src/main/res/drawable-xxxhdpi/ic_camera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iammert/TedBottomPicker/8003e3065f1ee9037ca5d1f33f25b3d6e171a638/tedbottompicker/src/main/res/drawable-xxxhdpi/ic_camera.png
--------------------------------------------------------------------------------
/tedbottompicker/src/main/res/drawable-xxxhdpi/ic_gallery.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iammert/TedBottomPicker/8003e3065f1ee9037ca5d1f33f25b3d6e171a638/tedbottompicker/src/main/res/drawable-xxxhdpi/ic_gallery.png
--------------------------------------------------------------------------------
/tedbottompicker/src/main/res/drawable-xxxhdpi/img_error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iammert/TedBottomPicker/8003e3065f1ee9037ca5d1f33f25b3d6e171a638/tedbottompicker/src/main/res/drawable-xxxhdpi/img_error.png
--------------------------------------------------------------------------------
/tedbottompicker/src/main/res/layout/tedbottompicker_content_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
15 |
16 |
17 |
27 |
28 |
38 |
39 |
40 |
41 |
70 |
71 |
72 |
79 |
80 |
89 |
90 |
99 |
100 |
101 |
109 |
110 |
111 |
120 |
121 |
126 |
127 |
128 |
--------------------------------------------------------------------------------
/tedbottompicker/src/main/res/layout/tedbottompicker_grid_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/tedbottompicker/src/main/res/layout/tedbottompicker_selected_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
21 |
22 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/tedbottompicker/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/tedbottompicker/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #000
4 | #ccc
5 | #f0f0f0
6 |
7 |
8 |
--------------------------------------------------------------------------------
/tedbottompicker/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1dp
4 |
5 | 90dp
6 |
7 |
--------------------------------------------------------------------------------
/tedbottompicker/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Select Image
4 | No Image
5 | Done
6 |
7 |
8 | Already %1$d images selected
9 | You have to choice %1$d images
10 |
11 |
12 |
--------------------------------------------------------------------------------
/tedbottompicker/src/main/res/values/style.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/tedbottompicker/src/main/res/xml/provider_paths.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------