Home | History | Annotate | Download | only in documentsui
      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.documentsui;
     18 
     19 import static com.android.documentsui.DocumentsActivity.TAG;
     20 
     21 import android.app.Fragment;
     22 import android.app.FragmentManager;
     23 import android.app.FragmentTransaction;
     24 import android.app.LoaderManager.LoaderCallbacks;
     25 import android.content.ContentResolver;
     26 import android.content.Context;
     27 import android.content.Loader;
     28 import android.database.Cursor;
     29 import android.graphics.drawable.Drawable;
     30 import android.net.Uri;
     31 import android.os.Bundle;
     32 import android.os.CancellationSignal;
     33 import android.text.Spannable;
     34 import android.text.SpannableStringBuilder;
     35 import android.text.TextUtils.TruncateAt;
     36 import android.text.style.ImageSpan;
     37 import android.util.Log;
     38 import android.view.LayoutInflater;
     39 import android.view.View;
     40 import android.view.ViewGroup;
     41 import android.widget.AdapterView;
     42 import android.widget.AdapterView.OnItemClickListener;
     43 import android.widget.BaseAdapter;
     44 import android.widget.ImageView;
     45 import android.widget.ListView;
     46 import android.widget.TextView;
     47 
     48 import com.android.documentsui.DocumentsActivity.State;
     49 import com.android.documentsui.RecentsProvider.RecentColumns;
     50 import com.android.documentsui.model.DocumentStack;
     51 import com.android.documentsui.model.DurableUtils;
     52 import com.android.documentsui.model.RootInfo;
     53 import com.google.android.collect.Lists;
     54 
     55 import libcore.io.IoUtils;
     56 
     57 import java.io.ByteArrayInputStream;
     58 import java.io.DataInputStream;
     59 import java.io.IOException;
     60 import java.util.ArrayList;
     61 import java.util.Collection;
     62 import java.util.List;
     63 
     64 /**
     65  * Display directories where recent creates took place.
     66  */
     67 public class RecentsCreateFragment extends Fragment {
     68 
     69     private View mEmptyView;
     70     private ListView mListView;
     71 
     72     private DocumentStackAdapter mAdapter;
     73     private LoaderCallbacks<List<DocumentStack>> mCallbacks;
     74 
     75     private static final int LOADER_RECENTS = 3;
     76 
     77     public static void show(FragmentManager fm) {
     78         final RecentsCreateFragment fragment = new RecentsCreateFragment();
     79         final FragmentTransaction ft = fm.beginTransaction();
     80         ft.replace(R.id.container_directory, fragment);
     81         ft.commitAllowingStateLoss();
     82     }
     83 
     84     @Override
     85     public View onCreateView(
     86             LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
     87         final Context context = inflater.getContext();
     88 
     89         final View view = inflater.inflate(R.layout.fragment_directory, container, false);
     90 
     91         mEmptyView = view.findViewById(android.R.id.empty);
     92 
     93         mListView = (ListView) view.findViewById(R.id.list);
     94         mListView.setOnItemClickListener(mItemListener);
     95 
     96         mAdapter = new DocumentStackAdapter();
     97         mListView.setAdapter(mAdapter);
     98 
     99         final RootsCache roots = DocumentsApplication.getRootsCache(context);
    100         final State state = ((DocumentsActivity) getActivity()).getDisplayState();
    101 
    102         mCallbacks = new LoaderCallbacks<List<DocumentStack>>() {
    103             @Override
    104             public Loader<List<DocumentStack>> onCreateLoader(int id, Bundle args) {
    105                 return new RecentsCreateLoader(context, roots, state);
    106             }
    107 
    108             @Override
    109             public void onLoadFinished(
    110                     Loader<List<DocumentStack>> loader, List<DocumentStack> data) {
    111                 mAdapter.swapStacks(data);
    112 
    113                 // When launched into empty recents, show drawer
    114                 if (mAdapter.isEmpty() && !state.stackTouched) {
    115                     ((DocumentsActivity) context).setRootsDrawerOpen(true);
    116                 }
    117             }
    118 
    119             @Override
    120             public void onLoaderReset(Loader<List<DocumentStack>> loader) {
    121                 mAdapter.swapStacks(null);
    122             }
    123         };
    124 
    125         return view;
    126     }
    127 
    128     @Override
    129     public void onStart() {
    130         super.onStart();
    131         getLoaderManager().restartLoader(LOADER_RECENTS, getArguments(), mCallbacks);
    132     }
    133 
    134     @Override
    135     public void onStop() {
    136         super.onStop();
    137         getLoaderManager().destroyLoader(LOADER_RECENTS);
    138     }
    139 
    140     private OnItemClickListener mItemListener = new OnItemClickListener() {
    141         @Override
    142         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    143             final DocumentStack stack = mAdapter.getItem(position);
    144             ((DocumentsActivity) getActivity()).onStackPicked(stack);
    145         }
    146     };
    147 
    148     public static class RecentsCreateLoader extends UriDerivativeLoader<Uri, List<DocumentStack>> {
    149         private final RootsCache mRoots;
    150         private final State mState;
    151 
    152         public RecentsCreateLoader(Context context, RootsCache roots, State state) {
    153             super(context, RecentsProvider.buildRecent());
    154             mRoots = roots;
    155             mState = state;
    156         }
    157 
    158         @Override
    159         public List<DocumentStack> loadInBackground(Uri uri, CancellationSignal signal) {
    160             final Collection<RootInfo> matchingRoots = mRoots.getMatchingRootsBlocking(mState);
    161             final ArrayList<DocumentStack> result = Lists.newArrayList();
    162 
    163             final ContentResolver resolver = getContext().getContentResolver();
    164             final Cursor cursor = resolver.query(
    165                     uri, null, null, null, RecentColumns.TIMESTAMP + " DESC", signal);
    166             try {
    167                 while (cursor != null && cursor.moveToNext()) {
    168                     final byte[] rawStack = cursor.getBlob(
    169                             cursor.getColumnIndex(RecentColumns.STACK));
    170                     try {
    171                         final DocumentStack stack = new DocumentStack();
    172                         DurableUtils.readFromArray(rawStack, stack);
    173 
    174                         // Only update root here to avoid spinning up all
    175                         // providers; we update the stack during the actual
    176                         // restore. This also filters away roots that don't
    177                         // match current filter.
    178                         stack.updateRoot(matchingRoots);
    179                         result.add(stack);
    180                     } catch (IOException e) {
    181                         Log.w(TAG, "Failed to resolve stack: " + e);
    182                     }
    183                 }
    184             } finally {
    185                 IoUtils.closeQuietly(cursor);
    186             }
    187 
    188             return result;
    189         }
    190     }
    191 
    192     private class DocumentStackAdapter extends BaseAdapter {
    193         private List<DocumentStack> mStacks;
    194 
    195         public DocumentStackAdapter() {
    196         }
    197 
    198         public void swapStacks(List<DocumentStack> stacks) {
    199             mStacks = stacks;
    200 
    201             if (isEmpty()) {
    202                 mEmptyView.setVisibility(View.VISIBLE);
    203             } else {
    204                 mEmptyView.setVisibility(View.GONE);
    205             }
    206 
    207             notifyDataSetChanged();
    208         }
    209 
    210         @Override
    211         public View getView(int position, View convertView, ViewGroup parent) {
    212             final Context context = parent.getContext();
    213 
    214             if (convertView == null) {
    215                 final LayoutInflater inflater = LayoutInflater.from(context);
    216                 convertView = inflater.inflate(R.layout.item_doc_list, parent, false);
    217             }
    218 
    219             final ImageView iconMime = (ImageView) convertView.findViewById(R.id.icon_mime);
    220             final TextView title = (TextView) convertView.findViewById(android.R.id.title);
    221             final View line2 = convertView.findViewById(R.id.line2);
    222 
    223             final DocumentStack stack = getItem(position);
    224             iconMime.setImageDrawable(stack.root.loadIcon(context));
    225 
    226             final Drawable crumb = context.getResources()
    227                     .getDrawable(R.drawable.ic_breadcrumb_arrow);
    228             crumb.setBounds(0, 0, crumb.getIntrinsicWidth(), crumb.getIntrinsicHeight());
    229 
    230             final SpannableStringBuilder builder = new SpannableStringBuilder();
    231             builder.append(stack.root.title);
    232             for (int i = stack.size() - 2; i >= 0; i--) {
    233                 appendDrawable(builder, crumb);
    234                 builder.append(stack.get(i).displayName);
    235             }
    236             title.setText(builder);
    237             title.setEllipsize(TruncateAt.MIDDLE);
    238 
    239             if (line2 != null) line2.setVisibility(View.GONE);
    240 
    241             return convertView;
    242         }
    243 
    244         @Override
    245         public int getCount() {
    246             return mStacks != null ? mStacks.size() : 0;
    247         }
    248 
    249         @Override
    250         public DocumentStack getItem(int position) {
    251             return mStacks.get(position);
    252         }
    253 
    254         @Override
    255         public long getItemId(int position) {
    256             return getItem(position).hashCode();
    257         }
    258     }
    259 
    260     private static void appendDrawable(SpannableStringBuilder b, Drawable d) {
    261         final int length = b.length();
    262         b.append("\u232a");
    263         b.setSpan(new ImageSpan(d), length, b.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    264     }
    265 }
    266