| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * 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. |
| */ |
| |
| package com.android.documentsui; |
| |
| import static com.android.documentsui.DocumentsActivity.TAG; |
| |
| import android.app.Fragment; |
| import android.app.FragmentManager; |
| import android.app.FragmentTransaction; |
| import android.app.LoaderManager.LoaderCallbacks; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.Loader; |
| import android.database.Cursor; |
| import android.graphics.drawable.Drawable; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.os.CancellationSignal; |
| import android.text.Spannable; |
| import android.text.SpannableStringBuilder; |
| import android.text.TextUtils.TruncateAt; |
| import android.text.style.ImageSpan; |
| import android.util.Log; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.widget.AdapterView; |
| import android.widget.AdapterView.OnItemClickListener; |
| import android.widget.BaseAdapter; |
| import android.widget.ImageView; |
| import android.widget.ListView; |
| import android.widget.TextView; |
| |
| import com.android.documentsui.BaseActivity.State; |
| import com.android.documentsui.RecentsProvider.RecentColumns; |
| import com.android.documentsui.model.DocumentStack; |
| import com.android.documentsui.model.DurableUtils; |
| import com.android.documentsui.model.RootInfo; |
| import com.google.android.collect.Lists; |
| |
| import libcore.io.IoUtils; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| |
| /** |
| * Display directories where recent creates took place. |
| */ |
| public class RecentsCreateFragment extends Fragment { |
| |
| private View mEmptyView; |
| private ListView mListView; |
| |
| private DocumentStackAdapter mAdapter; |
| private LoaderCallbacks<List<DocumentStack>> mCallbacks; |
| |
| private static final int LOADER_RECENTS = 3; |
| |
| public static void show(FragmentManager fm) { |
| final RecentsCreateFragment fragment = new RecentsCreateFragment(); |
| final FragmentTransaction ft = fm.beginTransaction(); |
| ft.replace(R.id.container_directory, fragment); |
| ft.commitAllowingStateLoss(); |
| } |
| |
| @Override |
| public View onCreateView( |
| LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { |
| final Context context = inflater.getContext(); |
| |
| final View view = inflater.inflate(R.layout.fragment_directory, container, false); |
| |
| mEmptyView = view.findViewById(android.R.id.empty); |
| |
| mListView = (ListView) view.findViewById(R.id.list); |
| mListView.setOnItemClickListener(mItemListener); |
| |
| mAdapter = new DocumentStackAdapter(); |
| mListView.setAdapter(mAdapter); |
| |
| final RootsCache roots = DocumentsApplication.getRootsCache(context); |
| final State state = ((BaseActivity) getActivity()).getDisplayState(); |
| |
| mCallbacks = new LoaderCallbacks<List<DocumentStack>>() { |
| @Override |
| public Loader<List<DocumentStack>> onCreateLoader(int id, Bundle args) { |
| return new RecentsCreateLoader(context, roots, state); |
| } |
| |
| @Override |
| public void onLoadFinished( |
| Loader<List<DocumentStack>> loader, List<DocumentStack> data) { |
| mAdapter.swapStacks(data); |
| |
| // When launched into empty recents, show drawer |
| if (mAdapter.isEmpty() && !state.stackTouched && |
| context instanceof DocumentsActivity) { |
| ((DocumentsActivity) context).setRootsDrawerOpen(true); |
| } |
| } |
| |
| @Override |
| public void onLoaderReset(Loader<List<DocumentStack>> loader) { |
| mAdapter.swapStacks(null); |
| } |
| }; |
| |
| return view; |
| } |
| |
| @Override |
| public void onStart() { |
| super.onStart(); |
| getLoaderManager().restartLoader(LOADER_RECENTS, getArguments(), mCallbacks); |
| } |
| |
| @Override |
| public void onStop() { |
| super.onStop(); |
| getLoaderManager().destroyLoader(LOADER_RECENTS); |
| } |
| |
| private OnItemClickListener mItemListener = new OnItemClickListener() { |
| @Override |
| public void onItemClick(AdapterView<?> parent, View view, int position, long id) { |
| final DocumentStack stack = mAdapter.getItem(position); |
| ((BaseActivity) getActivity()).onStackPicked(stack); |
| } |
| }; |
| |
| public static class RecentsCreateLoader extends UriDerivativeLoader<Uri, List<DocumentStack>> { |
| private final RootsCache mRoots; |
| private final State mState; |
| |
| public RecentsCreateLoader(Context context, RootsCache roots, State state) { |
| super(context, RecentsProvider.buildRecent()); |
| mRoots = roots; |
| mState = state; |
| } |
| |
| @Override |
| public List<DocumentStack> loadInBackground(Uri uri, CancellationSignal signal) { |
| final Collection<RootInfo> matchingRoots = mRoots.getMatchingRootsBlocking(mState); |
| final ArrayList<DocumentStack> result = Lists.newArrayList(); |
| |
| final ContentResolver resolver = getContext().getContentResolver(); |
| final Cursor cursor = resolver.query( |
| uri, null, null, null, RecentColumns.TIMESTAMP + " DESC", signal); |
| try { |
| while (cursor != null && cursor.moveToNext()) { |
| final byte[] rawStack = cursor.getBlob( |
| cursor.getColumnIndex(RecentColumns.STACK)); |
| try { |
| final DocumentStack stack = new DocumentStack(); |
| DurableUtils.readFromArray(rawStack, stack); |
| |
| // Only update root here to avoid spinning up all |
| // providers; we update the stack during the actual |
| // restore. This also filters away roots that don't |
| // match current filter. |
| stack.updateRoot(matchingRoots); |
| result.add(stack); |
| } catch (IOException e) { |
| Log.w(TAG, "Failed to resolve stack: " + e); |
| } |
| } |
| } finally { |
| IoUtils.closeQuietly(cursor); |
| } |
| |
| return result; |
| } |
| } |
| |
| private class DocumentStackAdapter extends BaseAdapter { |
| private List<DocumentStack> mStacks; |
| |
| public DocumentStackAdapter() { |
| } |
| |
| public void swapStacks(List<DocumentStack> stacks) { |
| mStacks = stacks; |
| |
| if (isEmpty()) { |
| mEmptyView.setVisibility(View.VISIBLE); |
| } else { |
| mEmptyView.setVisibility(View.GONE); |
| } |
| |
| notifyDataSetChanged(); |
| } |
| |
| @Override |
| public View getView(int position, View convertView, ViewGroup parent) { |
| final Context context = parent.getContext(); |
| |
| if (convertView == null) { |
| final LayoutInflater inflater = LayoutInflater.from(context); |
| convertView = inflater.inflate(R.layout.item_doc_list, parent, false); |
| } |
| |
| final ImageView iconMime = (ImageView) convertView.findViewById(R.id.icon_mime); |
| final TextView title = (TextView) convertView.findViewById(android.R.id.title); |
| final View line2 = convertView.findViewById(R.id.line2); |
| |
| final DocumentStack stack = getItem(position); |
| iconMime.setImageDrawable(stack.root.loadIcon(context)); |
| |
| final Drawable crumb = context.getDrawable(R.drawable.ic_breadcrumb_arrow); |
| crumb.setBounds(0, 0, crumb.getIntrinsicWidth(), crumb.getIntrinsicHeight()); |
| |
| final SpannableStringBuilder builder = new SpannableStringBuilder(); |
| builder.append(stack.root.title); |
| for (int i = stack.size() - 2; i >= 0; i--) { |
| appendDrawable(builder, crumb); |
| builder.append(stack.get(i).displayName); |
| } |
| title.setText(builder); |
| title.setEllipsize(TruncateAt.MIDDLE); |
| |
| if (line2 != null) line2.setVisibility(View.GONE); |
| |
| return convertView; |
| } |
| |
| @Override |
| public int getCount() { |
| return mStacks != null ? mStacks.size() : 0; |
| } |
| |
| @Override |
| public DocumentStack getItem(int position) { |
| return mStacks.get(position); |
| } |
| |
| @Override |
| public long getItemId(int position) { |
| return getItem(position).hashCode(); |
| } |
| } |
| |
| private static void appendDrawable(SpannableStringBuilder b, Drawable d) { |
| final int length = b.length(); |
| b.append("\u232a"); |
| b.setSpan(new ImageSpan(d), length, b.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); |
| } |
| } |