(128);
52 |
53 | private GalleryBitmapPool(int capacityBytes) {
54 | mPools = new SparseArrayBitmapPool[3];
55 | mPools[POOL_INDEX_SQUARE] = new SparseArrayBitmapPool(capacityBytes / 3, mSharedNodePool);
56 | mPools[POOL_INDEX_PHOTO] = new SparseArrayBitmapPool(capacityBytes / 3, mSharedNodePool);
57 | mPools[POOL_INDEX_MISC] = new SparseArrayBitmapPool(capacityBytes / 3, mSharedNodePool);
58 | mCapacityBytes = capacityBytes;
59 | }
60 |
61 | private static GalleryBitmapPool sInstance = new GalleryBitmapPool(CAPACITY_BYTES);
62 |
63 | public static GalleryBitmapPool getInstance() {
64 | return sInstance;
65 | }
66 |
67 | private SparseArrayBitmapPool getPoolForDimensions(int width, int height) {
68 | int index = getPoolIndexForDimensions(width, height);
69 | if (index == POOL_INDEX_NONE) {
70 | return null;
71 | } else {
72 | return mPools[index];
73 | }
74 | }
75 |
76 | private int getPoolIndexForDimensions(int width, int height) {
77 | if (width <= 0 || height <= 0) {
78 | return POOL_INDEX_NONE;
79 | }
80 | if (width == height) {
81 | return POOL_INDEX_SQUARE;
82 | }
83 | int min, max;
84 | if (width > height) {
85 | min = height;
86 | max = width;
87 | } else {
88 | min = width;
89 | max = height;
90 | }
91 | for (Point ar : COMMON_PHOTO_ASPECT_RATIOS) {
92 | if (min * ar.x == max * ar.y) {
93 | return POOL_INDEX_PHOTO;
94 | }
95 | }
96 | return POOL_INDEX_MISC;
97 | }
98 |
99 | /**
100 | * @return Capacity of the pool in bytes.
101 | */
102 | public synchronized int getCapacity() {
103 | return mCapacityBytes;
104 | }
105 |
106 | /**
107 | * @return Approximate total size in bytes of the bitmaps stored in the pool.
108 | */
109 | public int getSize() {
110 | // Note that this only returns an approximate size, since multiple threads
111 | // might be getting and putting Bitmaps from the pool and we lock at the
112 | // sub-pool level to avoid unnecessary blocking.
113 | int total = 0;
114 | for (SparseArrayBitmapPool p : mPools) {
115 | total += p.getSize();
116 | }
117 | return total;
118 | }
119 |
120 | /**
121 | * @return Bitmap from the pool with the desired height/width or null if none available.
122 | */
123 | public Bitmap get(int width, int height) {
124 | SparseArrayBitmapPool pool = getPoolForDimensions(width, height);
125 | if (pool == null) {
126 | return null;
127 | } else {
128 | return pool.get(width, height);
129 | }
130 | }
131 |
132 | /**
133 | * Adds the given bitmap to the pool.
134 | * @return Whether the bitmap was added to the pool.
135 | */
136 | public boolean put(Bitmap b) {
137 | if (b == null || b.getConfig() != Bitmap.Config.ARGB_8888) {
138 | return false;
139 | }
140 | SparseArrayBitmapPool pool = getPoolForDimensions(b.getWidth(), b.getHeight());
141 | if (pool == null) {
142 | b.recycle();
143 | return false;
144 | } else {
145 | return pool.put(b);
146 | }
147 | }
148 |
149 | /**
150 | * Empty the pool, recycling all the bitmaps currently in it.
151 | */
152 | public void clear() {
153 | for (SparseArrayBitmapPool p : mPools) {
154 | p.clear();
155 | }
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/library/src/main/java/com/cncoderx/photopicker/io/Pools.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2009 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.cncoderx.photopicker.io;
18 |
19 | /**
20 | * Helper class for crating pools of objects. An example use looks like this:
21 | *
22 | * public class MyPooledClass {
23 | *
24 | * private static final SynchronizedPool sPool =
25 | * new SynchronizedPool(10);
26 | *
27 | * public static MyPooledClass obtain() {
28 | * MyPooledClass instance = sPool.acquire();
29 | * return (instance != null) ? instance : new MyPooledClass();
30 | * }
31 | *
32 | * public void recycle() {
33 | * // Clear state if needed.
34 | * sPool.release(this);
35 | * }
36 | *
37 | * . . .
38 | * }
39 | *
40 | *
41 | * @hide
42 | */
43 | public final class Pools {
44 |
45 | /**
46 | * Interface for managing a pool of objects.
47 | *
48 | * @param The pooled type.
49 | */
50 | public static interface Pool {
51 |
52 | /**
53 | * @return An instance from the pool if such, null otherwise.
54 | */
55 | public T acquire();
56 |
57 | /**
58 | * Release an instance to the pool.
59 | *
60 | * @param instance The instance to release.
61 | * @return Whether the instance was put in the pool.
62 | *
63 | * @throws IllegalStateException If the instance is already in the pool.
64 | */
65 | public boolean release(T instance);
66 | }
67 |
68 | private Pools() {
69 | /* do nothing - hiding constructor */
70 | }
71 |
72 | /**
73 | * Simple (non-synchronized) pool of objects.
74 | *
75 | * @param The pooled type.
76 | */
77 | public static class SimplePool implements Pool {
78 | private final Object[] mPool;
79 |
80 | private int mPoolSize;
81 |
82 | /**
83 | * Creates a new instance.
84 | *
85 | * @param maxPoolSize The max pool size.
86 | *
87 | * @throws IllegalArgumentException If the max pool size is less than zero.
88 | */
89 | public SimplePool(int maxPoolSize) {
90 | if (maxPoolSize <= 0) {
91 | throw new IllegalArgumentException("The max pool size must be > 0");
92 | }
93 | mPool = new Object[maxPoolSize];
94 | }
95 |
96 | @Override
97 | @SuppressWarnings("unchecked")
98 | public T acquire() {
99 | if (mPoolSize > 0) {
100 | final int lastPooledIndex = mPoolSize - 1;
101 | T instance = (T) mPool[lastPooledIndex];
102 | mPool[lastPooledIndex] = null;
103 | mPoolSize--;
104 | return instance;
105 | }
106 | return null;
107 | }
108 |
109 | @Override
110 | public boolean release(T instance) {
111 | if (isInPool(instance)) {
112 | throw new IllegalStateException("Already in the pool!");
113 | }
114 | if (mPoolSize < mPool.length) {
115 | mPool[mPoolSize] = instance;
116 | mPoolSize++;
117 | return true;
118 | }
119 | return false;
120 | }
121 |
122 | private boolean isInPool(T instance) {
123 | for (int i = 0; i < mPoolSize; i++) {
124 | if (mPool[i] == instance) {
125 | return true;
126 | }
127 | }
128 | return false;
129 | }
130 | }
131 |
132 | /**
133 | * Synchronized) pool of objects.
134 | *
135 | * @param The pooled type.
136 | */
137 | public static class SynchronizedPool extends SimplePool {
138 | private final Object mLock = new Object();
139 |
140 | /**
141 | * Creates a new instance.
142 | *
143 | * @param maxPoolSize The max pool size.
144 | *
145 | * @throws IllegalArgumentException If the max pool size is less than zero.
146 | */
147 | public SynchronizedPool(int maxPoolSize) {
148 | super(maxPoolSize);
149 | }
150 |
151 | @Override
152 | public T acquire() {
153 | synchronized (mLock) {
154 | return super.acquire();
155 | }
156 | }
157 |
158 | @Override
159 | public boolean release(T element) {
160 | synchronized (mLock) {
161 | return super.release(element);
162 | }
163 | }
164 | }
165 | }
--------------------------------------------------------------------------------
/library/src/main/java/com/cncoderx/photopicker/io/SparseArrayBitmapPool.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.cncoderx.photopicker.io;
18 |
19 | import android.graphics.Bitmap;
20 | import android.util.SparseArray;
21 |
22 | /**
23 | * Bitmap pool backed by a sparse array indexing linked lists of bitmaps
24 | * sharing the same width. Performance will degrade if using this to store
25 | * many bitmaps with the same width but many different heights.
26 | */
27 | public class SparseArrayBitmapPool {
28 |
29 | private int mCapacityBytes;
30 | private SparseArray mStore = new SparseArray();
31 | private int mSizeBytes = 0;
32 |
33 | private Pools.Pool mNodePool;
34 | private Node mPoolNodesHead = null;
35 | private Node mPoolNodesTail = null;
36 |
37 | protected static class Node {
38 | Bitmap bitmap;
39 |
40 | // Each node is part of two doubly linked lists:
41 | // - A pool-level list (accessed by mPoolNodesHead and mPoolNodesTail)
42 | // that is used for FIFO eviction of nodes when the pool gets full.
43 | // - A bucket-level list for each index of the sparse array, so that
44 | // each index can store more than one item.
45 | Node prevInBucket;
46 | Node nextInBucket;
47 | Node nextInPool;
48 | Node prevInPool;
49 | }
50 |
51 | /**
52 | * @param capacityBytes Maximum capacity of the pool in bytes.
53 | * @param nodePool Shared pool to use for recycling linked list nodes, or null.
54 | */
55 | public SparseArrayBitmapPool(int capacityBytes, Pools.Pool nodePool) {
56 | mCapacityBytes = capacityBytes;
57 | if (nodePool == null) {
58 | mNodePool = new Pools.SimplePool(32);
59 | } else {
60 | mNodePool = nodePool;
61 | }
62 | }
63 |
64 | /**
65 | * Set the maximum capacity of the pool, and if necessary trim it down to size.
66 | */
67 | public synchronized void setCapacity(int capacityBytes) {
68 | mCapacityBytes = capacityBytes;
69 |
70 | // No-op unless current size exceeds the new capacity.
71 | freeUpCapacity(0);
72 | }
73 |
74 | private void freeUpCapacity(int bytesNeeded) {
75 | int targetSize = mCapacityBytes - bytesNeeded;
76 | // Repeatedly remove the oldest node until we have freed up at least bytesNeeded.
77 | while (mPoolNodesTail != null && mSizeBytes > targetSize) {
78 | unlinkAndRecycleNode(mPoolNodesTail, true);
79 | }
80 | }
81 |
82 | private void unlinkAndRecycleNode(Node n, boolean recycleBitmap) {
83 | // Unlink the node from its sparse array bucket list.
84 | if (n.prevInBucket != null) {
85 | // This wasn't the head, update the previous node.
86 | n.prevInBucket.nextInBucket = n.nextInBucket;
87 | } else {
88 | // This was the head of the bucket, replace it with the next node.
89 | mStore.put(n.bitmap.getWidth(), n.nextInBucket);
90 | }
91 | if (n.nextInBucket != null) {
92 | // This wasn't the tail, update the next node.
93 | n.nextInBucket.prevInBucket = n.prevInBucket;
94 | }
95 |
96 | // Unlink the node from the pool-wide list.
97 | if (n.prevInPool != null) {
98 | // This wasn't the head, update the previous node.
99 | n.prevInPool.nextInPool = n.nextInPool;
100 | } else {
101 | // This was the head of the pool-wide list, update the head pointer.
102 | mPoolNodesHead = n.nextInPool;
103 | }
104 | if (n.nextInPool != null) {
105 | // This wasn't the tail, update the next node.
106 | n.nextInPool.prevInPool = n.prevInPool;
107 | } else {
108 | // This was the tail, update the tail pointer.
109 | mPoolNodesTail = n.prevInPool;
110 | }
111 |
112 | // Recycle the node.
113 | n.nextInBucket = null;
114 | n.nextInPool = null;
115 | n.prevInBucket = null;
116 | n.prevInPool = null;
117 | mSizeBytes -= n.bitmap.getByteCount();
118 | if (recycleBitmap) n.bitmap.recycle();
119 | n.bitmap = null;
120 | mNodePool.release(n);
121 | }
122 |
123 | /**
124 | * @return Capacity of the pool in bytes.
125 | */
126 | public synchronized int getCapacity() {
127 | return mCapacityBytes;
128 | }
129 |
130 | /**
131 | * @return Total size in bytes of the bitmaps stored in the pool.
132 | */
133 | public synchronized int getSize() {
134 | return mSizeBytes;
135 | }
136 |
137 | /**
138 | * @return Bitmap from the pool with the desired height/width or null if none available.
139 | */
140 | public synchronized Bitmap get(int width, int height) {
141 | Node cur = mStore.get(width);
142 |
143 | // Traverse the list corresponding to the width bucket in the
144 | // sparse array, and unlink and return the first bitmap that
145 | // also has the correct height.
146 | while (cur != null) {
147 | if (cur.bitmap.getHeight() == height) {
148 | Bitmap b = cur.bitmap;
149 | unlinkAndRecycleNode(cur, false);
150 | return b;
151 | }
152 | cur = cur.nextInBucket;
153 | }
154 | return null;
155 | }
156 |
157 | /**
158 | * Adds the given bitmap to the pool.
159 | * @return Whether the bitmap was added to the pool.
160 | */
161 | public synchronized boolean put(Bitmap b) {
162 | if (b == null) {
163 | return false;
164 | }
165 |
166 | // Ensure there is enough room to contain the new bitmap.
167 | int bytes = b.getByteCount();
168 | freeUpCapacity(bytes);
169 |
170 | Node newNode = mNodePool.acquire();
171 | if (newNode == null) {
172 | newNode = new Node();
173 | }
174 | newNode.bitmap = b;
175 |
176 | // We append to the head, and freeUpCapacity clears from the tail,
177 | // resulting in FIFO eviction.
178 | newNode.prevInBucket = null;
179 | newNode.prevInPool = null;
180 | newNode.nextInPool = mPoolNodesHead;
181 | mPoolNodesHead = newNode;
182 |
183 | // Insert the node into its appropriate bucket based on width.
184 | int key = b.getWidth();
185 | newNode.nextInBucket = mStore.get(key);
186 | if (newNode.nextInBucket != null) {
187 | // The bucket already had nodes, update the old head.
188 | newNode.nextInBucket.prevInBucket = newNode;
189 | }
190 | mStore.put(key, newNode);
191 |
192 | if (newNode.nextInPool == null) {
193 | // This is the only node in the list, update the tail pointer.
194 | mPoolNodesTail = newNode;
195 | } else {
196 | newNode.nextInPool.prevInPool = newNode;
197 | }
198 | mSizeBytes += bytes;
199 | return true;
200 | }
201 |
202 | /**
203 | * Empty the pool, recycling all the bitmaps currently in it.
204 | */
205 | public synchronized void clear() {
206 | // Clearing is equivalent to ensuring all the capacity is available.
207 | freeUpCapacity(mCapacityBytes);
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/library/src/main/java/com/cncoderx/photopicker/ui/GalleryActivity.java:
--------------------------------------------------------------------------------
1 | package com.cncoderx.photopicker.ui;
2 |
3 | import android.content.Intent;
4 | import android.media.MediaScannerConnection;
5 | import android.net.Uri;
6 | import android.os.Build;
7 | import android.os.Bundle;
8 | import android.os.Environment;
9 | import android.provider.MediaStore;
10 | import android.support.annotation.Nullable;
11 | import android.support.v4.app.Fragment;
12 | import android.support.v4.app.FragmentTransaction;
13 | import android.support.v4.content.FileProvider;
14 | import android.support.v4.view.MenuItemCompat;
15 | import android.support.v7.app.AppCompatActivity;
16 | import android.support.v7.widget.Toolbar;
17 | import android.view.Menu;
18 | import android.view.MenuItem;
19 | import android.view.View;
20 | import android.view.Window;
21 | import android.widget.AdapterView;
22 | import android.widget.ListView;
23 | import android.widget.Toast;
24 |
25 | import com.cncoderx.photopicker.Configuration;
26 | import com.cncoderx.photopicker.PhotoPicker;
27 | import com.cncoderx.photopicker.R;
28 | import com.cncoderx.photopicker.adapter.AlbumListAdapter;
29 | import com.cncoderx.photopicker.anim.Animation;
30 | import com.cncoderx.photopicker.anim.AnimationListener;
31 | import com.cncoderx.photopicker.anim.SlideInUnderneathAnimation;
32 | import com.cncoderx.photopicker.anim.SlideOutUnderneathAnimation;
33 | import com.cncoderx.photopicker.bean.Album;
34 | import com.cncoderx.photopicker.core.IImage;
35 | import com.cncoderx.photopicker.io.BytesBufferPool;
36 | import com.cncoderx.photopicker.io.GalleryBitmapPool;
37 | import com.cncoderx.photopicker.utils.BucketHelper;
38 | import com.cncoderx.photopicker.utils.Logger;
39 | import com.cncoderx.photopicker.widget.ToolbarActionButton;
40 |
41 | import java.io.File;
42 | import java.text.SimpleDateFormat;
43 | import java.util.ArrayList;
44 | import java.util.Date;
45 | import java.util.List;
46 | import java.util.Locale;
47 |
48 | /**
49 | * Created by wujie on 2017/2/15.
50 | */
51 | public class GalleryActivity extends AppCompatActivity implements
52 | AdapterView.OnItemClickListener, MediaScannerConnection.MediaScannerConnectionClient {
53 | Toolbar mToolbar;
54 | MenuItem mPositiveItem;
55 | ToolbarActionButton mPositiveButton;
56 | ListView lvAlbum;
57 | View mAlbumOverview;
58 |
59 | private AlbumListAdapter mAlbumAdapter;
60 | private Configuration configuration;
61 | private int curAlbumIndex;
62 | private File mStorageDir;
63 | private File mStorageFile;
64 | MediaScannerConnection mMediaScanner;
65 |
66 | private Fragment mActivedFragment;
67 | private PhotoGridFragment mPhotoGridFragment;
68 | private SinglePreviewFragment mSinglePreviewFragment;
69 | private MultiPreviewFragment mMultiPreviewFragment;
70 |
71 | public static final int REQUEST_TAKE_PHOTO = 1;
72 | public static final int REQUEST_CROP_PHOTO = 2;
73 |
74 | @Override
75 | protected void onCreate(Bundle savedInstanceState) {
76 | super.onCreate(savedInstanceState);
77 | PhotoPicker.initialize(getApplicationContext());
78 | requestWindowFeature(Window.FEATURE_NO_TITLE);
79 | setContentView(R.layout.activity_gallery);
80 |
81 | Bundle bundle = getIntent().getExtras();
82 | configuration = bundle.getParcelable(PhotoPicker.EXTRA_CONFIGURATION);
83 |
84 | mMediaScanner = new MediaScannerConnection(getApplicationContext(), this);
85 | mStorageDir = new File(Environment.getExternalStorageDirectory(), "/DCIM/PhotoPicker/");
86 | if (!mStorageDir.exists()) mStorageDir.mkdirs();
87 |
88 | lvAlbum = (ListView) findViewById(R.id.lv_album);
89 | mAlbumOverview = findViewById(R.id.fl_album_overview);
90 | mToolbar = (Toolbar) findViewById(R.id.toolbar);
91 | setSupportActionBar(mToolbar);
92 | initAlbumList();
93 | }
94 |
95 | @Override
96 | protected void onPostCreate(@Nullable Bundle savedInstanceState) {
97 | super.onPostCreate(savedInstanceState);
98 | showPhotoGridFragment("");
99 | }
100 |
101 | private void initAlbumList() {
102 | mAlbumAdapter = new AlbumListAdapter(this);
103 | lvAlbum.setAdapter(mAlbumAdapter);
104 | lvAlbum.setOnItemClickListener(this);
105 | lvAlbum.post(new Runnable() {
106 | @Override
107 | public void run() {
108 | mAlbumOverview.setVisibility(View.INVISIBLE);
109 | new SlideOutUnderneathAnimation(lvAlbum)
110 | .setDirection(Animation.DIRECTION_UP)
111 | .setListener(new AnimationListener() {
112 | @Override
113 | public void onAnimationEnd(Animation animation) {
114 | mAlbumOverview.setVisibility(View.GONE);
115 | }
116 | })
117 | .animate();
118 | }
119 | });
120 | List albumList = BucketHelper.getAlbumList(getApplicationContext());
121 | mAlbumAdapter.addAll(albumList);
122 | mAlbumAdapter.notifyDataSetChanged();
123 | }
124 |
125 | public void showPhotoGridFragment(String albumId) {
126 | FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
127 | if (mPhotoGridFragment == null) {
128 | Bundle bundle = new Bundle();
129 | bundle.putInt("maxCount", configuration.getMaxCount());
130 | bundle.putBoolean("showCamera", !configuration.isHideCamera());
131 | mPhotoGridFragment = new PhotoGridFragment();
132 | mPhotoGridFragment.setArguments(bundle);
133 | ft.add(R.id.container, mPhotoGridFragment);
134 | }
135 | if (mActivedFragment != null)
136 | ft.hide(mActivedFragment);
137 | ft.show(mPhotoGridFragment);
138 | ft.commit();
139 |
140 | mActivedFragment = mPhotoGridFragment;
141 | mPhotoGridFragment.showPhotos(albumId);
142 | }
143 |
144 | public void showSinglePreviewFragment(IImage image) {
145 | FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
146 | if (mSinglePreviewFragment == null) {
147 | mSinglePreviewFragment = new SinglePreviewFragment();
148 | ft.add(R.id.container, mSinglePreviewFragment);
149 | }
150 | if (mActivedFragment != null)
151 | ft.hide(mActivedFragment);
152 | ft.show(mSinglePreviewFragment);
153 | ft.commit();
154 |
155 | mActivedFragment = mSinglePreviewFragment;
156 | mSinglePreviewFragment.setImage(image);
157 | }
158 |
159 | public void showMultiPreviewFragment(IImage... images) {
160 | FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
161 | if (mMultiPreviewFragment == null) {
162 | mMultiPreviewFragment = new MultiPreviewFragment();
163 | ft.add(R.id.container, mMultiPreviewFragment);
164 | }
165 | if (mActivedFragment != null)
166 | ft.hide(mActivedFragment);
167 | ft.show(mMultiPreviewFragment);
168 | ft.commit();
169 |
170 | mActivedFragment = mMultiPreviewFragment;
171 | mMultiPreviewFragment.setImages(images);
172 | }
173 |
174 | public void slideInAlbumList() {
175 | if (!albumListIsShowing()) {
176 | lvAlbum.setEnabled(false);
177 | mPositiveButton.setEnabled(false);
178 | mAlbumOverview.setVisibility(View.VISIBLE);
179 | new SlideInUnderneathAnimation(lvAlbum)
180 | .setDirection(Animation.DIRECTION_UP)
181 | .setDuration(Animation.DURATION_DEFAULT)
182 | .setListener(new AnimationListener() {
183 | @Override
184 | public void onAnimationEnd(Animation animation) {
185 | lvAlbum.setEnabled(true);
186 | mPositiveButton.setEnabled(true);
187 | }
188 | })
189 | .animate();
190 | }
191 | }
192 |
193 | public void slideOutAlbumList() {
194 | if (albumListIsShowing()) {
195 | lvAlbum.setEnabled(false);
196 | mPositiveButton.setEnabled(false);
197 | new SlideOutUnderneathAnimation(lvAlbum)
198 | .setDirection(Animation.DIRECTION_UP)
199 | .setDuration(Animation.DURATION_DEFAULT)
200 | .setListener(new AnimationListener() {
201 | @Override
202 | public void onAnimationEnd(Animation animation) {
203 | lvAlbum.setEnabled(true);
204 | mPositiveButton.setEnabled(true);
205 | mAlbumOverview.setVisibility(View.GONE);
206 | }
207 | })
208 | .animate();
209 | }
210 | }
211 |
212 | public boolean albumListIsShowing() {
213 | return mAlbumOverview.getVisibility() == View.VISIBLE;
214 | }
215 |
216 | public void takePhoto() {
217 | Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
218 | if (captureIntent.resolveActivity(getPackageManager()) != null) {
219 | String dateToken = new SimpleDateFormat("yyyyMMddHHmmss", Locale.CHINA).format(new Date());
220 | String filename = "IMG_" + dateToken + ".jpg";
221 | mStorageFile = new File(mStorageDir, filename);
222 | Uri uri;
223 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
224 | uri = Uri.fromFile(mStorageFile);
225 | } else {
226 | uri = FileProvider.getUriForFile(this, getApplicationContext().getPackageName() + ".provider", mStorageFile);
227 | }
228 | captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
229 | startActivityForResult(captureIntent, REQUEST_TAKE_PHOTO);
230 | } else {
231 | Toast.makeText(getApplicationContext(), R.string.open_camera_failure, Toast.LENGTH_SHORT).show();
232 | }
233 | }
234 |
235 | public void cropPhoto(String path) {
236 | Intent intent = new Intent(this, CropPhotoActivity.class);
237 | intent.putExtra("path", path);
238 | intent.putExtra("aspectX", configuration.getAspectX());
239 | intent.putExtra("aspectY", configuration.getAspectY());
240 | intent.putExtra("circleCrop", configuration.isCircleCrop());
241 | startActivityForResult(intent, REQUEST_CROP_PHOTO);
242 | }
243 |
244 | public void setResultAndFinish(ArrayList images) {
245 | Intent data = new Intent();
246 | data.putParcelableArrayListExtra("data", images);
247 | setResult(RESULT_OK, data);
248 | finish();
249 | }
250 |
251 | public void previewPhotos(IImage... images) {
252 | if (images.length == 1) {
253 | showSinglePreviewFragment(images[0]);
254 | } else if (images.length > 1) {
255 | showMultiPreviewFragment(images);
256 | }
257 | }
258 |
259 | @Override
260 | public boolean onCreateOptionsMenu(Menu menu) {
261 | getMenuInflater().inflate(R.menu.main, menu);
262 | mPositiveItem = menu.findItem(R.id.action_positive);
263 | mPositiveButton = (ToolbarActionButton) MenuItemCompat.getActionProvider(mPositiveItem);
264 | mPositiveButton.setOnClickListener(new View.OnClickListener() {
265 | @Override
266 | public void onClick(View v) {
267 | if (albumListIsShowing()) {
268 | slideOutAlbumList();
269 | } else {
270 | slideInAlbumList();
271 | }
272 | }
273 | });
274 | return true;
275 | }
276 |
277 | @Override
278 | public boolean onOptionsItemSelected(MenuItem item) {
279 | if (item.getItemId() == android.R.id.home) {
280 | if (mActivedFragment == mSinglePreviewFragment ||
281 | mActivedFragment == mMultiPreviewFragment) {
282 | showPhotoGridFragment(mAlbumAdapter.get(curAlbumIndex).getId());
283 | return true;
284 | }
285 | setResult(RESULT_CANCELED);
286 | finish();
287 | return true;
288 | }
289 | return super.onOptionsItemSelected(item);
290 | }
291 |
292 | @Override
293 | public void onItemClick(AdapterView> parent, View view, int position, long id) {
294 | slideOutAlbumList();
295 | if (curAlbumIndex != position) {
296 | Album album = mAlbumAdapter.get(position);
297 | mPositiveButton.setText(album.getName());
298 | showPhotoGridFragment(album.getId());
299 | }
300 | curAlbumIndex = position;
301 | }
302 |
303 | @Override
304 | public void onBackPressed() {
305 | if (mActivedFragment == mSinglePreviewFragment ||
306 | mActivedFragment == mMultiPreviewFragment) {
307 | showPhotoGridFragment(mAlbumAdapter.get(curAlbumIndex).getId());
308 | return;
309 | }
310 | super.onBackPressed();
311 | }
312 |
313 | @Override
314 | protected void onPause() {
315 | super.onPause();
316 | GalleryBitmapPool.getInstance().clear();
317 | BytesBufferPool.getInstance().clear();
318 | }
319 |
320 | @Override
321 | protected void onDestroy() {
322 | super.onDestroy();
323 | if (mMediaScanner.isConnected())
324 | mMediaScanner.disconnect();
325 | }
326 |
327 | @Override
328 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
329 | if (resultCode == RESULT_OK) {
330 | if (requestCode == REQUEST_TAKE_PHOTO) {
331 | Logger.i(String.format("take photo success, saved as:%s", mStorageFile.getAbsolutePath()));
332 |
333 | //刷新相册数据库
334 | mMediaScanner.connect();
335 | } else if (requestCode == REQUEST_CROP_PHOTO) {
336 | setResult(resultCode, data);
337 | finish();
338 | }
339 | }
340 | }
341 |
342 | @Override
343 | public void onMediaScannerConnected() {
344 | mMediaScanner.scanFile(mStorageFile.getAbsolutePath(), "image/jpeg");
345 | }
346 |
347 | @Override
348 | public void onScanCompleted(String path, Uri uri) {
349 | mMediaScanner.disconnect();
350 | runOnUiThread(new Runnable() {
351 | @Override
352 | public void run() {
353 | showPhotoGridFragment(mAlbumAdapter.get(curAlbumIndex).getId());
354 | }
355 | });
356 | }
357 | }
358 |
--------------------------------------------------------------------------------
/library/src/main/java/com/cncoderx/photopicker/ui/MultiPreviewFragment.java:
--------------------------------------------------------------------------------
1 | package com.cncoderx.photopicker.ui;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.support.annotation.Nullable;
6 | import android.support.v4.app.Fragment;
7 | import android.support.v4.view.ViewPager;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.widget.TextView;
12 |
13 | import com.cncoderx.photopicker.R;
14 | import com.cncoderx.photopicker.adapter.PhotoPreviewAdapter;
15 | import com.cncoderx.photopicker.core.IImage;
16 |
17 | import java.util.ArrayList;
18 | import java.util.List;
19 | import java.util.Locale;
20 |
21 | /**
22 | * Created by wujie on 2017/2/17.
23 | */
24 | public class MultiPreviewFragment extends Fragment implements View.OnClickListener, ViewPager.OnPageChangeListener {
25 | GalleryActivity mActivity;
26 | ViewPager mViewPager;
27 | TextView tvPreview;
28 | TextView tvCrop;
29 | TextView tvCommit;
30 |
31 | private List mImages;
32 | private PhotoPreviewAdapter mAdapter;
33 |
34 | @Override
35 | public void onAttach(Activity activity) {
36 | super.onAttach(activity);
37 | mActivity = (GalleryActivity) activity;
38 | }
39 |
40 | @Override
41 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
42 | View view = inflater.inflate(R.layout.fragment_multi_photo_preview, container, false);
43 | tvPreview = (TextView) view.findViewById(R.id.tv_bottom_bar_pre);
44 | tvCrop = (TextView) view.findViewById(R.id.tv_bottom_bar_next);
45 | tvCommit = (TextView) view.findViewById(R.id.tv_bottom_bar_commit);
46 | return view;
47 | }
48 |
49 | @Override
50 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
51 | super.onViewCreated(view, savedInstanceState);
52 | tvCommit.setText(R.string.commit);
53 | tvPreview.setVisibility(View.GONE);
54 | tvCrop.setVisibility(View.GONE);
55 | tvCommit.setOnClickListener(this);
56 | mViewPager = (ViewPager) view.findViewById(R.id.viewpager);
57 | mViewPager.addOnPageChangeListener(this);
58 | mAdapter = new PhotoPreviewAdapter(getContext());
59 | if (mImages != null) {
60 | mAdapter.addAll(mImages);
61 | }
62 | mViewPager.setAdapter(mAdapter);
63 | }
64 |
65 | public void setImages(IImage... images) {
66 | mImages = new ArrayList<>();
67 | if (images != null) {
68 | for (int i = 0; i < images.length; i++) {
69 | mImages.add(images[i]);
70 | }
71 | }
72 | if (mAdapter != null) {
73 | mAdapter.clear();
74 | mAdapter.addAll(mImages);
75 | mAdapter.notifyDataSetChanged();
76 | }
77 | }
78 |
79 | @Override
80 | public void onDestroyView() {
81 | super.onDestroyView();
82 | mViewPager.clearOnPageChangeListeners();
83 | }
84 |
85 | @Override
86 | public void onDetach() {
87 | super.onDetach();
88 | mActivity = null;
89 | }
90 |
91 | @Override
92 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
93 |
94 | }
95 |
96 | @Override
97 | public void onPageSelected(int position) {
98 | int count = mAdapter.getSize();
99 | if (mActivity.mToolbar != null) {
100 | mActivity.mToolbar.setTitle(String.format(Locale.US, "%d/%d", position+1, count));
101 | }
102 | }
103 |
104 | @Override
105 | public void onPageScrollStateChanged(int state) {
106 |
107 | }
108 |
109 | @Override
110 | public void onClick(View v) {
111 | if (v.getId() == R.id.tv_bottom_bar_commit) {
112 | mActivity.setResultAndFinish(new ArrayList<>(mImages));
113 | }
114 | }
115 |
116 | @Override
117 | public void onHiddenChanged(boolean hidden) {
118 | if (hidden) {
119 | if (mActivity.mToolbar != null) {
120 | mActivity.mToolbar.setTitle(R.string.image);
121 | }
122 | } else {
123 | int count = mAdapter.getSize();
124 | int index = mViewPager.getCurrentItem();
125 | if (mActivity.mToolbar != null) {
126 | mActivity.mToolbar.setTitle(String.format(Locale.US, "%d/%d", index+1, count));
127 | }
128 | }
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/library/src/main/java/com/cncoderx/photopicker/ui/PhotoGridFragment.java:
--------------------------------------------------------------------------------
1 | package com.cncoderx.photopicker.ui;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.support.annotation.Nullable;
6 | import android.support.v4.app.Fragment;
7 | import android.text.TextUtils;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.widget.AdapterView;
12 | import android.widget.GridView;
13 | import android.widget.TextView;
14 |
15 | import com.cncoderx.photopicker.R;
16 | import com.cncoderx.photopicker.adapter.PhotoGridAdapter;
17 | import com.cncoderx.photopicker.anim.Animation;
18 | import com.cncoderx.photopicker.anim.AnimationListener;
19 | import com.cncoderx.photopicker.anim.SlideInUnderneathAnimation;
20 | import com.cncoderx.photopicker.anim.SlideOutUnderneathAnimation;
21 | import com.cncoderx.photopicker.bean.Photo;
22 | import com.cncoderx.photopicker.core.IImage;
23 | import com.cncoderx.photopicker.utils.BucketHelper;
24 |
25 | import java.util.ArrayList;
26 | import java.util.List;
27 |
28 | /**
29 | * Created by wujie on 2017/2/15.
30 | */
31 | public class PhotoGridFragment extends Fragment implements
32 | View.OnClickListener,
33 | AdapterView.OnItemClickListener,
34 | PhotoGridAdapter.OnSelectedChangeListener {
35 | GridView mGridView;
36 | GalleryActivity mActivity;
37 | View mBottomBar;
38 | View mBottomBarOverview;
39 | TextView tvPreview;
40 | TextView tvCrop;
41 | TextView tvCommit;
42 |
43 | private String albumId;
44 | private boolean showCamera;
45 | private int maxCount;
46 | private PhotoGridAdapter mPhotoGridAdapter;
47 |
48 | @Override
49 | public void onAttach(Activity activity) {
50 | super.onAttach(activity);
51 | mActivity = (GalleryActivity) activity;
52 | }
53 |
54 | @Nullable
55 | @Override
56 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
57 | View view = inflater.inflate(R.layout.fragment_photo_grid, container, false);
58 | mBottomBar = view.findViewById(R.id.rl_bottom_bar);
59 | mBottomBarOverview = view.findViewById(R.id.fl_bottom_bar_overview);
60 | tvPreview = (TextView) view.findViewById(R.id.tv_bottom_bar_pre);
61 | tvCrop = (TextView) view.findViewById(R.id.tv_bottom_bar_next);
62 | tvCommit = (TextView) view.findViewById(R.id.tv_bottom_bar_commit);
63 | mGridView = (GridView) view.findViewById(R.id.grid);
64 | return view;
65 | }
66 |
67 | @Override
68 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
69 | super.onViewCreated(view, savedInstanceState);
70 | if (mActivity.mToolbar != null) {
71 | mActivity.mToolbar.setTitle(R.string.image);
72 | }
73 | tvPreview.setText(R.string.preview);
74 | tvCrop.setText(R.string.crop);
75 | tvCommit.setText(R.string.commit);
76 | tvPreview.setOnClickListener(this);
77 | tvCrop.setOnClickListener(this);
78 | tvCommit.setOnClickListener(this);
79 |
80 | Bundle bundle = getArguments();
81 | showCamera = bundle.getBoolean("showCamera", true);
82 | maxCount = bundle.getInt("maxCount", 1);
83 |
84 | mPhotoGridAdapter = new PhotoGridAdapter(getContext());
85 | mPhotoGridAdapter.setMaxCount(maxCount);
86 | mPhotoGridAdapter.setShowCamera(showCamera);
87 | mPhotoGridAdapter.setOnSelectedChangeListener(this);
88 | mGridView.setOnItemClickListener(this);
89 | mGridView.setAdapter(mPhotoGridAdapter);
90 |
91 | tvCommit.setText(getString(R.string.commit_count, 0, maxCount));
92 | mBottomBarOverview.setVisibility(View.INVISIBLE);
93 | new SlideOutUnderneathAnimation(mBottomBar)
94 | .setDirection(Animation.DIRECTION_DOWN)
95 | .setListener(new AnimationListener() {
96 | @Override
97 | public void onAnimationEnd(Animation animation) {
98 | mBottomBarOverview.setVisibility(View.GONE);
99 | }
100 | })
101 | .animate();
102 |
103 | showPhotos(albumId);
104 | }
105 |
106 | public void showPhotos(String albumId) {
107 | this.albumId = albumId;
108 |
109 | if (mPhotoGridAdapter != null) {
110 | List photos;
111 | if (TextUtils.isEmpty(albumId)) {
112 | photos = BucketHelper.getPhotoList(mActivity.getApplicationContext());
113 | } else {
114 | photos = BucketHelper.getPhotoListByBucketId(mActivity.getApplicationContext(), albumId);
115 | }
116 |
117 | mPhotoGridAdapter.clear();
118 | mPhotoGridAdapter.addAll(new ArrayList(photos));
119 | mPhotoGridAdapter.notifyDataSetChanged();
120 | }
121 | }
122 |
123 | public void slideInBottomBar() {
124 | if (!bottomBarIsShowing()) {
125 | mPhotoGridAdapter.setSelectable(false);
126 | mBottomBarOverview.setVisibility(View.VISIBLE);
127 | new SlideInUnderneathAnimation(mBottomBar)
128 | .setDirection(Animation.DIRECTION_DOWN)
129 | .setDuration(Animation.DURATION_DEFAULT)
130 | .setListener(new AnimationListener() {
131 | @Override
132 | public void onAnimationEnd(Animation animation) {
133 | mPhotoGridAdapter.setSelectable(true);
134 | }
135 | })
136 | .animate();
137 | }
138 | }
139 |
140 | public void slideOutBottomBar() {
141 | if (bottomBarIsShowing()) {
142 | mPhotoGridAdapter.setSelectable(false);
143 | new SlideOutUnderneathAnimation(mBottomBar)
144 | .setDirection(Animation.DIRECTION_DOWN)
145 | .setDuration(Animation.DURATION_DEFAULT)
146 | .setListener(new AnimationListener() {
147 | @Override
148 | public void onAnimationEnd(Animation animation) {
149 | mPhotoGridAdapter.setSelectable(true);
150 | mBottomBarOverview.setVisibility(View.GONE);
151 | }
152 | })
153 | .animate();
154 | }
155 | }
156 |
157 | public boolean bottomBarIsShowing() {
158 | return mBottomBarOverview.getVisibility() == View.VISIBLE;
159 | }
160 |
161 | @Override
162 | public void onItemClick(AdapterView> parent, View view, int position, long id) {
163 | if (showCamera) {
164 | if (position == 0) {
165 | mActivity.takePhoto();
166 | } else {
167 | IImage image = mPhotoGridAdapter.get(position - 1);
168 | mActivity.previewPhotos(image);
169 | }
170 | } else {
171 | IImage image = mPhotoGridAdapter.get(position);
172 | mActivity.previewPhotos(image);
173 | }
174 | }
175 |
176 | @Override
177 | public void onDetach() {
178 | super.onDetach();
179 | mActivity = null;
180 | }
181 |
182 | @Override
183 | public void select(int index) {
184 | int count = mPhotoGridAdapter.getSelectedCount();
185 | if (1 == count) {
186 | slideInBottomBar();
187 | tvCrop.setVisibility(View.VISIBLE);
188 | } else {
189 | tvCrop.setVisibility(View.GONE);
190 | }
191 | tvCommit.setText(getString(R.string.commit_count, count, maxCount));
192 | }
193 |
194 | @Override
195 | public void unselect(int index) {
196 | int count = mPhotoGridAdapter.getSelectedCount();
197 | if (0 == count) {
198 | slideOutBottomBar();
199 | tvCrop.setVisibility(View.GONE);
200 | } else if (1 == count) {
201 | tvCrop.setVisibility(View.VISIBLE);
202 | } else {
203 | tvCrop.setVisibility(View.GONE);
204 | }
205 | tvCommit.setText(getString(R.string.commit_count, count, maxCount));
206 | }
207 |
208 | @Override
209 | public void onClick(View v) {
210 | int id = v.getId();
211 | if (id == R.id.tv_bottom_bar_pre) {
212 | List images = mPhotoGridAdapter.getSelectedData();
213 | mActivity.previewPhotos(images.toArray(new IImage[images.size()]));
214 | } else if (id == R.id.tv_bottom_bar_next) {
215 | List images = mPhotoGridAdapter.getSelectedData();
216 | mActivity.cropPhoto(images.get(0).getPath());
217 | } else if (id == R.id.tv_bottom_bar_commit) {
218 | mActivity.setResultAndFinish(new ArrayList<>(mPhotoGridAdapter.getSelectedData()));
219 | }
220 | }
221 |
222 | @Override
223 | public void onHiddenChanged(boolean hidden) {
224 | if (mActivity.mPositiveItem != null)
225 | mActivity.mPositiveItem.setVisible(!hidden);
226 | }
227 | }
228 |
--------------------------------------------------------------------------------
/library/src/main/java/com/cncoderx/photopicker/ui/SinglePreviewFragment.java:
--------------------------------------------------------------------------------
1 | package com.cncoderx.photopicker.ui;
2 |
3 | import android.app.Activity;
4 | import android.graphics.Bitmap;
5 | import android.os.Bundle;
6 | import android.support.annotation.Nullable;
7 | import android.support.v4.app.Fragment;
8 | import android.util.DisplayMetrics;
9 | import android.view.LayoutInflater;
10 | import android.view.View;
11 | import android.view.ViewGroup;
12 | import android.widget.TextView;
13 |
14 | import com.cncoderx.photopicker.R;
15 | import com.cncoderx.photopicker.core.BitmapLoader;
16 | import com.cncoderx.photopicker.core.IImage;
17 | import com.cncoderx.photopicker.utils.Thumbnail;
18 |
19 | import java.util.ArrayList;
20 |
21 | import uk.co.senab.photoview.PhotoView;
22 |
23 | /**
24 | * Created by wujie on 2017/2/17.
25 | */
26 | public class SinglePreviewFragment extends Fragment implements View.OnClickListener {
27 | GalleryActivity mActivity;
28 | PhotoView mPhotoView;
29 | TextView tvPreview;
30 | TextView tvCrop;
31 | TextView tvCommit;
32 | private IImage mImage;
33 |
34 | @Override
35 | public void onAttach(Activity activity) {
36 | super.onAttach(activity);
37 | mActivity = (GalleryActivity) activity;
38 | }
39 |
40 | @Override
41 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
42 | View view = inflater.inflate(R.layout.fragment_single_photo_preview, container, false);
43 | mPhotoView = (PhotoView) view.findViewById(R.id.preview);
44 | tvPreview = (TextView) view.findViewById(R.id.tv_bottom_bar_pre);
45 | tvCrop = (TextView) view.findViewById(R.id.tv_bottom_bar_next);
46 | tvCommit = (TextView) view.findViewById(R.id.tv_bottom_bar_commit);
47 | return view;
48 | }
49 |
50 | @Override
51 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
52 | tvCrop.setText(R.string.crop);
53 | tvCommit.setText(R.string.commit);
54 | tvPreview.setVisibility(View.GONE);
55 | tvCrop.setOnClickListener(this);
56 | tvCommit.setOnClickListener(this);
57 |
58 | setImage(mImage);
59 | }
60 |
61 | public void setImage(IImage image) {
62 | mImage = image;
63 | if (mPhotoView != null) {
64 | if (image == null) {
65 | mPhotoView.setImageBitmap(null);
66 | return;
67 | }
68 | DisplayMetrics metrics = getResources().getDisplayMetrics();
69 | int targetSize = Math.min(metrics.heightPixels, metrics.widthPixels);
70 | new BitmapLoader(Thumbnail.normal, targetSize) {
71 | @Override
72 | protected void onPostExecute(Bitmap bitmap) {
73 | mPhotoView.setImageBitmap(bitmap);
74 | }
75 | }.execute(image);
76 | }
77 | }
78 |
79 | @Override
80 | public void onDetach() {
81 | super.onDetach();
82 | mActivity = null;
83 | }
84 |
85 | @Override
86 | public void onClick(View v) {
87 | int id = v.getId();
88 | if (id == R.id.tv_bottom_bar_next) {
89 | mActivity.cropPhoto(mImage.getPath());
90 | } else if (id == R.id.tv_bottom_bar_commit) {
91 | ArrayList images = new ArrayList<>();
92 | images.add(mImage);
93 | mActivity.setResultAndFinish(images);
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/library/src/main/java/com/cncoderx/photopicker/utils/BucketHelper.java:
--------------------------------------------------------------------------------
1 | package com.cncoderx.photopicker.utils;
2 |
3 | import android.content.ContentResolver;
4 | import android.content.Context;
5 | import android.database.Cursor;
6 | import android.media.ExifInterface;
7 | import android.os.Build;
8 | import android.provider.MediaStore;
9 | import android.text.TextUtils;
10 |
11 | import com.cncoderx.photopicker.R;
12 | import com.cncoderx.photopicker.bean.Album;
13 | import com.cncoderx.photopicker.bean.Photo;
14 |
15 | import java.io.IOException;
16 | import java.util.ArrayList;
17 | import java.util.List;
18 |
19 | /**
20 | * Created by wujie on 2017/2/15.
21 | */
22 | public class BucketHelper {
23 | static final String[] PROJECTION_GET_PHOTO = {
24 | MediaStore.Images.ImageColumns._ID, // 0
25 | MediaStore.Images.ImageColumns.TITLE, // 1
26 | MediaStore.Images.ImageColumns.MIME_TYPE, // 2
27 | MediaStore.Images.ImageColumns.LATITUDE, // 3
28 | MediaStore.Images.ImageColumns.LONGITUDE, // 4
29 | MediaStore.Images.ImageColumns.DATE_TAKEN, // 5
30 | MediaStore.Images.ImageColumns.DATE_ADDED, // 6
31 | MediaStore.Images.ImageColumns.DATE_MODIFIED, // 7
32 | MediaStore.Images.ImageColumns.DATA, // 8
33 | MediaStore.Images.ImageColumns.ORIENTATION, // 9
34 | MediaStore.Images.ImageColumns.BUCKET_ID, // 10
35 | MediaStore.Images.ImageColumns.SIZE, // 11
36 | MediaStore$Images$ImageColumns$WIDTH(), // 12
37 | MediaStore$Images$ImageColumns$HEIGHT(), // 13
38 | };
39 |
40 | static final String ORDER_GET_PHOTO = MediaStore.Images.ImageColumns.DATE_ADDED + " DESC";
41 |
42 | static final String[] PROJECTION_GET_ALBUM = {
43 | MediaStore.Images.Media.BUCKET_ID,
44 | MediaStore.Images.Media.DATA,
45 | MediaStore.Images.Media.BUCKET_DISPLAY_NAME,
46 | MediaStore.Images.Media.ORIENTATION,
47 | "COUNT(1)"
48 | };
49 |
50 | static final String SELECTION_GET_ALBUM = "1) GROUP BY (1";
51 |
52 | static String MediaStore$Images$ImageColumns$WIDTH() {
53 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
54 | return "0";
55 | } else {
56 | return MediaStore.Images.ImageColumns.WIDTH;
57 | }
58 | }
59 |
60 | static String MediaStore$Images$ImageColumns$HEIGHT() {
61 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
62 | return "0";
63 | } else {
64 | return MediaStore.Images.ImageColumns.HEIGHT;
65 | }
66 | }
67 |
68 | public static List getAlbumList(Context context) {
69 | List albumList = new ArrayList<>();
70 | ContentResolver contentResolver = context.getContentResolver();
71 | Cursor cursor = contentResolver.query(
72 | MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
73 | PROJECTION_GET_ALBUM, SELECTION_GET_ALBUM,
74 | null, null);
75 | if (cursor == null) {
76 | Logger.w("cannot open media database: " + MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
77 | return albumList;
78 | }
79 |
80 | int photoCount = 0;
81 | try {
82 | while (cursor.moveToNext()) {
83 | String id = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_ID));
84 | String name = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_DISPLAY_NAME));
85 | String cover = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
86 | int count = cursor.getInt(4);
87 | photoCount += count;
88 | Album album = new Album();
89 | album.setId(id);
90 | album.setName(name);
91 | album.setCoverPath(cover);
92 | album.setPhotoCount(count);
93 | albumList.add(album);
94 | }
95 | } finally {
96 | cursor.close();
97 | }
98 |
99 | if (photoCount > 0) {
100 | Album album = new Album();
101 | album.setId("");
102 | album.setName(context.getString(R.string.all_image));
103 | album.setCoverPath(albumList.get(0).getCoverPath());
104 | album.setPhotoCount(photoCount);
105 | albumList.add(0, album);
106 | }
107 |
108 | return albumList;
109 | }
110 |
111 | public static List getPhotoList(Context context) {
112 | List photoList = new ArrayList<>();
113 | ContentResolver contentResolver = context.getContentResolver();
114 | Cursor cursor = contentResolver.query(
115 | MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
116 | PROJECTION_GET_PHOTO,
117 | null,
118 | null,
119 | ORDER_GET_PHOTO);
120 | if (cursor == null) {
121 | Logger.w("cannot open media database: " + MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
122 | return photoList;
123 | }
124 | try {
125 | while (cursor.moveToNext()) {
126 | Photo photo = new Photo(cursor);
127 | photoList.add(photo);
128 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
129 | String filePath = photo.getFilePath();
130 | if (!TextUtils.isEmpty(filePath)) {
131 | try {
132 | ExifInterface exifInterface = new ExifInterface(filePath);
133 | int width = exifInterface.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0);
134 | int height = exifInterface.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0);
135 | photo.setWidth(width);
136 | photo.setHeight(height);
137 | } catch (IOException e) {
138 | Logger.e(e);
139 | }
140 | }
141 | }
142 | }
143 | } finally {
144 | cursor.close();
145 | }
146 | return photoList;
147 | }
148 |
149 | public static List getPhotoListByBucketId(Context context, String bucketId) {
150 | List photoList = new ArrayList<>();
151 | ContentResolver contentResolver = context.getContentResolver();
152 | Cursor cursor = contentResolver.query(
153 | MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
154 | PROJECTION_GET_PHOTO,
155 | MediaStore.Images.ImageColumns.BUCKET_ID + "=?",
156 | new String[]{bucketId},
157 | ORDER_GET_PHOTO);
158 | if (cursor == null) {
159 | Logger.w("cannot open media database: " + MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
160 | return photoList;
161 | }
162 | try {
163 | while (cursor.moveToNext()) {
164 | Photo photo = new Photo(cursor);
165 | photoList.add(photo);
166 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
167 | String filePath = photo.getFilePath();
168 | if (!TextUtils.isEmpty(filePath)) {
169 | try {
170 | ExifInterface exifInterface = new ExifInterface(filePath);
171 | int width = exifInterface.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0);
172 | int height = exifInterface.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0);
173 | photo.setWidth(width);
174 | photo.setHeight(height);
175 | } catch (IOException e) {
176 | Logger.e(e);
177 | }
178 | }
179 | }
180 | }
181 | } finally {
182 | cursor.close();
183 | }
184 | return photoList;
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/library/src/main/java/com/cncoderx/photopicker/utils/IOUtils.java:
--------------------------------------------------------------------------------
1 | package com.cncoderx.photopicker.utils;
2 |
3 | import java.io.Closeable;
4 | import java.io.IOException;
5 |
6 | /**
7 | * Created by admin on 2017/2/15.
8 | */
9 | public class IOUtils {
10 |
11 | public static void close(Closeable closeable) {
12 | if (closeable != null) {
13 | try {
14 | closeable.close();
15 | } catch (IOException e) {
16 | Logger.e(e);
17 | }
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/library/src/main/java/com/cncoderx/photopicker/utils/Logger.java:
--------------------------------------------------------------------------------
1 | package com.cncoderx.photopicker.utils;
2 |
3 | import android.util.Log;
4 |
5 | import com.cncoderx.photopicker.BuildConfig;
6 |
7 | /**
8 | * Created by admin on 2017/2/15.
9 | */
10 | public class Logger {
11 | public static final String TAG = "PhotoPicker";
12 | public static final boolean DEBUG = BuildConfig.DEBUG;
13 |
14 | public static void v(String msg){
15 | if(DEBUG) {
16 | Log.v(TAG, msg);
17 | }
18 | }
19 |
20 | public static void d(String msg){
21 | if(DEBUG) {
22 | Log.d(TAG, msg);
23 | }
24 | }
25 |
26 | public static void e(String msg) {
27 | if(DEBUG) {
28 | Log.e(TAG, msg);
29 | }
30 | }
31 |
32 | public static void e(Throwable throwable) {
33 | if(DEBUG) {
34 | if (throwable != null)
35 | Log.e(TAG, throwable.getMessage());
36 | }
37 | }
38 |
39 | public static void i(String msg){
40 | if(DEBUG) {
41 | Log.i(TAG, msg);
42 | }
43 | }
44 |
45 | public static void w(String msg){
46 | if(DEBUG) {
47 | Log.w(TAG, msg);
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/library/src/main/java/com/cncoderx/photopicker/utils/MathUtils.java:
--------------------------------------------------------------------------------
1 | package com.cncoderx.photopicker.utils;
2 |
3 | /**
4 | * Created by admin on 2017/2/16.
5 | */
6 | public class MathUtils {
7 | private static final long POLY64REV = 0x95AC9329AC4BC9B5L;
8 | private static final long INITIALCRC = 0xFFFFFFFFFFFFFFFFL;
9 |
10 | private static long[] sCrcTable = new long[256];
11 |
12 | public static final long crc64Long(String in) {
13 | if (in == null || in.length() == 0) {
14 | return 0;
15 | }
16 | return crc64Long(getBytes(in));
17 | }
18 |
19 | public static byte[] getBytes(String in) {
20 | byte[] result = new byte[in.length() * 2];
21 | int output = 0;
22 | for (char ch : in.toCharArray()) {
23 | result[output++] = (byte) (ch & 0xFF);
24 | result[output++] = (byte) (ch >> 8);
25 | }
26 | return result;
27 | }
28 |
29 | static {
30 | // http://bioinf.cs.ucl.ac.uk/downloads/crc64/crc64.c
31 | long part;
32 | for (int i = 0; i < 256; i++) {
33 | part = i;
34 | for (int j = 0; j < 8; j++) {
35 | long x = ((int) part & 1) != 0 ? POLY64REV : 0;
36 | part = (part >> 1) ^ x;
37 | }
38 | sCrcTable[i] = part;
39 | }
40 | }
41 |
42 | public static final long crc64Long(byte[] buffer) {
43 | long crc = INITIALCRC;
44 | for (int k = 0, n = buffer.length; k < n; ++k) {
45 | crc = sCrcTable[(((int) crc) ^ buffer[k]) & 0xff] ^ (crc >> 8);
46 | }
47 | return crc;
48 | }
49 |
50 | public static int prevPowerOf2(int n) {
51 | if (n <= 0) throw new IllegalArgumentException();
52 | return Integer.highestOneBit(n);
53 | }
54 |
55 | public static int nextPowerOf2(int n) {
56 | if (n <= 0 || n > (1 << 30))
57 | throw new IllegalArgumentException("n is invalid: " + n);
58 | n -= 1;
59 | n |= n >> 16;
60 | n |= n >> 8;
61 | n |= n >> 4;
62 | n |= n >> 2;
63 | n |= n >> 1;
64 | return n + 1;
65 | }
66 |
67 | public static int clamp(int x, int min, int max) {
68 | if (x > max) return max;
69 | if (x < min) return min;
70 | return x;
71 | }
72 |
73 | public static float clamp(float x, float min, float max) {
74 | if (x > max) return max;
75 | if (x < min) return min;
76 | return x;
77 | }
78 |
79 | public static long clamp(long x, long min, long max) {
80 | if (x > max) return max;
81 | if (x < min) return min;
82 | return x;
83 | }
84 |
85 | public static boolean isOpaque(int color) {
86 | return color >>> 24 == 0xFF;
87 | }
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/library/src/main/java/com/cncoderx/photopicker/utils/Thumbnail.java:
--------------------------------------------------------------------------------
1 | package com.cncoderx.photopicker.utils;
2 |
3 | /**
4 | * Created by wujie on 2017/2/20.
5 | */
6 | public enum Thumbnail {
7 | normal, micro
8 | }
9 |
--------------------------------------------------------------------------------
/library/src/main/java/com/cncoderx/photopicker/widget/CropImageView.java:
--------------------------------------------------------------------------------
1 | package com.cncoderx.photopicker.widget;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Rect;
6 | import android.util.AttributeSet;
7 | import android.view.MotionEvent;
8 |
9 | import com.cncoderx.photopicker.ui.CropPhotoActivity;
10 |
11 | import java.util.ArrayList;
12 |
13 | public class CropImageView extends ImageViewTouchBase {
14 | ArrayList mHighlightViews = new ArrayList<>();
15 | HighlightView mMotionHighlightView = null;
16 | float mLastX, mLastY;
17 | int mMotionEdge;
18 |
19 | @Override
20 | protected void onLayout(boolean changed, int left, int top,
21 | int right, int bottom) {
22 | super.onLayout(changed, left, top, right, bottom);
23 | if (mBitmapDisplayed.getBitmap() != null) {
24 | for (HighlightView hv : mHighlightViews) {
25 | hv.mMatrix.set(getImageMatrix());
26 | hv.invalidate();
27 | if (hv.mIsFocused) {
28 | centerBasedOnHighlightView(hv);
29 | }
30 | }
31 | }
32 | }
33 |
34 | public CropImageView(Context context, AttributeSet attrs) {
35 | super(context, attrs);
36 | }
37 |
38 | @Override
39 | public void zoomTo(float scale, float centerX, float centerY) {
40 | super.zoomTo(scale, centerX, centerY);
41 | for (HighlightView hv : mHighlightViews) {
42 | hv.mMatrix.set(getImageMatrix());
43 | hv.invalidate();
44 | }
45 | }
46 |
47 | @Override
48 | public void zoomIn() {
49 | super.zoomIn();
50 | for (HighlightView hv : mHighlightViews) {
51 | hv.mMatrix.set(getImageMatrix());
52 | hv.invalidate();
53 | }
54 | }
55 |
56 | @Override
57 | public void zoomOut() {
58 | super.zoomOut();
59 | for (HighlightView hv : mHighlightViews) {
60 | hv.mMatrix.set(getImageMatrix());
61 | hv.invalidate();
62 | }
63 | }
64 |
65 | @Override
66 | public void postTranslate(float deltaX, float deltaY) {
67 | super.postTranslate(deltaX, deltaY);
68 | for (int i = 0; i < mHighlightViews.size(); i++) {
69 | HighlightView hv = mHighlightViews.get(i);
70 | hv.mMatrix.postTranslate(deltaX, deltaY);
71 | hv.invalidate();
72 | }
73 | }
74 |
75 | // According to the event's position, change the focus to the first
76 | // hitting cropping rectangle.
77 | private void recomputeFocus(MotionEvent event) {
78 | for (int i = 0; i < mHighlightViews.size(); i++) {
79 | HighlightView hv = mHighlightViews.get(i);
80 | hv.setFocus(false);
81 | hv.invalidate();
82 | }
83 |
84 | for (int i = 0; i < mHighlightViews.size(); i++) {
85 | HighlightView hv = mHighlightViews.get(i);
86 | int edge = hv.getHit(event.getX(), event.getY());
87 | if (edge != HighlightView.GROW_NONE) {
88 | if (!hv.hasFocus()) {
89 | hv.setFocus(true);
90 | hv.invalidate();
91 | }
92 | break;
93 | }
94 | }
95 | invalidate();
96 | }
97 |
98 | @Override
99 | public boolean onTouchEvent(MotionEvent event) {
100 | CropPhotoActivity cropImage = (CropPhotoActivity) this.getContext();
101 | if (cropImage.isSaved()) {
102 | return false;
103 | }
104 |
105 | switch (event.getAction()) {
106 | case MotionEvent.ACTION_DOWN:
107 | for (int i = 0; i < mHighlightViews.size(); i++) {
108 | HighlightView hv = mHighlightViews.get(i);
109 | int edge = hv.getHit(event.getX(), event.getY());
110 | if (edge != HighlightView.GROW_NONE) {
111 | mMotionEdge = edge;
112 | mMotionHighlightView = hv;
113 | mLastX = event.getX();
114 | mLastY = event.getY();
115 | mMotionHighlightView.setMode(
116 | (edge == HighlightView.MOVE)
117 | ? HighlightView.ModifyMode.Move
118 | : HighlightView.ModifyMode.Grow);
119 | break;
120 | }
121 | }
122 | break;
123 | case MotionEvent.ACTION_UP:
124 | if (mMotionHighlightView != null) {
125 | centerBasedOnHighlightView(mMotionHighlightView);
126 | mMotionHighlightView.setMode(
127 | HighlightView.ModifyMode.None);
128 | }
129 | mMotionHighlightView = null;
130 | center(true, true);
131 | break;
132 | case MotionEvent.ACTION_MOVE:
133 | if (mMotionHighlightView != null) {
134 | mMotionHighlightView.handleMotion(mMotionEdge,
135 | event.getX() - mLastX,
136 | event.getY() - mLastY);
137 | mLastX = event.getX();
138 | mLastY = event.getY();
139 |
140 | // This section of code is optional. It has some user
141 | // benefit in that moving the crop rectangle against
142 | // the edge of the screen causes scrolling but it means
143 | // that the crop rectangle is no longer fixed under
144 | // the user's finger.
145 | ensureVisible(mMotionHighlightView);
146 |
147 | // if we're not zoomed then there's no point in even allowing
148 | // the user to move the image around. This call to center puts
149 | // it back to the normalized location (with false meaning don't
150 | // animate).
151 | if (getScale() == 1F) {
152 | center(true, true);
153 | }
154 | }
155 | break;
156 | }
157 |
158 | return true;
159 | }
160 |
161 | // Pan the displayed image to make sure the cropping rectangle is visible.
162 | private void ensureVisible(HighlightView hv) {
163 | Rect r = hv.mDrawRect;
164 |
165 | int panDeltaX1 = Math.max(0, this.getLeft() - r.left);
166 | int panDeltaX2 = Math.min(0, this.getRight() - r.right);
167 |
168 | int panDeltaY1 = Math.max(0, this.getTop() - r.top);
169 | int panDeltaY2 = Math.min(0, this.getBottom() - r.bottom);
170 |
171 | int panDeltaX = panDeltaX1 != 0 ? panDeltaX1 : panDeltaX2;
172 | int panDeltaY = panDeltaY1 != 0 ? panDeltaY1 : panDeltaY2;
173 |
174 | if (panDeltaX != 0 || panDeltaY != 0) {
175 | panBy(panDeltaX, panDeltaY);
176 | }
177 | }
178 |
179 | // If the cropping rectangle's size changed significantly, change the
180 | // view's center and scale according to the cropping rectangle.
181 | private void centerBasedOnHighlightView(HighlightView hv) {
182 | Rect drawRect = hv.mDrawRect;
183 |
184 | float width = drawRect.width();
185 | float height = drawRect.height();
186 |
187 | float thisWidth = getWidth();
188 | float thisHeight = getHeight();
189 |
190 | float z1 = thisWidth / width * .6F;
191 | float z2 = thisHeight / height * .6F;
192 |
193 | float zoom = Math.min(z1, z2);
194 | zoom = zoom * this.getScale();
195 | zoom = Math.max(1F, zoom);
196 |
197 | if ((Math.abs(zoom - getScale()) / zoom) > .1) {
198 | float [] coordinates = new float[] {hv.mCropRect.centerX(),
199 | hv.mCropRect.centerY()};
200 | getImageMatrix().mapPoints(coordinates);
201 | zoomTo(zoom, coordinates[0], coordinates[1], 300F);
202 | }
203 |
204 | ensureVisible(hv);
205 | }
206 |
207 | @Override
208 | protected void onDraw(Canvas canvas) {
209 | super.onDraw(canvas);
210 | for (int i = 0; i < mHighlightViews.size(); i++) {
211 | mHighlightViews.get(i).draw(canvas);
212 | }
213 | }
214 |
215 | public void add(HighlightView hv) {
216 | mHighlightViews.add(hv);
217 | invalidate();
218 | }
219 |
220 | public ArrayList getHighlightViews() {
221 | return mHighlightViews;
222 | }
223 | }
224 |
--------------------------------------------------------------------------------
/library/src/main/java/com/cncoderx/photopicker/widget/HackyViewPager.java:
--------------------------------------------------------------------------------
1 | package com.cncoderx.photopicker.widget;
2 |
3 | import android.content.Context;
4 | import android.support.v4.view.ViewPager;
5 | import android.util.AttributeSet;
6 | import android.view.MotionEvent;
7 |
8 | public class HackyViewPager extends ViewPager {
9 | private boolean isLocked;
10 |
11 | public HackyViewPager(Context context) {
12 | super(context);
13 | isLocked = false;
14 | }
15 |
16 | public HackyViewPager(Context context, AttributeSet attrs) {
17 | super(context, attrs);
18 | isLocked = false;
19 | }
20 |
21 | @Override
22 | public boolean onInterceptTouchEvent(MotionEvent ev) {
23 | if (!isLocked) {
24 | try {
25 | return super.onInterceptTouchEvent(ev);
26 | } catch (IllegalArgumentException e) {
27 | e.printStackTrace();
28 | return false;
29 | }
30 | }
31 | return false;
32 | }
33 |
34 | @Override
35 | public boolean onTouchEvent(MotionEvent event) {
36 | return !isLocked && super.onTouchEvent(event);
37 | }
38 |
39 | public void toggleLock() {
40 | isLocked = !isLocked;
41 | }
42 |
43 | public void setLocked(boolean isLocked) {
44 | this.isLocked = isLocked;
45 | }
46 |
47 | public boolean isLocked() {
48 | return isLocked;
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/library/src/main/java/com/cncoderx/photopicker/widget/SquareImageView.java:
--------------------------------------------------------------------------------
1 | package com.cncoderx.photopicker.widget;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.widget.ImageView;
6 |
7 |
8 | public class SquareImageView extends ImageView {
9 |
10 | public SquareImageView(Context context) {
11 | super(context);
12 | }
13 |
14 | public SquareImageView(Context context, AttributeSet attrs) {
15 | super(context, attrs);
16 | }
17 |
18 | public SquareImageView(Context context, AttributeSet attrs, int defStyle) {
19 | super(context, attrs, defStyle);
20 | }
21 |
22 | @Override
23 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
24 | int widthMode = MeasureSpec.getMode(widthMeasureSpec);
25 | int heightMode = MeasureSpec.getMode(heightMeasureSpec);
26 | if (widthMode == MeasureSpec.EXACTLY && heightMode != MeasureSpec.EXACTLY) {
27 | int width = MeasureSpec.getSize(widthMeasureSpec);
28 | int height = width;
29 | if (heightMode == MeasureSpec.AT_MOST) {
30 | height = Math.min(height, MeasureSpec.getSize(heightMeasureSpec));
31 | }
32 | setMeasuredDimension(width, height);
33 | } else {
34 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/library/src/main/java/com/cncoderx/photopicker/widget/ToolbarActionButton.java:
--------------------------------------------------------------------------------
1 | package com.cncoderx.photopicker.widget;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.StringRes;
5 | import android.support.v4.view.ActionProvider;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.widget.TextView;
9 |
10 | import com.cncoderx.photopicker.R;
11 |
12 | /**
13 | * Created by admin on 2017/2/21.
14 | */
15 | public class ToolbarActionButton extends ActionProvider {
16 | private Context context;
17 | private TextView mTextView;
18 | private View.OnClickListener mListener;
19 |
20 | public ToolbarActionButton(Context context) {
21 | super(context);
22 | this.context = context;
23 | }
24 |
25 | @Override
26 | public View onCreateActionView() {
27 | LayoutInflater inflater = LayoutInflater.from(context);
28 | View view = inflater.inflate(R.layout.toolbar_button, null);
29 | mTextView = (TextView) view.findViewById(R.id.tv_album_name);
30 | mTextView.setOnClickListener(mListener);
31 | return view;
32 | }
33 |
34 | public void setText(@StringRes int resId) {
35 | mTextView.setText(resId);
36 | }
37 |
38 | public void setText(CharSequence text) {
39 | mTextView.setText(text);
40 | }
41 |
42 | public CharSequence getText() {
43 | return mTextView.getText();
44 | }
45 |
46 | public void setOnClickListener(View.OnClickListener listener) {
47 | mListener = listener;
48 | }
49 |
50 | public void setEnabled(boolean enabled) {
51 | if (mTextView != null) {
52 | mTextView.setEnabled(enabled);
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/library/src/main/res/anim/bottom_bar_push_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
--------------------------------------------------------------------------------
/library/src/main/res/anim/bottom_bar_push_out.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
--------------------------------------------------------------------------------
/library/src/main/res/color/gallery_text_color_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable-hdpi/camera_crop_height.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CNCoderX/PhotoPicker/96ff98bb5ec6d31e1ed495574708b95f79cb70de/library/src/main/res/drawable-hdpi/camera_crop_height.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable-hdpi/camera_crop_width.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CNCoderX/PhotoPicker/96ff98bb5ec6d31e1ed495574708b95f79cb70de/library/src/main/res/drawable-hdpi/camera_crop_width.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CNCoderX/PhotoPicker/96ff98bb5ec6d31e1ed495574708b95f79cb70de/library/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable-hdpi/indicator_autocrop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CNCoderX/PhotoPicker/96ff98bb5ec6d31e1ed495574708b95f79cb70de/library/src/main/res/drawable-hdpi/indicator_autocrop.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable-xhdpi/gallery_ic_camera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CNCoderX/PhotoPicker/96ff98bb5ec6d31e1ed495574708b95f79cb70de/library/src/main/res/drawable-xhdpi/gallery_ic_camera.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable-xhdpi/gallery_ic_close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CNCoderX/PhotoPicker/96ff98bb5ec6d31e1ed495574708b95f79cb70de/library/src/main/res/drawable-xhdpi/gallery_ic_close.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable-xhdpi/gallery_ic_corner_gray.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CNCoderX/PhotoPicker/96ff98bb5ec6d31e1ed495574708b95f79cb70de/library/src/main/res/drawable-xhdpi/gallery_ic_corner_gray.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable/bottom_bar_divider.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable/gallery_bg_bucket.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CNCoderX/PhotoPicker/96ff98bb5ec6d31e1ed495574708b95f79cb70de/library/src/main/res/drawable/gallery_bg_bucket.9.png
--------------------------------------------------------------------------------
/library/src/main/res/drawable/gallery_bucket_item_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 | -
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable/gallery_button_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 | -
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/activity_crop_photo.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/activity_gallery.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
20 |
21 |
25 |
26 |
29 |
30 |
35 |
36 |
40 |
41 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/fragment_multi_photo_preview.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/fragment_photo_grid.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/fragment_single_photo_preview.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/item_album_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
21 |
22 |
29 |
30 |
37 |
38 |
46 |
47 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/item_camera_grid.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/item_photo_grid.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
25 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/item_photo_preview.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/layout_bottom_bar.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
19 |
20 |
31 |
32 |
43 |
44 |
45 |
60 |
--------------------------------------------------------------------------------
/library/src/main/res/layout/toolbar_button.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
14 |
15 |
29 |
30 |
36 |
37 |
--------------------------------------------------------------------------------
/library/src/main/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/library/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 2dp
6 | 2dp
7 |
8 |
--------------------------------------------------------------------------------
/library/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | 图库
3 |
4 | 确定
5 | 取消
6 | 图片
7 | 所有图片
8 | 保存图片
9 | 请稍后…
10 | 裁剪
11 | 预览
12 | 提交
13 | 提交 (%1$d/%2$d)
14 | 最多只能选择%1$s张图片
15 | 共%1$d张
16 | 放大
17 | 缩小
18 | 拍摄照片
19 | 打开相机失败
20 |
21 |
--------------------------------------------------------------------------------
/library/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
16 |
17 |
--------------------------------------------------------------------------------
/library/src/test/java/com/cncoderx/photopicker/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.cncoderx.photopicker;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':library'
2 |
--------------------------------------------------------------------------------