diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..86c290e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,53 @@
+### Android ###
+# Built application files
+*.apk
+*.ap_
+
+# Files for the Dalvik VM
+*.dex
+
+# Java class files
+*.class
+
+# Generated files
+bin/
+gen/
+
+# Gradle files
+.gradle/
+build/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Proguard folder generated by Eclipse
+proguard/
+
+
+### Intellij ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
+
+## Directory-based project format
+.idea/
+# if you remove the above rule, at least ignore user-specific stuff:
+# .idea/workspace.xml
+# .idea/tasks.xml
+# and these sensitive or high-churn files:
+# .idea/dataSources.ids
+# .idea/dataSources.xml
+# .idea/sqlDataSources.xml
+# .idea/dynamic.xml
+
+## File-based project format
+*.ipr
+*.iws
+*.iml
+
+## Additional for IntelliJ
+out/
+
+# generated by mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# generated by JIRA plugin
+atlassian-ide-plugin.xml
\ No newline at end of file
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..38fd060
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,27 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 23
+ buildToolsVersion "23.0.2"
+
+ defaultConfig {
+ applicationId "com.litao.android.androidimagepicker"
+ minSdkVersion 14
+ targetSdkVersion 23
+ versionCode 1
+ versionName "1.0"
+ vectorDrawables.useSupportLibrary = true
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ compile project(':lib')
+ compile 'org.greenrobot:eventbus:3.0.0'
+}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..901932f
--- /dev/null
+++ b/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/demohour/Desktop/litao/soft/adt-bundle-mac-x86_64-20140702/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/app/src/androidTest/java/com/litao/android/androidimagepicker/ApplicationTest.java b/app/src/androidTest/java/com/litao/android/androidimagepicker/ApplicationTest.java
new file mode 100644
index 0000000..fe786a5
--- /dev/null
+++ b/app/src/androidTest/java/com/litao/android/androidimagepicker/ApplicationTest.java
@@ -0,0 +1,13 @@
+package com.litao.android.androidimagepicker;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * Testing Fundamentals
+ */
+public class ApplicationTest extends ApplicationTestCase {
+ public ApplicationTest() {
+ super(Application.class);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..d7518e1
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/com/litao/android/androidimagepicker/ChooseAdapter.java b/app/src/main/java/com/litao/android/androidimagepicker/ChooseAdapter.java
new file mode 100644
index 0000000..eaaa13b
--- /dev/null
+++ b/app/src/main/java/com/litao/android/androidimagepicker/ChooseAdapter.java
@@ -0,0 +1,134 @@
+package com.litao.android.androidimagepicker;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.bumptech.glide.Glide;
+import com.litao.android.lib.entity.PhotoEntry;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Created by 李涛 on 16/4/30.
+ */
+public class ChooseAdapter extends RecyclerView.Adapter {
+
+ private List list = new ArrayList();
+
+ private Context mContext;
+
+ private LayoutInflater mInflater;
+
+ private OnItmeClickListener mlistener;
+
+ public interface OnItmeClickListener{
+ void onItemClicked(int position);
+
+ }
+
+ public ChooseAdapter(Context mContext) {
+ this.mContext = mContext;
+ mlistener = (OnItmeClickListener) mContext;
+ mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ list.add(createAddEntry());
+ }
+
+ public void reloadList(List data) {
+ if (data != null) {
+ list.clear();
+ list.addAll(data);
+ list.add(createAddEntry());
+ } else {
+ list.clear();
+ }
+ notifyDataSetChanged();
+
+ }
+
+ public void appendList(List data) {
+ if (data != null) {
+ list.addAll(list.size()-1,data);
+ } else {
+ list.clear();
+ }
+ notifyDataSetChanged();
+
+ }
+
+
+ public void appendPhoto(PhotoEntry entry) {
+ if (entry != null) {
+ list.add(list.size()-1,entry);
+ }
+ notifyDataSetChanged();
+ }
+
+ public List getData(){
+ return list.subList(0,list.size()-1);
+ }
+ public PhotoEntry getEntry(int position) {
+ return list.get(position);
+ }
+
+ private PhotoEntry createAddEntry(){
+ return new PhotoEntry();
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return position;
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
+ ViewHolder vh = new ViewHolder(mInflater.inflate(R.layout.item_selected_photo, viewGroup, false), i);
+ return vh;
+ }
+
+
+ @Override
+ public void onBindViewHolder(ViewHolder viewHolder, int i) {
+ if (i==list.size()-1){
+ viewHolder.mImageView.setImageResource(R.mipmap.add);
+ }else {
+ PhotoEntry entry = list.get(i);
+ Glide.with(mContext)
+ .load(new File(entry.getPath()))
+ .centerCrop()
+ .placeholder(com.litao.android.lib.R.mipmap.default_image)
+ .into(viewHolder.mImageView);
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ return list.size();
+ }
+
+ public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
+ private ImageView mImageView;
+
+ private int position;
+
+ public ViewHolder(View itemView, int position) {
+ super(itemView);
+ this.position = position;
+ mImageView = (ImageView) itemView.findViewById(R.id.image);
+ mImageView.setOnClickListener(this);
+ }
+
+ @Override
+ public void onClick(View view) {
+ mlistener.onItemClicked(position);
+ }
+ }
+
+}
diff --git a/app/src/main/java/com/litao/android/androidimagepicker/EventEntry.java b/app/src/main/java/com/litao/android/androidimagepicker/EventEntry.java
new file mode 100644
index 0000000..f57d477
--- /dev/null
+++ b/app/src/main/java/com/litao/android/androidimagepicker/EventEntry.java
@@ -0,0 +1,24 @@
+package com.litao.android.androidimagepicker;
+
+import com.litao.android.lib.entity.PhotoEntry;
+
+import java.util.List;
+
+/**
+ * Created by 李涛 on 16/4/30.
+ */
+public class EventEntry {
+
+ public static final int RECEIVED_PHOTOS_ID = 0x00000010;
+
+ public static final int SELECTED_PHOTOS_ID = 0x00000020;
+
+
+ public List photos;
+ public int id;
+
+ public EventEntry(List photos, int id){
+ this.photos = photos;
+ this.id = id;
+ }
+}
diff --git a/app/src/main/java/com/litao/android/androidimagepicker/MainActivity.java b/app/src/main/java/com/litao/android/androidimagepicker/MainActivity.java
new file mode 100644
index 0000000..5873d7d
--- /dev/null
+++ b/app/src/main/java/com/litao/android/androidimagepicker/MainActivity.java
@@ -0,0 +1,70 @@
+package com.litao.android.androidimagepicker;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.Menu;
+
+import com.litao.android.lib.Utils.GridSpacingItemDecoration;
+import com.litao.android.lib.entity.PhotoEntry;
+
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+public class MainActivity extends AppCompatActivity implements ChooseAdapter.OnItmeClickListener {
+
+ private RecyclerView mRecyclerView;
+
+ private ChooseAdapter mAdapter;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ EventBus.getDefault().register(this);
+
+ mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
+ mAdapter = new ChooseAdapter(this);
+ mRecyclerView.setLayoutManager(new GridLayoutManager(this, 3));
+ mRecyclerView.setAdapter(mAdapter);
+ mRecyclerView.addItemDecoration(new GridSpacingItemDecoration(3, 4, true));
+
+ }
+
+ @Override
+ protected void onDestroy() {
+ EventBus.getDefault().unregister(this);
+ super.onDestroy();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_main, menu);
+ return true;
+ }
+
+ @Override
+ public void onItemClicked(int position) {
+ if (position == mAdapter.getItemCount()-1) {
+ startActivity(new Intent(MainActivity.this, PhotosActivity.class));
+ EventBus.getDefault().postSticky(new EventEntry(mAdapter.getData(),EventEntry.SELECTED_PHOTOS_ID));
+ }
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void photosMessageEvent(EventEntry entries){
+ if (entries.id == EventEntry.RECEIVED_PHOTOS_ID) {
+ mAdapter.reloadList(entries.photos);
+ }
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void photoMessageEvent(PhotoEntry entry){
+ mAdapter.appendPhoto(entry);
+ }
+
+}
diff --git a/app/src/main/java/com/litao/android/androidimagepicker/PhotosActivity.java b/app/src/main/java/com/litao/android/androidimagepicker/PhotosActivity.java
new file mode 100644
index 0000000..534edd3
--- /dev/null
+++ b/app/src/main/java/com/litao/android/androidimagepicker/PhotosActivity.java
@@ -0,0 +1,147 @@
+package com.litao.android.androidimagepicker;
+
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.TextView;
+
+import com.litao.android.lib.BaseGalleryActivity;
+import com.litao.android.lib.Configuration;
+import com.litao.android.lib.entity.PhotoEntry;
+
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+import java.util.List;
+
+/**
+ * Created by 李涛 on 16/4/29.
+ */
+public class PhotosActivity extends BaseGalleryActivity implements View.OnClickListener {
+
+ private TextView mTextViewOpenAlbum;
+ private TextView mTextViewSelectedCount;
+ private TextView mTextViewSend;
+
+ private List mSelectedPhotos;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_photos);
+ EventBus.getDefault().register(this);
+ setTitle("Photos");
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setDisplayShowHomeEnabled(true);
+ initView();
+ attachFragment(R.id.gallery_root);
+ }
+
+ @Override
+ protected void onDestroy() {
+ EventBus.getDefault().unregister(this);
+ super.onDestroy();
+ }
+
+ private void initView(){
+
+ mTextViewOpenAlbum = (TextView) findViewById(R.id.album);
+ mTextViewSelectedCount = (TextView) findViewById(R.id.selected_count);
+ mTextViewSend = (TextView) findViewById(R.id.send_photos);
+
+ mTextViewOpenAlbum.setOnClickListener(this);
+ mTextViewSelectedCount.setOnClickListener(this);
+ mTextViewSend.setOnClickListener(this);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_main, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home){
+ finish();
+ }
+ return super.onOptionsItemSelected(item);
+
+ }
+
+ @Override
+ public void onClick(View view) {
+ switch(view.getId()){
+ case R.id.album:
+ openAlbum();
+ break;
+ case R.id.send_photos:
+ sendPhotos();
+ break;
+ }
+ }
+
+ @Override
+ public Configuration getConfiguration() {
+ Configuration cfg=new Configuration.Builder()
+ .hasCamera(true)
+ .hasShade(true)
+ .hasPreview(true)
+ .setSpaceSize(3)
+ .setPhotoMaxWidth(120)
+ .setCheckBoxColor(0xFF3F51B5)
+ .setDialogHeight(Configuration.DIALOG_HALF)
+ .setDialogMode(Configuration.DIALOG_GRID)
+ .setMaximum(9)
+ .setTip(null)
+ .setAblumsTitle(null)
+ .build();
+ return cfg;
+ }
+
+ @Override
+ public List getSelectPhotos() {
+ return mSelectedPhotos;
+ }
+
+ @Override
+ public void onSelectedCountChanged(int count) {
+ mTextViewSelectedCount.setVisibility(count>0?View.VISIBLE:View.INVISIBLE);
+ mTextViewSelectedCount.setText(String.valueOf(count));
+ }
+
+ @Override
+ public void onAlbumChanged(String name) {
+ getSupportActionBar().setSubtitle(name);
+ }
+
+ @Override
+ public void onTakePhoto(PhotoEntry entry) {
+ EventBus.getDefault().post(entry);
+ finish();
+ }
+
+ @Override
+ public void onChoosePhotos(List entries) {
+ EventBus.getDefault().post(new EventEntry(entries,EventEntry.RECEIVED_PHOTOS_ID));
+ finish();
+ }
+
+ @Override
+ public void onPhotoClick(PhotoEntry entry) {
+
+ }
+
+ @Subscribe(sticky = true,threadMode = ThreadMode.MAIN)
+ public void photosMessageEvent(EventEntry entry){
+ if (entry.id == EventEntry.SELECTED_PHOTOS_ID) {
+ mSelectedPhotos = entry.photos;
+ }
+ }
+
+
+}
diff --git a/app/src/main/res/drawable/shape_round.xml b/app/src/main/res/drawable/shape_round.xml
new file mode 100644
index 0000000..0499bf1
--- /dev/null
+++ b/app/src/main/res/drawable/shape_round.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..124dd52
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,15 @@
+
+
+
+
diff --git a/app/src/main/res/layout/activity_photos.xml b/app/src/main/res/layout/activity_photos.xml
new file mode 100644
index 0000000..4e5a08f
--- /dev/null
+++ b/app/src/main/res/layout/activity_photos.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_selected_photo.xml b/app/src/main/res/layout/item_selected_photo.xml
new file mode 100644
index 0000000..d7ddcca
--- /dev/null
+++ b/app/src/main/res/layout/item_selected_photo.xml
@@ -0,0 +1,10 @@
+
+
+
+
diff --git a/app/src/main/res/menu/menu_main.xml b/app/src/main/res/menu/menu_main.xml
new file mode 100644
index 0000000..b1cb908
--- /dev/null
+++ b/app/src/main/res/menu/menu_main.xml
@@ -0,0 +1,6 @@
+
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/add.png b/app/src/main/res/mipmap-xhdpi/add.png
new file mode 100644
index 0000000..ef868e2
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/add.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..77c7a47
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+
+
+ #FF424242
+ #66000000
+ #FFFFFFFF
+ #212121
+ #3F51B5
+ #3F51B5
+ #FF4081
+
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..34d1fbe
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+ Android-Image-Picker
+
+ Hello world!
+ Settings
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..4883d6b
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,19 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.1.0-beta3'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
diff --git a/gif/gallery.gif b/gif/gallery.gif
new file mode 100644
index 0000000..ae883d1
Binary files /dev/null and b/gif/gallery.gif differ
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..1d3591c
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,18 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..fd3e60d
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Apr 21 19:18:30 CST 2016
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/lib/.gitignore b/lib/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/lib/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/lib/build.gradle b/lib/build.gradle
new file mode 100644
index 0000000..fcd7d77
--- /dev/null
+++ b/lib/build.gradle
@@ -0,0 +1,26 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 23
+ buildToolsVersion "23.0.2"
+
+ defaultConfig {
+ minSdkVersion 14
+ targetSdkVersion 23
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ compile 'com.android.support:design:23.3.0'
+ compile 'com.android.support:recyclerview-v7:23.3.0'
+ compile 'com.github.bumptech.glide:glide:3.7.0'
+}
diff --git a/lib/proguard-rules.pro b/lib/proguard-rules.pro
new file mode 100644
index 0000000..901932f
--- /dev/null
+++ b/lib/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/demohour/Desktop/litao/soft/adt-bundle-mac-x86_64-20140702/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/lib/src/androidTest/java/com/litao/android/lib/ApplicationTest.java b/lib/src/androidTest/java/com/litao/android/lib/ApplicationTest.java
new file mode 100644
index 0000000..20ff5fd
--- /dev/null
+++ b/lib/src/androidTest/java/com/litao/android/lib/ApplicationTest.java
@@ -0,0 +1,13 @@
+package com.litao.android.lib;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * Testing Fundamentals
+ */
+public class ApplicationTest extends ApplicationTestCase {
+ public ApplicationTest() {
+ super(Application.class);
+ }
+}
\ No newline at end of file
diff --git a/lib/src/main/AndroidManifest.xml b/lib/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..fae626f
--- /dev/null
+++ b/lib/src/main/AndroidManifest.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
diff --git a/lib/src/main/java/com/litao/android/lib/BaseGalleryActivity.java b/lib/src/main/java/com/litao/android/lib/BaseGalleryActivity.java
new file mode 100644
index 0000000..8c7f04b
--- /dev/null
+++ b/lib/src/main/java/com/litao/android/lib/BaseGalleryActivity.java
@@ -0,0 +1,28 @@
+package com.litao.android.lib;
+
+
+import android.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+
+/**
+ * Created by 李涛 on 16/4/22.
+ */
+public abstract class BaseGalleryActivity extends AppCompatActivity implements GalleryFragment.OnGalleryAttachedListener {
+
+ private GalleryFragment fragment;
+
+ protected void attachFragment(int layoutId) {
+ fragment = (GalleryFragment) GalleryFragment.newInstance();
+ FragmentManager fragmentManager = getFragmentManager();
+ fragmentManager.beginTransaction().replace(layoutId, fragment).commit();
+ }
+
+ protected void openAlbum() {
+ fragment.openAlbum();
+ }
+
+ protected void sendPhotos() {
+ fragment.sendPhtots();
+ }
+
+}
diff --git a/lib/src/main/java/com/litao/android/lib/Configuration.java b/lib/src/main/java/com/litao/android/lib/Configuration.java
new file mode 100644
index 0000000..4421dd7
--- /dev/null
+++ b/lib/src/main/java/com/litao/android/lib/Configuration.java
@@ -0,0 +1,212 @@
+package com.litao.android.lib;
+
+/**
+ * Created by 李涛 on 16/4/21.
+ */
+public class Configuration {
+
+ /**
+ * ablums dialog full screen
+ */
+ public static final int DIALOG_FULL = -1;
+
+ /**
+ * ablums dialog half screen
+ */
+ public static final int DIALOG_HALF = -2;
+
+ /**
+ * ablums dialog mode grid view
+ */
+ public static final int DIALOG_GRID = -1;
+
+ /**
+ * ablums dialog mode list view
+ */
+ public static final int DIALOG_LIST = -2;
+
+ /**
+ * Flag to indicate that this view has a camera button
+ */
+ public boolean hasCamera;
+
+ /**
+ * Flag to indicate that this photo view has a layer mask
+ */
+ public boolean hasShade;
+
+ /**
+ * Flag to indicate that this photo view clickable
+ */
+ public boolean hasPreview;
+
+ /**
+ * GridView spacing between rows and columns
+ */
+ public int spaceSize;
+
+ /**
+ * Maximum width of photos
+ */
+ public int photoMaxWidth;
+
+ /**
+ * Checkbox background color
+ */
+ public int checkBoxColor;
+
+ /**
+ * ablums dialog height
+ */
+ public int dialogHeight;
+
+ /**
+ * ablums dialog title
+ */
+ public String ablumsTitle;
+
+ /**
+ * maximum photos selection limit
+ */
+ public int maximum;
+
+ /**
+ * ablums dialog mode
+ *
+ * DIALOG_FULL or DIALOG_HALF
+ */
+ public int dialogMode;
+
+ /**
+ * Toast of maximum photos selection limit
+ */
+ public String tip;
+
+ private Configuration(final Builder builder) {
+ this.hasCamera = builder.hasCamera;
+ this.spaceSize = builder.spaceSize;
+ this.photoMaxWidth = builder.photoMaxWidth;
+ this.checkBoxColor = builder.checkBoxColor;
+ this.dialogHeight = builder.dialogHeight;
+ this.maximum = builder.maximum;
+ this.tip = builder.tip;
+ this.ablumsTitle = builder.ablumsTitle;
+ this.hasShade = builder.hasShade;
+ this.dialogMode = builder.dialogMode;
+ this.hasPreview = builder.hasPreview;
+ }
+
+ public static class Builder {
+
+ private boolean hasCamera;
+
+ private boolean hasShade;
+
+ private boolean hasPreview;
+
+ private int spaceSize;
+
+ private int photoMaxWidth;
+
+ private int checkBoxColor;
+
+ private int dialogHeight;
+
+ private int dialogMode;
+
+ public String ablumsTitle;
+
+ private int maximum;
+
+ private String tip;
+
+ public Builder() {
+ hasCamera = true;
+ hasShade = true;
+ hasPreview = true;
+ spaceSize = 4;
+ photoMaxWidth = 120;
+ checkBoxColor = 0xFF3F51B5;
+ dialogHeight = DIALOG_HALF;
+ dialogMode = DIALOG_GRID;
+ maximum = 9;
+ tip = null;
+ ablumsTitle = null;
+ }
+
+ public Builder(final Configuration cfg) {
+ hasCamera = cfg.hasCamera;
+ hasShade = cfg.hasShade;
+ hasPreview = cfg.hasPreview;
+ spaceSize = cfg.spaceSize;
+ photoMaxWidth = cfg.photoMaxWidth;
+ checkBoxColor = cfg.checkBoxColor;
+ dialogHeight = cfg.dialogHeight;
+ dialogMode = cfg.dialogMode;
+ maximum = cfg.maximum;
+ tip = cfg.tip;
+ ablumsTitle = cfg.ablumsTitle;
+
+ }
+
+ public Builder hasCamera(boolean hasCamera) {
+ this.hasCamera = hasCamera;
+ return this;
+ }
+
+ public Builder hasShade(boolean hasShade){
+ this.hasShade = hasShade;
+ return this;
+ }
+
+ public Builder hasPreview(boolean hasPreview){
+ this.hasPreview = hasPreview;
+ return this;
+ }
+
+ public Builder setSpaceSize(int spaceSize) {
+ this.spaceSize = spaceSize;
+ return this;
+ }
+
+ public Builder setPhotoMaxWidth(int photoMaxWidth) {
+ this.photoMaxWidth = photoMaxWidth;
+ return this;
+ }
+
+ public Builder setCheckBoxColor(int checkBoxColor) {
+ this.checkBoxColor = checkBoxColor;
+ return this;
+ }
+
+
+ public Builder setDialogHeight(int dialogHeight) {
+ this.dialogHeight = dialogHeight;
+ return this;
+ }
+
+ public Builder setDialogMode(int dialogMode){
+ this.dialogMode = dialogMode;
+ return this;
+ }
+
+ public Builder setAblumsTitle(String ablumsTitle) {
+ this.ablumsTitle = ablumsTitle;
+ return this;
+ }
+
+ public Builder setMaximum(int maximum) {
+ this.maximum = maximum;
+ return this;
+ }
+
+ public Builder setTip(String maximumMsg) {
+ this.tip = tip;
+ return this;
+ }
+
+ public Configuration build() {
+ return new Configuration(this);
+ }
+ }
+}
diff --git a/lib/src/main/java/com/litao/android/lib/GalleryFragment.java b/lib/src/main/java/com/litao/android/lib/GalleryFragment.java
new file mode 100644
index 0000000..b05f829
--- /dev/null
+++ b/lib/src/main/java/com/litao/android/lib/GalleryFragment.java
@@ -0,0 +1,310 @@
+package com.litao.android.lib;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.MediaStore;
+import android.support.annotation.Nullable;
+import android.support.design.widget.BottomSheetBehavior;
+import android.support.design.widget.BottomSheetDialog;
+import android.support.v7.widget.DefaultItemAnimator;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.litao.android.lib.Utils.GridSpacingItemDecoration;
+import com.litao.android.lib.Utils.Utils;
+import com.litao.android.lib.Utils.VerticalSpaceItemDecoration;
+import com.litao.android.lib.adapter.AlbumsGridAdapter;
+import com.litao.android.lib.adapter.AlbumsListAdapter;
+import com.litao.android.lib.adapter.PhotosAdapter;
+import com.litao.android.lib.controller.MediaController;
+import com.litao.android.lib.entity.AlbumEntry;
+import com.litao.android.lib.entity.PhotoEntry;
+import com.litao.android.lib.view.MDCheckBox;
+import com.litao.android.lib.view.SquaredView;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by 李涛 on 16/4/21.
+ */
+public class GalleryFragment extends Fragment implements MediaController.OnViewClickListener, MediaController.OnDataLoadListener {
+
+ private final String TAG = "GalleryFragment";
+
+ /**
+ * take photo request code
+ */
+ private static final int REQUEST_TAKE_PHOTO = 0x00001024;
+
+ /**
+ * take photo id
+ */
+ private static final int TAKE_PHOTO_ID = Integer.MAX_VALUE;
+
+ private int mPhotoColumnNumber;
+
+ private int mAlbumColumnNumber;
+
+ private PhotosAdapter mAdapterPhoto;
+
+ private AlbumsGridAdapter mAdapterAlbumGrid;
+
+ private AlbumsListAdapter mAdapterAlbumList;
+
+ private Configuration mConfig;
+
+ private MediaController mMediaController;
+
+ private List albumsSorted;
+
+ private List selectedPhotos;
+
+ private BottomSheetDialog dialog;
+
+ private BottomSheetBehavior mBehavior;
+
+ private File mTmpFile;
+
+ private OnGalleryAttachedListener mListener;
+
+ public interface OnGalleryAttachedListener {
+ Configuration getConfiguration();
+
+ List getSelectPhotos();
+
+ void onSelectedCountChanged(int count);
+
+ void onAlbumChanged(String name);
+
+ void onTakePhoto(PhotoEntry entry);
+
+ void onChoosePhotos(List entries);
+
+ void onPhotoClick(PhotoEntry entry);
+ }
+
+ public static Fragment newInstance() {
+ return new GalleryFragment();
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mConfig = mListener.getConfiguration();
+
+ mAdapterPhoto = new PhotosAdapter(getActivity(), this, mConfig);
+
+ if (mConfig.dialogMode >= Configuration.DIALOG_GRID) {
+ mAdapterAlbumGrid = new AlbumsGridAdapter(getActivity(), this);
+ }else {
+ mAdapterAlbumList = new AlbumsListAdapter(getActivity(),this);
+ }
+
+ selectedPhotos = new ArrayList();
+ mMediaController = new MediaController(this, getActivity());
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ mPhotoColumnNumber = getGallerWidth(container) / dp2px(mConfig.photoMaxWidth);
+ mAlbumColumnNumber = getGallerWidth(container) / dp2px(mConfig.photoMaxWidth * 1.5f);
+ return inflater.inflate(R.layout.layout_photos, container, false);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ RecyclerView mGalleryView = (RecyclerView) view.findViewById(R.id.recycler_view);
+ mGalleryView.setLayoutManager(new GridLayoutManager(getActivity(), mPhotoColumnNumber));
+ mGalleryView.setAdapter(mAdapterPhoto);
+ mGalleryView.addItemDecoration(new GridSpacingItemDecoration(mPhotoColumnNumber, dp2px(mConfig.spaceSize), true));
+
+
+ mMediaController.loadGalleryPhotos();
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ try {
+ mListener = (OnGalleryAttachedListener) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString() + " must implement OnGalleryAttachedListener");
+ }
+ }
+
+
+ public void openAlbum() {
+ int screenHeight = Utils.getScreenHeight(getActivity());
+ if (dialog != null && dialog.isShowing()) {
+ dialog.dismiss();
+ return;
+ }
+ dialog = new BottomSheetDialog(getActivity());
+
+ View view = LayoutInflater.from(getActivity()).inflate(R.layout.layout_albums, null);
+
+ ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, screenHeight);
+ view.setLayoutParams(lp);
+
+ TextView mTextViewTitle = (TextView) view.findViewById(R.id.title);
+ mTextViewTitle.setText(mConfig.ablumsTitle == null ? getString(R.string.album) : mConfig.ablumsTitle);
+
+ RecyclerView mGalleryView = (RecyclerView) view.findViewById(R.id.recycler_view);
+
+ if (mConfig.dialogMode >= Configuration.DIALOG_GRID) {
+ mGalleryView.setLayoutManager(new GridLayoutManager(getActivity(), mAlbumColumnNumber));
+ mGalleryView.addItemDecoration(new GridSpacingItemDecoration(mAlbumColumnNumber, dp2px(mConfig.spaceSize), true));
+ mGalleryView.setItemAnimator(new DefaultItemAnimator());
+ mGalleryView.setAdapter(mAdapterAlbumGrid);
+ mAdapterAlbumGrid.appendList(albumsSorted);
+ }else {
+ mGalleryView.setLayoutManager(new LinearLayoutManager(getActivity()));
+ mGalleryView.addItemDecoration(new VerticalSpaceItemDecoration(dp2px(mConfig.spaceSize)));
+ mGalleryView.setItemAnimator(new DefaultItemAnimator());
+ mGalleryView.setAdapter(mAdapterAlbumList);
+ mAdapterAlbumList.appendList(albumsSorted);
+ }
+
+
+ dialog.setContentView(view);
+ mBehavior = BottomSheetBehavior.from((View) view.getParent());
+
+ if (mConfig.dialogHeight < 0) {
+ mBehavior.setPeekHeight(mConfig.dialogHeight <= Configuration.DIALOG_HALF ? screenHeight / 2 : screenHeight);
+ } else {
+ mBehavior.setPeekHeight(mConfig.dialogHeight >= screenHeight ? screenHeight : mConfig.dialogHeight);
+ }
+ dialog.show();
+ }
+
+ public void openCamera() {
+ Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+ if (cameraIntent.resolveActivity(getActivity().getPackageManager()) != null) {
+ mTmpFile = Utils.createTmpFile(getActivity());
+ cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mTmpFile));
+ this.startActivityForResult(cameraIntent, REQUEST_TAKE_PHOTO);
+ } else {
+ Log.e(TAG,"No camera is found");
+ }
+ }
+
+ public void sendPhtots() {
+ mListener.onChoosePhotos(selectedPhotos);
+ }
+
+ public void togglePhoto(int position, MDCheckBox mCheckBox, SquaredView viewShade){
+ if (mConfig.maximum <= selectedPhotos.size() && !mCheckBox.isChecked()) {
+ Toast.makeText(getActivity(), mConfig.tip == null ? String.format(getString(R.string.select_maximum),mConfig.maximum) : mConfig.tip, Toast.LENGTH_SHORT).show();
+ } else {
+ PhotoEntry photoEntry = mAdapterPhoto.getEntry(position);
+ mCheckBox.toggle();
+ viewShade.toggle();
+ photoEntry.toggle();
+ if (mCheckBox.isChecked()) {
+ selectedPhotos.add(photoEntry);
+ } else {
+ selectedPhotos.remove(photoEntry);
+ }
+ mListener.onSelectedCountChanged(selectedPhotos.size());
+ }
+ }
+
+ private void updateCheckstatus() {
+ List allEntries = albumsSorted.get(0).getPhotos();
+ if (mListener.getSelectPhotos() != null) {
+ for (PhotoEntry entry1 : mListener.getSelectPhotos()) {
+ for (PhotoEntry entry2 : allEntries) {
+ if (TextUtils.equals(entry2.getPath(),entry1.getPath())) {
+ entry2.setChecked(true);
+ selectedPhotos.add(entry2);
+ break;
+ }
+ }
+ }
+ }
+ mListener.onSelectedCountChanged(selectedPhotos.size());
+ mAdapterPhoto.notifyDataSetChanged();
+ }
+
+
+ private int dp2px(float dp) {
+ return (int) (dp * getActivity().getResources().getDisplayMetrics().density + 0.5f);
+ }
+
+ private int getGallerWidth(ViewGroup container) {
+ return Utils.getScreenWidth(getActivity()) - container.getPaddingLeft() - container.getPaddingRight();
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ if (requestCode == REQUEST_TAKE_PHOTO) {
+ if (resultCode == Activity.RESULT_OK) {
+ PhotoEntry entry = new PhotoEntry();
+ entry.setImageId(TAKE_PHOTO_ID);
+ entry.setPath(mTmpFile.getAbsolutePath());
+ Utils.addMediaToGallery(getActivity().getApplicationContext(), Uri.fromFile(new File(mTmpFile.getAbsolutePath())));
+ mListener.onTakePhoto(entry);
+ }
+ }
+ }
+
+ @Override
+ public void onPhotoClicked(int position, MDCheckBox mCheckBox, SquaredView viewShade) {
+ if (mConfig.hasPreview) {
+ mListener.onPhotoClick(mAdapterPhoto.getEntry(position));
+ }else {
+ togglePhoto(position,mCheckBox,viewShade);
+ }
+ }
+
+ @Override
+ public void onAlbumClicked(int position, View imageView) {
+ mListener.onAlbumChanged(albumsSorted.get(position).getBucketName());
+ mAdapterPhoto.reloadList(albumsSorted.get(position).getPhotos());
+ dialog.dismiss();
+ }
+
+ @Override
+ public void onCheckBoxClicked(int position, MDCheckBox mCheckBox,SquaredView viewShade) {
+ togglePhoto(position,mCheckBox,viewShade);
+ }
+
+ @Override
+ public void onCameraClicked() {
+ if (mConfig.maximum > selectedPhotos.size()) {
+ openCamera();
+ } else {
+ Toast.makeText(getActivity(), mConfig.tip == null ? String.format(getString(R.string.select_maximum),mConfig.maximum) : mConfig.tip, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ @Override
+ public void onLoadFinished(List data) {
+ if (data != null && data.size()>0) {
+ albumsSorted = data;
+ mListener.onAlbumChanged(data.get(0).getBucketName());
+ mAdapterPhoto.reloadList(data.get(0).getPhotos());
+ updateCheckstatus();
+ }else {
+ mAdapterPhoto.addCamera();
+ }
+ }
+
+}
diff --git a/lib/src/main/java/com/litao/android/lib/Utils/GridSpacingItemDecoration.java b/lib/src/main/java/com/litao/android/lib/Utils/GridSpacingItemDecoration.java
new file mode 100644
index 0000000..e4cefd1
--- /dev/null
+++ b/lib/src/main/java/com/litao/android/lib/Utils/GridSpacingItemDecoration.java
@@ -0,0 +1,45 @@
+package com.litao.android.lib.Utils;
+
+import android.graphics.Rect;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+/**
+ * Created by 李涛 on 16/4/22.
+ *
+ * http://stackoverflow.com/questions/28531996/android-recyclerview-gridlayoutmanager-column-spacing/30701422#30701422
+ */
+public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
+
+ private int spanCount;
+ private int spacing;
+ private boolean includeEdge;
+
+ public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) {
+ this.spanCount = spanCount;
+ this.spacing = spacing;
+ this.includeEdge = includeEdge;
+ }
+
+ @Override
+ public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
+ int position = parent.getChildAdapterPosition(view); // item position
+ int column = position % spanCount; // item column
+
+ if (includeEdge) {
+ outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing)
+ outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing)
+
+ if (position < spanCount) { // top edge
+ outRect.top = spacing;
+ }
+ outRect.bottom = spacing; // item bottom
+ } else {
+ outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing)
+ outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing)
+ if (position >= spanCount) {
+ outRect.top = spacing; // item top
+ }
+ }
+ }
+}
diff --git a/lib/src/main/java/com/litao/android/lib/Utils/Utils.java b/lib/src/main/java/com/litao/android/lib/Utils/Utils.java
new file mode 100644
index 0000000..3cf7fee
--- /dev/null
+++ b/lib/src/main/java/com/litao/android/lib/Utils/Utils.java
@@ -0,0 +1,56 @@
+package com.litao.android.lib.Utils;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Point;
+import android.net.Uri;
+import android.os.Environment;
+import android.view.Display;
+
+import java.io.File;
+import java.util.UUID;
+
+/**
+ * Created by 李涛 on 16/4/28.
+ */
+public class Utils {
+ private static Point getScreenSize(Context activity) {
+ Display display = ((Activity) activity).getWindowManager().getDefaultDisplay();
+ Point size = new Point();
+ display.getSize(size);
+ return size;
+ }
+
+ public static int getScreenWidth(Context activity) {
+ return getScreenSize(activity).x;
+ }
+
+ public static int getScreenHeight(Context activity) {
+ return getScreenSize(activity).y;
+ }
+
+ public static File createTmpFile(Context context) {
+
+ String state = Environment.getExternalStorageState();
+ if (state.equals(Environment.MEDIA_MOUNTED)) {
+ File pic = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
+ String fileName = UUID.randomUUID().toString();
+ return new File(pic, fileName + ".jpg");
+ } else {
+ File cacheDir = context.getCacheDir();
+ String fileName = UUID.randomUUID().toString();
+ return new File(cacheDir, fileName + ".jpg");
+ }
+
+ }
+
+ public static void addMediaToGallery(Context context,Uri uri) {
+ if (uri == null) {
+ return;
+ }
+ Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+ mediaScanIntent.setData(uri);
+ context.sendBroadcast(mediaScanIntent);
+ }
+}
diff --git a/lib/src/main/java/com/litao/android/lib/Utils/VerticalSpaceItemDecoration.java b/lib/src/main/java/com/litao/android/lib/Utils/VerticalSpaceItemDecoration.java
new file mode 100644
index 0000000..a5caf98
--- /dev/null
+++ b/lib/src/main/java/com/litao/android/lib/Utils/VerticalSpaceItemDecoration.java
@@ -0,0 +1,25 @@
+package com.litao.android.lib.Utils;
+
+import android.graphics.Rect;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+/**
+ * Created by 李涛 on 16/5/2.
+ */
+public class VerticalSpaceItemDecoration extends RecyclerView.ItemDecoration {
+
+ private final int mVerticalSpaceHeight;
+
+ public VerticalSpaceItemDecoration(int mVerticalSpaceHeight) {
+ this.mVerticalSpaceHeight = mVerticalSpaceHeight;
+ }
+
+ @Override
+ public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
+ RecyclerView.State state) {
+ if (parent.getChildAdapterPosition(view) != parent.getAdapter().getItemCount() - 1) {
+ outRect.bottom = mVerticalSpaceHeight;
+ }
+ }
+}
diff --git a/lib/src/main/java/com/litao/android/lib/adapter/AlbumsGridAdapter.java b/lib/src/main/java/com/litao/android/lib/adapter/AlbumsGridAdapter.java
new file mode 100644
index 0000000..1469b8c
--- /dev/null
+++ b/lib/src/main/java/com/litao/android/lib/adapter/AlbumsGridAdapter.java
@@ -0,0 +1,111 @@
+package com.litao.android.lib.adapter;
+
+import android.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.bumptech.glide.Glide;
+import com.litao.android.lib.R;
+import com.litao.android.lib.controller.MediaController;
+import com.litao.android.lib.entity.AlbumEntry;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by 李涛 on 16/4/25.
+ */
+public class AlbumsGridAdapter extends RecyclerView.Adapter {
+
+ private List list = new ArrayList();
+
+ private Context mContext;
+
+ public MediaController.OnViewClickListener mClickListener;
+
+ public AlbumsGridAdapter(Context mContext, MediaController.OnViewClickListener mClickListener) {
+ this.mClickListener = mClickListener;
+ this.mContext = mContext;
+ }
+
+ public void appendList(List data) {
+ if (data != null) {
+ list.clear();
+ list.addAll(data);
+ } else {
+ list.clear();
+ }
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return position;
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
+ ViewHolder vh = new ViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_grid_album, null), mClickListener, i);
+ return vh;
+ }
+
+
+ @Override
+ public void onBindViewHolder(ViewHolder viewHolder, int i) {
+ AlbumEntry entry = list.get(i);
+
+ viewHolder.mTextViewName.setText(entry.getBucketName());
+ viewHolder.mTextViewCount.setText(String.valueOf(entry.getPhotos().size()));
+
+ Glide.with(mContext)
+ .load(new File(entry.getCoverPhoto().getPath()))
+ .centerCrop()
+ .placeholder(R.mipmap.default_image)
+ .into(viewHolder.mImageView);
+ }
+
+ @Override
+ public int getItemCount() {
+ return list.size();
+ }
+
+ public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
+
+ private RelativeLayout mLayout;
+
+ private ImageView mImageView;
+
+ private TextView mTextViewName;
+
+ private TextView mTextViewCount;
+
+ public MediaController.OnViewClickListener mListener;
+
+ private int position;
+
+ public ViewHolder(View itemView, MediaController.OnViewClickListener mListener, int position) {
+ super(itemView);
+
+ this.position = position;
+ this.mListener = mListener;
+
+ mLayout = (RelativeLayout) itemView.findViewById(R.id.grid_item);
+ mImageView = (ImageView) itemView.findViewById(R.id.image);
+ mTextViewName = (TextView) itemView.findViewById(R.id.name);
+ mTextViewCount = (TextView) itemView.findViewById(R.id.count);
+
+ mLayout.setOnClickListener(this);
+ }
+
+ @Override
+ public void onClick(View view) {
+ mListener.onAlbumClicked(position,view);
+ }
+ }
+}
diff --git a/lib/src/main/java/com/litao/android/lib/adapter/AlbumsListAdapter.java b/lib/src/main/java/com/litao/android/lib/adapter/AlbumsListAdapter.java
new file mode 100644
index 0000000..5aeb969
--- /dev/null
+++ b/lib/src/main/java/com/litao/android/lib/adapter/AlbumsListAdapter.java
@@ -0,0 +1,111 @@
+package com.litao.android.lib.adapter;
+
+import android.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.bumptech.glide.Glide;
+import com.litao.android.lib.R;
+import com.litao.android.lib.controller.MediaController;
+import com.litao.android.lib.entity.AlbumEntry;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by 李涛 on 16/4/25.
+ */
+public class AlbumsListAdapter extends RecyclerView.Adapter {
+
+ private List list = new ArrayList();
+
+ private Context mContext;
+
+ public MediaController.OnViewClickListener mClickListener;
+
+ public AlbumsListAdapter(Context mContext, MediaController.OnViewClickListener mClickListener) {
+ this.mClickListener = mClickListener;
+ this.mContext = mContext;
+ }
+
+ public void appendList(List data) {
+ if (data != null) {
+ list.clear();
+ list.addAll(data);
+ } else {
+ list.clear();
+ }
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return position;
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
+ ViewHolder vh = new ViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_list_album, null), mClickListener, i);
+ return vh;
+ }
+
+
+ @Override
+ public void onBindViewHolder(ViewHolder viewHolder, int i) {
+ AlbumEntry entry = list.get(i);
+
+ viewHolder.mTextViewName.setText(entry.getBucketName());
+ viewHolder.mTextViewCount.setText(String.valueOf(entry.getPhotos().size()));
+
+ Glide.with(mContext)
+ .load(new File(entry.getCoverPhoto().getPath()))
+ .centerCrop()
+ .placeholder(R.mipmap.default_image)
+ .into(viewHolder.mImageView);
+ }
+
+ @Override
+ public int getItemCount() {
+ return list.size();
+ }
+
+ public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
+
+ private RelativeLayout mLayout;
+
+ private ImageView mImageView;
+
+ private TextView mTextViewName;
+
+ private TextView mTextViewCount;
+
+ public MediaController.OnViewClickListener mListener;
+
+ private int position;
+
+ public ViewHolder(View itemView, MediaController.OnViewClickListener mListener, int position) {
+ super(itemView);
+
+ this.position = position;
+ this.mListener = mListener;
+
+ mLayout = (RelativeLayout) itemView.findViewById(R.id.list_item);
+ mImageView = (ImageView) itemView.findViewById(R.id.image);
+ mTextViewName = (TextView) itemView.findViewById(R.id.name);
+ mTextViewCount = (TextView) itemView.findViewById(R.id.count);
+
+ mLayout.setOnClickListener(this);
+ }
+
+ @Override
+ public void onClick(View view) {
+ mListener.onAlbumClicked(position, view);
+ }
+ }
+}
\ No newline at end of file
diff --git a/lib/src/main/java/com/litao/android/lib/adapter/PhotosAdapter.java b/lib/src/main/java/com/litao/android/lib/adapter/PhotosAdapter.java
new file mode 100644
index 0000000..c69196c
--- /dev/null
+++ b/lib/src/main/java/com/litao/android/lib/adapter/PhotosAdapter.java
@@ -0,0 +1,176 @@
+package com.litao.android.lib.adapter;
+
+import android.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.bumptech.glide.Glide;
+import com.litao.android.lib.Configuration;
+import com.litao.android.lib.R;
+import com.litao.android.lib.controller.MediaController;
+import com.litao.android.lib.entity.PhotoEntry;
+import com.litao.android.lib.view.MDCheckBox;
+import com.litao.android.lib.view.SquaredView;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by 李涛 on 16/4/21.
+ */
+public class PhotosAdapter extends RecyclerView.Adapter {
+
+ private List list = new ArrayList();
+
+ private Context mContext;
+
+ private LayoutInflater mInflater;
+
+ private Configuration mConfig;
+
+ public MediaController.OnViewClickListener mClickListener;
+
+ public PhotosAdapter(Context mContext, MediaController.OnViewClickListener mClickListener, Configuration mConfig) {
+ this.mClickListener = mClickListener;
+ this.mContext = mContext;
+ this.mConfig = mConfig;
+ mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ public void reloadList(List data) {
+ if (data != null) {
+ list.clear();
+ if (mConfig.hasCamera) {
+ PhotoEntry cameraEntry = new PhotoEntry();
+ list.add(cameraEntry);
+ }
+ list.addAll(data);
+ } else {
+ list.clear();
+ }
+ notifyDataSetChanged();
+ }
+
+ public void appendList(List data) {
+ if (data != null) {
+ if (mConfig.hasCamera) {
+ PhotoEntry cameraEntry = new PhotoEntry();
+ list.add(cameraEntry);
+ }
+ list.addAll(data);
+ } else {
+ list.clear();
+ }
+ notifyDataSetChanged();
+ }
+
+ public void appendPhoto(PhotoEntry entry) {
+ if (entry != null) {
+ list.add(entry);
+ }
+ notifyDataSetChanged();
+ }
+
+ public void addCamera(){
+ if (mConfig.hasCamera) {
+ PhotoEntry cameraEntry = new PhotoEntry();
+ list.add(cameraEntry);
+ }
+ notifyDataSetChanged();
+ }
+
+ public PhotoEntry getEntry(int position) {
+ return list.get(mConfig.hasCamera ? position + 1 : position);
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return position;
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
+ ViewHolder vh = new ViewHolder(mInflater.inflate(R.layout.item_photo, viewGroup, false), mClickListener, i, mConfig.hasCamera);
+ return vh;
+ }
+
+
+ @Override
+ public void onBindViewHolder(ViewHolder viewHolder, int i) {
+
+ if (mConfig.hasCamera && i == 0) {
+ viewHolder.mImageView.setImageResource(R.mipmap.camera);
+ viewHolder.mCheckBox.setVisibility(View.INVISIBLE);
+
+ } else {
+ PhotoEntry entry = list.get(i);
+ viewHolder.mCheckBox.setChecked(entry.isChecked());
+ viewHolder.mCheckBox.setVisibility(View.VISIBLE);
+ if (mConfig.hasShade) {
+ viewHolder.mViewShade.setVisibility(entry.isChecked() ? View.VISIBLE : View.INVISIBLE);
+ }
+
+ Glide.with(mContext)
+ .load(new File(entry.getPath()))
+ .centerCrop()
+ .placeholder(R.mipmap.default_image)
+ .into(viewHolder.mImageView);
+
+ }
+ viewHolder.mCheckBox.setCheckBoxColor(mConfig.checkBoxColor);
+ }
+
+ @Override
+ public int getItemCount() {
+ return list.size();
+ }
+
+ public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
+
+ private ImageView mImageView;
+
+ private MDCheckBox mCheckBox;
+
+ private SquaredView mViewShade;
+
+ public MediaController.OnViewClickListener mListener;
+
+ private int position;
+
+ private boolean hasCamera;
+
+ public ViewHolder(View itemView, MediaController.OnViewClickListener mListener, int position, boolean hasCamera) {
+
+ super(itemView);
+
+ this.position = position;
+ this.hasCamera = hasCamera;
+ this.mListener = mListener;
+
+ mImageView = (ImageView) itemView.findViewById(R.id.image);
+ mCheckBox = (MDCheckBox) itemView.findViewById(R.id.checkbox);
+ mViewShade = (SquaredView) itemView.findViewById(R.id.shade);
+ mImageView.setOnClickListener(this);
+ mCheckBox.setOnClickListener(this);
+ }
+
+ @Override
+ public void onClick(View view) {
+ if (mListener == null) return;
+
+ if (view instanceof ImageView) {
+ if (position == 0 && hasCamera) {
+ mListener.onCameraClicked();
+ }else {
+ mListener.onPhotoClicked(hasCamera ? position - 1 : position, mCheckBox, mViewShade);
+ }
+ } else if (view instanceof MDCheckBox) {
+ mListener.onCheckBoxClicked(hasCamera ? position - 1 : position, mCheckBox, mViewShade);
+ }
+ }
+ }
+}
diff --git a/lib/src/main/java/com/litao/android/lib/controller/MediaController.java b/lib/src/main/java/com/litao/android/lib/controller/MediaController.java
new file mode 100644
index 0000000..fecdaa9
--- /dev/null
+++ b/lib/src/main/java/com/litao/android/lib/controller/MediaController.java
@@ -0,0 +1,132 @@
+package com.litao.android.lib.controller;
+
+import android.app.Activity;
+import android.content.Context;
+import android.database.Cursor;
+import android.os.Environment;
+import android.provider.MediaStore;
+import android.view.View;
+
+import com.litao.android.lib.entity.AlbumEntry;
+import com.litao.android.lib.entity.PhotoEntry;
+import com.litao.android.lib.view.MDCheckBox;
+import com.litao.android.lib.view.SquaredView;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Created by 李涛 on 16/4/22.
+ */
+public class MediaController {
+ private String TAG = "MediaController";
+
+ private OnDataLoadListener listener;
+
+ private Activity mContext;
+
+ private static final String[] projectionPhotos = {
+ MediaStore.Images.Media._ID,
+ MediaStore.Images.Media.BUCKET_ID,
+ MediaStore.Images.Media.BUCKET_DISPLAY_NAME,
+ MediaStore.Images.Media.DATA,
+ MediaStore.Images.Media.DATE_TAKEN
+ };
+
+ public interface OnDataLoadListener {
+ void onLoadFinished(List data);
+ }
+
+ public interface OnViewClickListener {
+ void onPhotoClicked(int position, MDCheckBox checkBox, SquaredView viewShade);
+
+ void onAlbumClicked(int position, View imageView);
+
+ void onCheckBoxClicked(int position, MDCheckBox checkBox, SquaredView viewShade);
+
+ void onCameraClicked();
+
+ }
+
+
+ public MediaController(OnDataLoadListener listener, Activity mContext) {
+ this.listener = listener;
+ this.mContext = mContext;
+ }
+
+ public void loadGalleryPhotos() {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ final ArrayList albumsSorted = new ArrayList<>();
+ HashMap albums = new HashMap<>();
+ AlbumEntry allPhotosAlbum = null;
+ String cameraFolder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath() + "/" + "Camera/";
+
+ Cursor cursor = null;
+
+ try {
+ cursor = MediaStore.Images.Media.query(mContext.getContentResolver(), MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projectionPhotos, "", null, MediaStore.Images.Media.DATE_TAKEN + " DESC");
+ if (cursor != null) {
+ int imageIdColumn = cursor.getColumnIndex(MediaStore.Images.Media._ID);
+ int bucketIdColumn = cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_ID);
+ int bucketNameColumn = cursor.getColumnIndex(MediaStore.Images.Media.BUCKET_DISPLAY_NAME);
+ int dataColumn = cursor.getColumnIndex(MediaStore.Images.Media.DATA);
+ int dateColumn = cursor.getColumnIndex(MediaStore.Images.Media.DATE_TAKEN);
+
+ while (cursor.moveToNext()) {
+ int imageId = cursor.getInt(imageIdColumn);
+ int bucketId = cursor.getInt(bucketIdColumn);
+ String bucketName = cursor.getString(bucketNameColumn);
+ String path = cursor.getString(dataColumn);
+ long dateTaken = cursor.getLong(dateColumn);
+
+ if (path == null || path.length() == 0) {
+ continue;
+ }
+
+ PhotoEntry photoEntry = new PhotoEntry(bucketId, imageId, dateTaken, path);
+
+ if (allPhotosAlbum == null) {
+ allPhotosAlbum = new AlbumEntry(0, "All Photos", photoEntry);
+ albumsSorted.add(0, allPhotosAlbum);
+ }
+
+ allPhotosAlbum.addPhoto(photoEntry);
+
+ AlbumEntry albumEntry = albums.get(bucketId);
+ if (albumEntry == null) {
+ albumEntry = new AlbumEntry(bucketId, bucketName, photoEntry);
+ albums.put(bucketId, albumEntry);
+
+ albumsSorted.add(albumEntry);
+ }
+
+ albumEntry.addPhoto(photoEntry);
+ }
+ mContext.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ listener.onLoadFinished(albumsSorted);
+ }
+ });
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ if (cursor != null) {
+ try {
+ cursor.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ }
+ }).start();
+ }
+
+
+}
diff --git a/lib/src/main/java/com/litao/android/lib/entity/AlbumEntry.java b/lib/src/main/java/com/litao/android/lib/entity/AlbumEntry.java
new file mode 100644
index 0000000..ae09a30
--- /dev/null
+++ b/lib/src/main/java/com/litao/android/lib/entity/AlbumEntry.java
@@ -0,0 +1,68 @@
+package com.litao.android.lib.entity;
+
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by 李涛 on 16/4/22.
+ */
+public class AlbumEntry {
+ private int bucketId;
+ private String bucketName;
+ private PhotoEntry coverPhoto;
+ private SparseArray photoById = new SparseArray();
+ private List photos = new ArrayList();
+
+ public AlbumEntry(int bucketId, String bucketName, PhotoEntry coverPhoto) {
+ this.bucketId = bucketId;
+ this.bucketName = bucketName;
+ this.coverPhoto = coverPhoto;
+ }
+
+ public void addPhoto(PhotoEntry photoEntry) {
+ photoById.put(photoEntry.getImageId(), photoEntry);
+ photos.add(photoEntry);
+ }
+
+ public String getBucketName() {
+ return bucketName;
+ }
+
+ public void setBucketName(String bucketName) {
+ this.bucketName = bucketName;
+ }
+
+ public int getBucketId() {
+ return bucketId;
+ }
+
+ public void setBucketId(int bucketId) {
+ this.bucketId = bucketId;
+ }
+
+ public PhotoEntry getCoverPhoto() {
+ return coverPhoto;
+ }
+
+ public void setCoverPhoto(PhotoEntry coverPhoto) {
+ this.coverPhoto = coverPhoto;
+ }
+
+ public SparseArray getPhotoById() {
+ return photoById;
+ }
+
+ public void setPhotoById(SparseArray photoById) {
+ this.photoById = photoById;
+ }
+
+ public List getPhotos() {
+ return photos;
+ }
+
+ public void setPhotos(List photos) {
+ this.photos = photos;
+ }
+}
diff --git a/lib/src/main/java/com/litao/android/lib/entity/PhotoEntry.java b/lib/src/main/java/com/litao/android/lib/entity/PhotoEntry.java
new file mode 100644
index 0000000..f9d17f7
--- /dev/null
+++ b/lib/src/main/java/com/litao/android/lib/entity/PhotoEntry.java
@@ -0,0 +1,68 @@
+package com.litao.android.lib.entity;
+
+/**
+ * Created by 李涛 on 16/4/21.
+ */
+public class PhotoEntry {
+
+ private int bucketId;
+ private int imageId;
+ private long dateTaken;
+ private String path;
+ private boolean isChecked;
+
+ public PhotoEntry() {
+
+ }
+
+ public PhotoEntry(int bucketId, int imageId, long dateTaken, String path) {
+ this.bucketId = bucketId;
+ this.imageId = imageId;
+ this.dateTaken = dateTaken;
+ this.path = path;
+ }
+
+ public void toggle() {
+ isChecked = !isChecked;
+ }
+
+ public int getBucketId() {
+ return bucketId;
+ }
+
+ public void setBucketId(int bucketId) {
+ this.bucketId = bucketId;
+ }
+
+ public int getImageId() {
+ return imageId;
+ }
+
+ public void setImageId(int imageId) {
+ this.imageId = imageId;
+ }
+
+ public long getDateTaken() {
+ return dateTaken;
+ }
+
+ public void setDateTaken(long dateTaken) {
+ this.dateTaken = dateTaken;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ }
+
+ public boolean isChecked() {
+ return isChecked;
+ }
+
+ public void setChecked(boolean checked) {
+ isChecked = checked;
+ }
+}
diff --git a/lib/src/main/java/com/litao/android/lib/view/MDCheckBox.java b/lib/src/main/java/com/litao/android/lib/view/MDCheckBox.java
new file mode 100644
index 0000000..b3036d2
--- /dev/null
+++ b/lib/src/main/java/com/litao/android/lib/view/MDCheckBox.java
@@ -0,0 +1,234 @@
+package com.litao.android.lib.view;
+
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.Checkable;
+
+import com.litao.android.lib.R;
+
+/**
+ * Created by 李涛 on 16/4/25.
+ */
+public class MDCheckBox extends View implements Checkable {
+
+ private final static float BOUNCE_VALUE = 0.2f;
+
+ private Drawable checkDrawable;
+
+ private Paint bitmapPaint;
+ private Paint bitmapEraser;
+ private Paint checkEraser;
+ private Paint borderPaint;
+ private Paint uncheckPaint;
+
+ private Bitmap drawBitmap;
+ private Bitmap checkBitmap;
+ private Canvas bitmapCanvas;
+ private Canvas checkCanvas;
+
+ private float progress;
+ private ObjectAnimator checkAnim;
+
+ private boolean attachedToWindow;
+ private boolean isChecked;
+
+ private int size = 26;
+ private int bitmapColor = 0xFF3F51B5;
+ private int borderColor = 0xFFFFFFFF;
+ private int uncheckColor = 0x22000000;
+
+ public MDCheckBox(Context context) {
+ this(context, null);
+ }
+
+ public MDCheckBox(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public MDCheckBox(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init(context, attrs);
+ }
+
+ private void init(Context context, AttributeSet attrs) {
+ bitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ bitmapEraser = new Paint(Paint.ANTI_ALIAS_FLAG);
+ uncheckPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ bitmapEraser.setColor(0);
+ bitmapEraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+ checkEraser = new Paint(Paint.ANTI_ALIAS_FLAG);
+ checkEraser.setColor(0);
+ checkEraser.setStyle(Paint.Style.STROKE);
+ checkEraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+ borderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ borderPaint.setStyle(Paint.Style.STROKE);
+ borderPaint.setStrokeWidth(dp(2));
+ checkDrawable = context.getResources().getDrawable(R.mipmap.check);
+ setVisibility(VISIBLE);
+ }
+
+ @Override
+ public void setVisibility(int visibility) {
+ super.setVisibility(visibility);
+ if (visibility == VISIBLE && drawBitmap == null) {
+ drawBitmap = Bitmap.createBitmap(dp(size), dp(size), Bitmap.Config.ARGB_8888);
+ bitmapCanvas = new Canvas(drawBitmap);
+ checkBitmap = Bitmap.createBitmap(dp(size), dp(size), Bitmap.Config.ARGB_8888);
+ checkCanvas = new Canvas(checkBitmap);
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int newSpec = MeasureSpec.makeMeasureSpec(dp(size), MeasureSpec.getMode(Math.min(widthMeasureSpec, heightMeasureSpec)));
+ super.onMeasure(newSpec, newSpec);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (getVisibility() != VISIBLE) {
+ return;
+ }
+ checkEraser.setStrokeWidth(dp(size));
+
+ drawBitmap.eraseColor(0);
+ float rad = getMeasuredWidth() / 2;
+
+ float bitmapProgress = progress >= 0.5f ? 1.0f : progress / 0.5f;
+ float checkProgress = progress < 0.5f ? 0.0f : (progress - 0.5f) / 0.5f;
+
+ float p = isChecked ? progress : (1.0f - progress);
+
+ if (p < BOUNCE_VALUE) {
+ rad -= dp(2) * p;
+ } else if (p < BOUNCE_VALUE * 2) {
+ rad -= dp(2) - dp(2) * p;
+ }
+
+ borderPaint.setColor(borderColor);
+ uncheckPaint.setColor(uncheckColor);
+ canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, rad - dp(1), borderPaint);
+ canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, rad - dp(2), uncheckPaint);
+
+ bitmapPaint.setColor(bitmapColor);
+
+ bitmapCanvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, rad, bitmapPaint);
+ bitmapCanvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, rad * (1 - bitmapProgress), bitmapEraser);
+ canvas.drawBitmap(drawBitmap, 0, 0, null);
+
+ checkBitmap.eraseColor(0);
+ int w = checkDrawable.getIntrinsicWidth();
+ int h = checkDrawable.getIntrinsicHeight();
+ int x = (getMeasuredWidth() - w) / 2;
+ int y = (getMeasuredHeight() - h) / 2;
+
+ checkDrawable.setBounds(x, y, x + w, y + h);
+ checkDrawable.draw(checkCanvas);
+ checkCanvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, rad * (1 - checkProgress), checkEraser);
+
+ canvas.drawBitmap(checkBitmap, 0, 0, null);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ attachedToWindow = true;
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ attachedToWindow = false;
+ }
+
+
+ public void setProgress(float value) {
+ if (progress == value) {
+ return;
+ }
+ progress = value;
+ invalidate();
+ }
+
+ public void setCheckBoxColor(int bitmapColor) {
+ this.bitmapColor = bitmapColor;
+ }
+
+ public void setCheckBoxSize(int size) {
+ this.size = size;
+ }
+
+ public float getProgress() {
+ return progress;
+ }
+
+ public void setCheckedColor(int value) {
+ bitmapColor = value;
+ }
+
+ public void setBorderColor(int value) {
+ borderColor = value;
+ borderPaint.setColor(borderColor);
+ }
+
+ private void cancelAnim() {
+ if (checkAnim != null) {
+ checkAnim.cancel();
+ }
+ }
+
+ private void addAnim(boolean isChecked) {
+ checkAnim = ObjectAnimator.ofFloat(this, "progress", isChecked ? 1.0f : 0.0f);
+ checkAnim.setDuration(300);
+ checkAnim.start();
+ }
+
+ public void setChecked(boolean checked, boolean animated) {
+ if (checked == isChecked) {
+ return;
+ }
+ isChecked = checked;
+
+ if (attachedToWindow && animated) {
+ addAnim(checked);
+ } else {
+ cancelAnim();
+ setProgress(checked ? 1.0f : 0.0f);
+ }
+ }
+
+ public void toggle(boolean animated) {
+ setChecked(!isChecked, animated);
+ }
+
+ @Override
+ public void toggle() {
+ setChecked(!isChecked);
+ }
+
+ @Override
+ public void setChecked(boolean b) {
+ setChecked(b, true);
+ }
+
+ @Override
+ public boolean isChecked() {
+ return isChecked;
+ }
+
+ public int dp(float value) {
+ if (value == 0) {
+ return 0;
+ }
+ float density = getContext().getResources().getDisplayMetrics().density;
+ return (int) Math.ceil(density * value);
+ }
+}
diff --git a/lib/src/main/java/com/litao/android/lib/view/SquaredImageView.java b/lib/src/main/java/com/litao/android/lib/view/SquaredImageView.java
new file mode 100644
index 0000000..b66035e
--- /dev/null
+++ b/lib/src/main/java/com/litao/android/lib/view/SquaredImageView.java
@@ -0,0 +1,24 @@
+package com.litao.android.lib.view;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+/**
+ * Created by 李涛 on 16/4/21.
+ */
+public class SquaredImageView extends ImageView {
+ public SquaredImageView(Context context) {
+ super(context);
+ }
+
+ public SquaredImageView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth());
+ }
+}
diff --git a/lib/src/main/java/com/litao/android/lib/view/SquaredView.java b/lib/src/main/java/com/litao/android/lib/view/SquaredView.java
new file mode 100644
index 0000000..b9e67e6
--- /dev/null
+++ b/lib/src/main/java/com/litao/android/lib/view/SquaredView.java
@@ -0,0 +1,28 @@
+package com.litao.android.lib.view;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * Created by 李涛 on 16/5/2.
+ */
+public class SquaredView extends View {
+ public SquaredView(Context context) {
+ super(context);
+ }
+
+ public SquaredView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth());
+ }
+
+ public void toggle(){
+ setVisibility(getVisibility()==VISIBLE?INVISIBLE:VISIBLE);
+ }
+}
diff --git a/lib/src/main/res/drawable/item_list_click_color.xml b/lib/src/main/res/drawable/item_list_click_color.xml
new file mode 100644
index 0000000..f1c7807
--- /dev/null
+++ b/lib/src/main/res/drawable/item_list_click_color.xml
@@ -0,0 +1,14 @@
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/lib/src/main/res/layout/item_grid_album.xml b/lib/src/main/res/layout/item_grid_album.xml
new file mode 100644
index 0000000..64fddab
--- /dev/null
+++ b/lib/src/main/res/layout/item_grid_album.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
diff --git a/lib/src/main/res/layout/item_list_album.xml b/lib/src/main/res/layout/item_list_album.xml
new file mode 100644
index 0000000..97daeb9
--- /dev/null
+++ b/lib/src/main/res/layout/item_list_album.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lib/src/main/res/layout/item_photo.xml b/lib/src/main/res/layout/item_photo.xml
new file mode 100644
index 0000000..57bc8d9
--- /dev/null
+++ b/lib/src/main/res/layout/item_photo.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
diff --git a/lib/src/main/res/layout/layout_albums.xml b/lib/src/main/res/layout/layout_albums.xml
new file mode 100644
index 0000000..b8eb71e
--- /dev/null
+++ b/lib/src/main/res/layout/layout_albums.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/lib/src/main/res/layout/layout_photos.xml b/lib/src/main/res/layout/layout_photos.xml
new file mode 100644
index 0000000..870bfd3
--- /dev/null
+++ b/lib/src/main/res/layout/layout_photos.xml
@@ -0,0 +1,12 @@
+
+
+
+
\ No newline at end of file
diff --git a/lib/src/main/res/mipmap-hdpi/check.png b/lib/src/main/res/mipmap-hdpi/check.png
new file mode 100755
index 0000000..f00b4ff
Binary files /dev/null and b/lib/src/main/res/mipmap-hdpi/check.png differ
diff --git a/lib/src/main/res/mipmap-mdpi/check.png b/lib/src/main/res/mipmap-mdpi/check.png
new file mode 100755
index 0000000..6ff1a82
Binary files /dev/null and b/lib/src/main/res/mipmap-mdpi/check.png differ
diff --git a/lib/src/main/res/mipmap-xhdpi/camera.png b/lib/src/main/res/mipmap-xhdpi/camera.png
new file mode 100644
index 0000000..735f522
Binary files /dev/null and b/lib/src/main/res/mipmap-xhdpi/camera.png differ
diff --git a/lib/src/main/res/mipmap-xhdpi/check.png b/lib/src/main/res/mipmap-xhdpi/check.png
new file mode 100755
index 0000000..0ccccb7
Binary files /dev/null and b/lib/src/main/res/mipmap-xhdpi/check.png differ
diff --git a/lib/src/main/res/mipmap-xhdpi/default_image.png b/lib/src/main/res/mipmap-xhdpi/default_image.png
new file mode 100644
index 0000000..1f51881
Binary files /dev/null and b/lib/src/main/res/mipmap-xhdpi/default_image.png differ
diff --git a/lib/src/main/res/mipmap-xhdpi/icon.png b/lib/src/main/res/mipmap-xhdpi/icon.png
new file mode 100644
index 0000000..75087d6
Binary files /dev/null and b/lib/src/main/res/mipmap-xhdpi/icon.png differ
diff --git a/lib/src/main/res/mipmap-xhdpi/to.png b/lib/src/main/res/mipmap-xhdpi/to.png
new file mode 100644
index 0000000..8ca21c8
Binary files /dev/null and b/lib/src/main/res/mipmap-xhdpi/to.png differ
diff --git a/lib/src/main/res/values-zh/strings.xml b/lib/src/main/res/values-zh/strings.xml
new file mode 100644
index 0000000..45d904d
--- /dev/null
+++ b/lib/src/main/res/values-zh/strings.xml
@@ -0,0 +1,5 @@
+
+
+ 相册
+ 最多选择%1$d张
+
\ No newline at end of file
diff --git a/lib/src/main/res/values/attrs.xml b/lib/src/main/res/values/attrs.xml
new file mode 100644
index 0000000..2b6083b
--- /dev/null
+++ b/lib/src/main/res/values/attrs.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lib/src/main/res/values/colors.xml b/lib/src/main/res/values/colors.xml
new file mode 100644
index 0000000..b0ff92a
--- /dev/null
+++ b/lib/src/main/res/values/colors.xml
@@ -0,0 +1,8 @@
+
+
+ #FF424242
+ #66000000
+ #FFFFFFFF
+ #FF212121
+ #FF424242
+
\ No newline at end of file
diff --git a/lib/src/main/res/values/strings.xml b/lib/src/main/res/values/strings.xml
new file mode 100644
index 0000000..d69ea5e
--- /dev/null
+++ b/lib/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+ Lib
+ Albums
+ select a maximum of %1$d photos
+
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..3cbe249
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+include ':app', ':lib'