<repo_name>tylerjroach/SlidingShareSelector<file_sep>/README.md
# SlidingShareSelector - Android

An adaptable BottomSheetDialogFragment that shows a scrolling list of recent photos, along with a grid of custimizable icons for sharing.

![demo](preview.gif?raw=true "Demo Preview")

## How to use
Make sure your app has permission for android.permission.READ_EXTERNAL_STORAGE

Create a list of share actions by passing a drawable, background color, and callback id for each option.
```
final ShareSelectorOption[] shareOptions = new ShareSelectorOption[] {
    new ShareSelectorOption(R.drawable.ic_camera_alt_white_24dp, R.color.green, SELECTION_CAMERA),
    new ShareSelectorOption(R.drawable.ic_videocam_white_24dp, R.color.red, SELECTION_VIDEO),
    new ShareSelectorOption(R.drawable.ic_image_white_24dp, R.color.blue, SELECTION_GALLERY),
    new ShareSelectorOption(R.drawable.ic_place_white_24dp, R.color.purple, SELECTION_LOCATION)
};
```
Show the sliding selector by instantiating the fragment and calling show().
```
addMedia = (ImageButton) findViewById(R.id.add_media);
addMedia.setOnClickListener(new View.OnClickListener() {
  @Override public void onClick(View v) {
    if (getSupportFragmentManager().findFragmentByTag(SlidingShareSelector.class.getName()) == null) {
      SlidingShareSelector slidingShareSelector = SlidingShareSelector.newInstance(shareOptions, true);
      slidingShareSelector.show(getSupportFragmentManager(), SlidingShareSelector.class.getName());
    }
  }
});
```
Take action based on the callback received
```
@Override public void onThumbnailClicked(Uri thumbUri) {
  dismissShareSelector();
  SelectedImageDialogFragment.newInstance(thumbUri.toString()).show(getSupportFragmentManager(), SelectedImageDialogFragment.class.getName());
}

@Override public void onShareOptionClicked(int optionId) {
  String selection = "";
  switch (optionId) {
    case SELECTION_CAMERA:
      selection = "Camera";
      break;
    case SELECTION_VIDEO:
      selection = "Video";
      break;
    case SELECTION_GALLERY:
      selection = "Gallery";
      break;
    case SELECTION_LOCATION:
      selection = "Location";
      break;
  }

  dismissShareSelector();
  Toast.makeText(this, "Selected: " + selection, Toast.LENGTH_SHORT).show();
}
```

## Comments
Feel free to suggest any additions or create pull requests. 
This was created in a couple of hours and doesn't have many configuration options. 
Documentation is also non-existent, but the code should be simple to figure out.

##Future Updates (Time Permitting)
Post to MavenCentral - For now, build aar manually<br>
Fix lazy paddings/margins/itemdecorations to properly follow material design<br>
Add documentation to code<br>
Add more customization options

##Known Issues
I haven't taken into account a users gallery could be empty

## License
```
Copyright 2016 <NAME>

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```
<file_sep>/app/src/main/java/com/tylerjroach/slidingshareselector/example/ui/screens/SelectedImageDialogFragment.java
package com.tylerjroach.slidingshareselector.example.ui.screens;

import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.squareup.picasso.Picasso;
import com.tylerjroach.slidingshareselector.example.R;

public class SelectedImageDialogFragment extends DialogFragment {

  private static final String CONTENT_PATH = "CONTENT_PATH";

  private AppCompatActivity ACA;

  private ImageView imageView;

  private Uri contentUri;

  public SelectedImageDialogFragment() {}

  public static SelectedImageDialogFragment newInstance(String contentPath) {
    SelectedImageDialogFragment fragment = new SelectedImageDialogFragment();
    Bundle args = new Bundle();
    args.putString(CONTENT_PATH, contentPath);
    fragment.setArguments(args);
    return fragment;
  }

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (getArguments() != null) {
      contentUri = Uri.parse(getArguments().getString(CONTENT_PATH));
    }
    setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Black_NoTitleBar_Fullscreen);
  }

  @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View rootView = inflater.inflate(R.layout.fragment_selected_image_dialog, container, false);
    imageView = (ImageView) rootView.findViewById(R.id.image);

    Picasso.with(ACA).load(contentUri).fit().centerCrop().into(imageView);

    return rootView;
  }

  @Override public void onAttach(Context context) {
    super.onAttach(context);
    ACA = (AppCompatActivity) context;
  }

  @Override public void onDetach() {
    super.onDetach();
    ACA = null;
  }
}
<file_sep>/slidingshareselector/src/main/java/com/tylerjroach/slidingshareselector/ui/screens/RecentsRecyclerAdapter.java
package com.tylerjroach.slidingshareselector.ui.screens;

import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.MediaStore;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import com.squareup.picasso.Picasso;
import com.tylerjroach.slidingshareselector.R;
import com.tylerjroach.slidingshareselector.data.Thumbnail;
import com.tylerjroach.slidingshareselector.storage.DbHelper;
import com.tylerjroach.slidingshareselector.ui.adapters.CursorRecyclerAdapter;

/**
 * Created by @tylerjroach on 10/3/16.
 */
public class RecentsRecyclerAdapter
    extends CursorRecyclerAdapter<RecentsRecyclerAdapter.ThumbnailViewHolder> {

  private Context context;
  private ThumbnailSelectionListener listener;

  public RecentsRecyclerAdapter(ThumbnailSelectionListener listener, Cursor cursor, Context context) {
    super(cursor);
    this.context = context;
    this.listener = listener;
  }

  @Override public ThumbnailViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View v = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
    ThumbnailViewHolder vh = new ThumbnailViewHolder(v);
    return vh;
  }

  @Override public void onBindViewHolderCursor(final ThumbnailViewHolder holder, final Cursor cursor, int position) {

    final Thumbnail thumbnail = ((DbHelper.ThumbnailCursor) cursor).getThumbnail();
    final Uri imageUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, thumbnail.getId());
    Picasso.with(context).load(imageUri).fit().centerCrop().into(holder.thumbnail);

    holder.thumbnail.setOnClickListener(new View.OnClickListener() {
      @Override public void onClick(View v) {
        if (listener != null)
          listener.onThumbnailSelected(imageUri);
      }
    });

  }

  @Override public int getItemViewType(int position) {
    return R.layout.grid_square_thumbnail;
  }

  public static class ThumbnailViewHolder extends RecyclerView.ViewHolder {
    private ImageView thumbnail;

    public ThumbnailViewHolder(View v) {
      super(v);
      thumbnail = (ImageView) v.findViewById(R.id.thumbnail);
    }
  }

  public interface ThumbnailSelectionListener {
    void onThumbnailSelected(Uri imageUri);
  }
}
<file_sep>/slidingshareselector/src/main/java/com/tylerjroach/slidingshareselector/data/ShareSelectorOption.java
package com.tylerjroach.slidingshareselector.data;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Created by tylerjroach on 10/3/16.
 */

public class ShareSelectorOption implements Parcelable {

  private int drawableRes;
  private int backgroundColorRes;
  private int callbackId;

  public ShareSelectorOption(int drawableRes, int backgroundColorRes, int callbackId) {
    this.drawableRes = drawableRes;
    this.backgroundColorRes = backgroundColorRes;
    this.callbackId = callbackId;
  }

  public int getDrawableRes() {
    return drawableRes;
  }

  public void setDrawableRes(int drawableRes) {
    this.drawableRes = drawableRes;
  }

  public int getBackgroundColorRes() {
    return backgroundColorRes;
  }

  public void setBackgroundColorRes(int backgroundColorRes) {
    this.backgroundColorRes = backgroundColorRes;
  }

  public int getCallbackId() {
    return callbackId;
  }

  public void setCallbackId(int callbackId) {
    this.callbackId = callbackId;
  }

  @Override public int describeContents() {
    return 0;
  }

  @Override public void writeToParcel(Parcel dest, int flags) {
    dest.writeInt(this.drawableRes);
    dest.writeInt(this.backgroundColorRes);
    dest.writeInt(this.callbackId);
  }

  protected ShareSelectorOption(Parcel in) {
    this.drawableRes = in.readInt();
    this.backgroundColorRes = in.readInt();
    this.callbackId = in.readInt();
  }

  public static final Parcelable.Creator<ShareSelectorOption> CREATOR =
      new Parcelable.Creator<ShareSelectorOption>() {
        @Override public ShareSelectorOption createFromParcel(Parcel source) {
          return new ShareSelectorOption(source);
        }

        @Override public ShareSelectorOption[] newArray(int size) {
          return new ShareSelectorOption[size];
        }
      };
}
<file_sep>/slidingshareselector/src/main/java/com/tylerjroach/slidingshareselector/ui/screens/SelectionRecyclerAdapter.java
package com.tylerjroach.slidingshareselector.ui.screens;

import android.content.Context;
import android.database.Cursor;
import android.graphics.Rect;
import android.graphics.drawable.GradientDrawable;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.tylerjroach.slidingshareselector.R;
import com.tylerjroach.slidingshareselector.data.ShareSelectorOption;
import com.tylerjroach.slidingshareselector.ui.views.ForegroundImageView;

/**
 * Created by @tylerjroach on 10/3/16.
 */
public class SelectionRecyclerAdapter extends RecyclerView.Adapter<SelectionRecyclerAdapter.BaseViewHolder> {

  private final int VIEW_TYPE_RECENTS_SLIDER = 1;
  private final int VIEW_TYPE_GRID_ITEM = 2;

  private Context context;

  private RecyclerView selectionRecycler;
  private RecentsRecyclerAdapter recentsRecyclerAdapter;
  private LinearLayoutManager recentsLayoutManager;
  private OptionSelectedListener listener;

  private ShareSelectorOption[] shareSelectorOptions;
  private boolean roundIconBackground;

  public SelectionRecyclerAdapter(ShareSelectorOption[] shareSelectorOptions,
      RecyclerView selectionRecycler,
      RecentsRecyclerAdapter recentsRecyclerAdapter,
      LinearLayoutManager recentsLayoutManager,
      boolean roundIconBackground,
      OptionSelectedListener optionSelectedListener,
      final Context context) {
    this.shareSelectorOptions = shareSelectorOptions;
    this.selectionRecycler = selectionRecycler;

    final GridLayoutManager selectionRecyclerLayoutManager = (GridLayoutManager) this.selectionRecycler.getLayoutManager();
    selectionRecyclerLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
      @Override public int getSpanSize(int position) {
        if (position == 0 && selectionRecyclerLayoutManager != null)
          //position 0 is recents header
          return selectionRecyclerLayoutManager.getSpanCount();
        else
          return 1;
      }
    });

    this.recentsRecyclerAdapter = recentsRecyclerAdapter;
    this.recentsLayoutManager = recentsLayoutManager;
    this.roundIconBackground = roundIconBackground;
    this.listener = optionSelectedListener;
    this.context = context;
  }

  // Create new views (invoked by the layout manager)
  @Override public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    if (viewType == VIEW_TYPE_RECENTS_SLIDER) {
      View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_nested_thumbnail_recycler, parent, false);
      return  new NestedRecyclerViewHolder(v);
    } else if (viewType == VIEW_TYPE_GRID_ITEM) {
      View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.share_option, parent, false);
      return  new OptionViewHolder(v);
    }

    return null;

  }

  // Replace the contents of a view (invoked by the layout manager)
  @Override public void onBindViewHolder(BaseViewHolder holder, final int position) {
    if (holder.getItemViewType() == VIEW_TYPE_RECENTS_SLIDER)
      return;

    int arrayPosition = position - 1;

    final ShareSelectorOption shareSelection = shareSelectorOptions[arrayPosition];
    OptionViewHolder optionViewHolder = (OptionViewHolder) holder;

    optionViewHolder.imageView.setImageDrawable(context.getResources().getDrawable(shareSelection.getDrawableRes()));

    if (roundIconBackground) {
      optionViewHolder.imageView.setBackground(context.getResources().getDrawable(R.drawable.circle_background));
      GradientDrawable bgShape = (GradientDrawable) optionViewHolder.imageView.getBackground();
      bgShape.setColor(ContextCompat.getColor(context, shareSelection.getBackgroundColorRes()));
    } else {
      optionViewHolder.imageView.setBackgroundColor(ContextCompat.getColor(context, shareSelection.getBackgroundColorRes()));
    }

    optionViewHolder.imageView.setOnClickListener(new View.OnClickListener() {
      @Override public void onClick(View v) {
        if (listener != null)
          listener.onOptionSelected(shareSelection.getCallbackId());
      }
    });
  }


  @Override public int getItemViewType(int position) {
    if (position == 0) {
      return VIEW_TYPE_RECENTS_SLIDER;
    }

    return VIEW_TYPE_GRID_ITEM;
  }

  @Override public int getItemCount() {

    int optionsAvailable = 0;

    if (shareSelectorOptions != null)
      optionsAvailable = shareSelectorOptions.length;

    return 1 + optionsAvailable;
  }

  public static class BaseViewHolder extends RecyclerView.ViewHolder {
    public BaseViewHolder(View v) {
      super(v);
    }
  }

  public class NestedRecyclerViewHolder extends BaseViewHolder {
    RecyclerView nestedRecyclerView;

    public NestedRecyclerViewHolder(View v) {
      super(v);
      nestedRecyclerView = (RecyclerView) v.findViewById(R.id.nested_thumbnail_recycler);
      nestedRecyclerView.setHasFixedSize(true);
      nestedRecyclerView.setNestedScrollingEnabled(false);

      nestedRecyclerView.addItemDecoration(new RecyclerView.ItemDecoration() {
        @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
            RecyclerView.State state) {
          super.getItemOffsets(outRect, view, parent, state);
          int sideMargin = context.getResources().getDimensionPixelSize(R.dimen.slidingshareselector_thumbnail_grid_side_margin);
          int topBottomMargin = context.getResources().getDimensionPixelSize(R.dimen.slidingshareselector_thumbnail_grid_top_bottom_margin);
          outRect.top = topBottomMargin;
          outRect.bottom = topBottomMargin;
          outRect.left = sideMargin;
          outRect.right = sideMargin;
        }
      });

      if (recentsLayoutManager != null)
        nestedRecyclerView.setLayoutManager(recentsLayoutManager);

      if (recentsRecyclerAdapter != null)
        nestedRecyclerView.setAdapter(recentsRecyclerAdapter);
    }
  }

  public class OptionViewHolder extends BaseViewHolder {
    ForegroundImageView imageView;
    public OptionViewHolder(View v) {
      super(v);
      imageView = (ForegroundImageView) v.findViewById(R.id.icon);
    }
  }

  public void setRecentsCursor(Cursor cursor) {
    if (recentsRecyclerAdapter != null)
      recentsRecyclerAdapter.swapCursor(cursor);
  }

  public interface OptionSelectedListener {
    void onOptionSelected(int optionId);
  }
}
