Home | History | Annotate | Download | only in photos
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.photos;
     18 
     19 import android.app.Activity;
     20 import android.app.Fragment;
     21 import android.os.Bundle;
     22 import android.os.Handler;
     23 import android.util.SparseBooleanArray;
     24 import android.view.LayoutInflater;
     25 import android.view.View;
     26 import android.view.ViewGroup;
     27 import android.view.animation.AnimationUtils;
     28 import android.widget.AdapterView;
     29 import android.widget.GridView;
     30 import android.widget.ListAdapter;
     31 import android.widget.TextView;
     32 
     33 import com.android.gallery3d.R;
     34 
     35 public abstract class MultiSelectGridFragment extends Fragment
     36         implements MultiChoiceManager.Delegate, AdapterView.OnItemClickListener {
     37 
     38     final private Handler mHandler = new Handler();
     39 
     40     final private Runnable mRequestFocus = new Runnable() {
     41         @Override
     42         public void run() {
     43             mGrid.focusableViewAvailable(mGrid);
     44         }
     45     };
     46 
     47     ListAdapter mAdapter;
     48     GridView mGrid;
     49     TextView mEmptyView;
     50     View mProgressContainer;
     51     View mGridContainer;
     52     CharSequence mEmptyText;
     53     boolean mGridShown;
     54     MultiChoiceManager.Provider mHost;
     55 
     56     public MultiSelectGridFragment() {
     57     }
     58 
     59     /**
     60      * Provide default implementation to return a simple grid view. Subclasses
     61      * can override to replace with their own layout. If doing so, the returned
     62      * view hierarchy <em>must</em> have a GridView whose id is
     63      * {@link android.R.id#grid android.R.id.list} and can optionally have a
     64      * sibling text view id {@link android.R.id#empty android.R.id.empty} that
     65      * is to be shown when the grid is empty.
     66      */
     67     @Override
     68     public View onCreateView(LayoutInflater inflater, ViewGroup container,
     69             Bundle savedInstanceState) {
     70         return inflater.inflate(R.layout.multigrid_content, container, false);
     71     }
     72 
     73     @Override
     74     public void onAttach(Activity activity) {
     75         super.onAttach(activity);
     76         mHost = (MultiChoiceManager.Provider) activity;
     77         if (mGrid != null) {
     78             mGrid.setMultiChoiceModeListener(mHost.getMultiChoiceManager());
     79         }
     80     }
     81 
     82     @Override
     83     public void onDetach() {
     84         super.onDetach();
     85         mHost = null;
     86     }
     87 
     88     /**
     89      * Attach to grid view once the view hierarchy has been created.
     90      */
     91     @Override
     92     public void onViewCreated(View view, Bundle savedInstanceState) {
     93         super.onViewCreated(view, savedInstanceState);
     94         ensureGrid();
     95     }
     96 
     97     /**
     98      * Detach from grid view.
     99      */
    100     @Override
    101     public void onDestroyView() {
    102         mHandler.removeCallbacks(mRequestFocus);
    103         mGrid = null;
    104         mGridShown = false;
    105         mEmptyView = null;
    106         mProgressContainer = mGridContainer = null;
    107         super.onDestroyView();
    108     }
    109 
    110     /**
    111      * This method will be called when an item in the grid is selected.
    112      * Subclasses should override. Subclasses can call
    113      * getGridView().getItemAtPosition(position) if they need to access the data
    114      * associated with the selected item.
    115      *
    116      * @param g The GridView where the click happened
    117      * @param v The view that was clicked within the GridView
    118      * @param position The position of the view in the grid
    119      * @param id The id of the item that was clicked
    120      */
    121     public void onGridItemClick(GridView g, View v, int position, long id) {
    122     }
    123 
    124     /**
    125      * Provide the cursor for the grid view.
    126      */
    127     public void setAdapter(ListAdapter adapter) {
    128         boolean hadAdapter = mAdapter != null;
    129         mAdapter = adapter;
    130         if (mGrid != null) {
    131             mGrid.setAdapter(adapter);
    132             if (!mGridShown && !hadAdapter) {
    133                 // The grid was hidden, and previously didn't have an
    134                 // adapter. It is now time to show it.
    135                 setGridShown(true, getView().getWindowToken() != null);
    136             }
    137         }
    138     }
    139 
    140     /**
    141      * Set the currently selected grid item to the specified position with the
    142      * adapter's data
    143      *
    144      * @param position
    145      */
    146     public void setSelection(int position) {
    147         ensureGrid();
    148         mGrid.setSelection(position);
    149     }
    150 
    151     /**
    152      * Get the position of the currently selected grid item.
    153      */
    154     public int getSelectedItemPosition() {
    155         ensureGrid();
    156         return mGrid.getSelectedItemPosition();
    157     }
    158 
    159     /**
    160      * Get the cursor row ID of the currently selected grid item.
    161      */
    162     public long getSelectedItemId() {
    163         ensureGrid();
    164         return mGrid.getSelectedItemId();
    165     }
    166 
    167     /**
    168      * Get the activity's grid view widget.
    169      */
    170     public GridView getGridView() {
    171         ensureGrid();
    172         return mGrid;
    173     }
    174 
    175     /**
    176      * The default content for a MultiSelectGridFragment has a TextView that can
    177      * be shown when the grid is empty. If you would like to have it shown, call
    178      * this method to supply the text it should use.
    179      */
    180     public void setEmptyText(CharSequence text) {
    181         ensureGrid();
    182         if (mEmptyView == null) {
    183             return;
    184         }
    185         mEmptyView.setText(text);
    186         if (mEmptyText == null) {
    187             mGrid.setEmptyView(mEmptyView);
    188         }
    189         mEmptyText = text;
    190     }
    191 
    192     /**
    193      * Control whether the grid is being displayed. You can make it not
    194      * displayed if you are waiting for the initial data to show in it. During
    195      * this time an indeterminate progress indicator will be shown instead.
    196      * <p>
    197      * Applications do not normally need to use this themselves. The default
    198      * behavior of MultiSelectGridFragment is to start with the grid not being
    199      * shown, only showing it once an adapter is given with
    200      * {@link #setAdapter(ListAdapter)}. If the grid at that point had not been
    201      * shown, when it does get shown it will be do without the user ever seeing
    202      * the hidden state.
    203      *
    204      * @param shown If true, the grid view is shown; if false, the progress
    205      *            indicator. The initial value is true.
    206      */
    207     public void setGridShown(boolean shown) {
    208         setGridShown(shown, true);
    209     }
    210 
    211     /**
    212      * Like {@link #setGridShown(boolean)}, but no animation is used when
    213      * transitioning from the previous state.
    214      */
    215     public void setGridShownNoAnimation(boolean shown) {
    216         setGridShown(shown, false);
    217     }
    218 
    219     /**
    220      * Control whether the grid is being displayed. You can make it not
    221      * displayed if you are waiting for the initial data to show in it. During
    222      * this time an indeterminate progress indicator will be shown instead.
    223      *
    224      * @param shown If true, the grid view is shown; if false, the progress
    225      *            indicator. The initial value is true.
    226      * @param animate If true, an animation will be used to transition to the
    227      *            new state.
    228      */
    229     private void setGridShown(boolean shown, boolean animate) {
    230         ensureGrid();
    231         if (mProgressContainer == null) {
    232             throw new IllegalStateException("Can't be used with a custom content view");
    233         }
    234         if (mGridShown == shown) {
    235             return;
    236         }
    237         mGridShown = shown;
    238         if (shown) {
    239             if (animate) {
    240                 mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
    241                         getActivity(), android.R.anim.fade_out));
    242                 mGridContainer.startAnimation(AnimationUtils.loadAnimation(
    243                         getActivity(), android.R.anim.fade_in));
    244             } else {
    245                 mProgressContainer.clearAnimation();
    246                 mGridContainer.clearAnimation();
    247             }
    248             mProgressContainer.setVisibility(View.GONE);
    249             mGridContainer.setVisibility(View.VISIBLE);
    250         } else {
    251             if (animate) {
    252                 mProgressContainer.startAnimation(AnimationUtils.loadAnimation(
    253                         getActivity(), android.R.anim.fade_in));
    254                 mGridContainer.startAnimation(AnimationUtils.loadAnimation(
    255                         getActivity(), android.R.anim.fade_out));
    256             } else {
    257                 mProgressContainer.clearAnimation();
    258                 mGridContainer.clearAnimation();
    259             }
    260             mProgressContainer.setVisibility(View.VISIBLE);
    261             mGridContainer.setVisibility(View.GONE);
    262         }
    263     }
    264 
    265     /**
    266      * Get the ListAdapter associated with this activity's GridView.
    267      */
    268     public ListAdapter getAdapter() {
    269         return mGrid.getAdapter();
    270     }
    271 
    272     private void ensureGrid() {
    273         if (mGrid != null) {
    274             return;
    275         }
    276         View root = getView();
    277         if (root == null) {
    278             throw new IllegalStateException("Content view not yet created");
    279         }
    280         if (root instanceof GridView) {
    281             mGrid = (GridView) root;
    282         } else {
    283             View empty = root.findViewById(android.R.id.empty);
    284             if (empty != null && empty instanceof TextView) {
    285                 mEmptyView = (TextView) empty;
    286             }
    287             mProgressContainer = root.findViewById(R.id.progressContainer);
    288             mGridContainer = root.findViewById(R.id.gridContainer);
    289             View rawGridView = root.findViewById(android.R.id.list);
    290             if (!(rawGridView instanceof GridView)) {
    291                 throw new RuntimeException(
    292                         "Content has view with id attribute 'android.R.id.list' "
    293                                 + "that is not a GridView class");
    294             }
    295             mGrid = (GridView) rawGridView;
    296             if (mGrid == null) {
    297                 throw new RuntimeException(
    298                         "Your content must have a GridView whose id attribute is " +
    299                                 "'android.R.id.list'");
    300             }
    301             if (mEmptyView != null) {
    302                 mGrid.setEmptyView(mEmptyView);
    303             }
    304         }
    305         mGridShown = true;
    306         mGrid.setOnItemClickListener(this);
    307         mGrid.setMultiChoiceModeListener(mHost.getMultiChoiceManager());
    308         if (mAdapter != null) {
    309             ListAdapter adapter = mAdapter;
    310             mAdapter = null;
    311             setAdapter(adapter);
    312         } else {
    313             // We are starting without an adapter, so assume we won't
    314             // have our data right away and start with the progress indicator.
    315             if (mProgressContainer != null) {
    316                 setGridShown(false, false);
    317             }
    318         }
    319         mHandler.post(mRequestFocus);
    320     }
    321 
    322     @Override
    323     public Object getItemAtPosition(int position) {
    324         return getAdapter().getItem(position);
    325     }
    326 
    327     @Override
    328     public Object getPathForItemAtPosition(int position) {
    329         return getPathForItem(getItemAtPosition(position));
    330     }
    331 
    332     @Override
    333     public SparseBooleanArray getSelectedItemPositions() {
    334         return mGrid.getCheckedItemPositions();
    335     }
    336 
    337     @Override
    338     public int getSelectedItemCount() {
    339         return mGrid.getCheckedItemCount();
    340     }
    341 
    342     public abstract Object getPathForItem(Object item);
    343 
    344     @Override
    345     public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
    346         onGridItemClick((GridView) parent, v, position, id);
    347     }
    348 }
    349