Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8e3fd767 authored by Ben Kwa's avatar Ben Kwa
Browse files

Refactor DocumentHolder.

Primary goals of this refactor are to reduce DirectoryFragment bloat,
and to simplify the code (especially the binding code) for the different
layouts.

- Decouple DocumentHolder from DirectoryFragment.
- Move it into its own file.
- Move binding code from DirectoryFragment into DocumentHolder.
- Split DocumentHolder implementation into three separate subclasses,
  for grid items, list items, and dividers.

BUG=24326989

Change-Id: I217bf4e5b8e1b33173b8b0275591a8c5d8e9161c
parent 6280de03
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
package com.android.documentsui;

import android.content.Context;
import android.text.format.DateUtils;
import android.text.format.Time;

/** @hide */
public final class Shared {
@@ -40,4 +42,26 @@ public final class Shared {
    public static final String getQuantityString(Context context, int resourceId, int quantity) {
        return context.getResources().getQuantityString(resourceId, quantity, quantity);
    }

    public static String formatTime(Context context, long when) {
        // TODO: DateUtils should make this easier
        Time then = new Time();
        then.set(when);
        Time now = new Time();
        now.setToNow();

        int flags = DateUtils.FORMAT_NO_NOON | DateUtils.FORMAT_NO_MIDNIGHT
                | DateUtils.FORMAT_ABBREV_ALL;

        if (then.year != now.year) {
            flags |= DateUtils.FORMAT_SHOW_YEAR | DateUtils.FORMAT_SHOW_DATE;
        } else if (then.yearDay != now.yearDay) {
            flags |= DateUtils.FORMAT_SHOW_DATE;
        } else {
            flags |= DateUtils.FORMAT_SHOW_TIME;
        }

        return DateUtils.formatDateTime(context, when, flags);
    }

}
+37 −395

File changed.

Preview size limit exceeded, changes collapsed.

+131 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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.dirlist;

import static com.android.internal.util.Preconditions.checkState;

import android.content.Context;
import android.database.Cursor;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.android.documentsui.R;
import com.android.documentsui.State;

public abstract class DocumentHolder
        extends RecyclerView.ViewHolder
        implements View.OnKeyListener {

    public @Nullable String modelId;

    final int mSelectedItemColor;
    final int mDefaultItemColor;
    final boolean mAlwaysShowSummary;
    final Context mContext;
    final IconHelper mIconHelper;

    private ListDocumentHolder.ClickListener mClickListener;
    private View.OnKeyListener mKeyListener;

    public DocumentHolder(Context context, ViewGroup parent, int layout, IconHelper iconHelper) {
        this(context, inflateLayout(context, parent, layout), iconHelper);
    }

    public DocumentHolder(Context context, View item, IconHelper iconHelper) {
        super(item);

        itemView.setOnKeyListener(this);

        mContext = context;

        mDefaultItemColor = context.getColor(R.color.item_doc_background);
        mSelectedItemColor = context.getColor(R.color.item_doc_background_selected);
        mAlwaysShowSummary = context.getResources().getBoolean(R.bool.always_show_summary);

        mIconHelper = iconHelper;
    }

    /**
     * Binds the view to the given item data.
     * @param cursor
     * @param modelId
     * @param state
     */
    public abstract void bind(Cursor cursor, String modelId, State state);

    public void setSelected(boolean selected) {
        itemView.setActivated(selected);
        itemView.setBackgroundColor(selected ? mSelectedItemColor : mDefaultItemColor);
    }

    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
        // Intercept enter key-up events, and treat them as clicks.  Forward other events.
        if (event.getAction() == KeyEvent.ACTION_UP &&
                keyCode == KeyEvent.KEYCODE_ENTER) {
            if (mClickListener != null) {
                mClickListener.onClick(this);
            }
            return true;
        } else if (mKeyListener != null) {
            return mKeyListener.onKey(v, keyCode, event);
        }
        return false;
    }

    public void addClickListener(ListDocumentHolder.ClickListener listener) {
        // Just handle one for now; switch to a list if necessary.
        checkState(mClickListener == null);
        mClickListener = listener;
    }

    public void addOnKeyListener(View.OnKeyListener listener) {
        // Just handle one for now; switch to a list if necessary.
        checkState(mKeyListener == null);
        mKeyListener = listener;
    }

    public void setEnabled(boolean enabled) {
        setEnabledRecursive(itemView, enabled);
    }

    static void setEnabledRecursive(View itemView, boolean enabled) {
        if (itemView == null) return;
        if (itemView.isEnabled() == enabled) return;
        itemView.setEnabled(enabled);

        if (itemView instanceof ViewGroup) {
            final ViewGroup vg = (ViewGroup) itemView;
            for (int i = vg.getChildCount() - 1; i >= 0; i--) {
                setEnabledRecursive(vg.getChildAt(i), enabled);
            }
        }
    }

    private static View inflateLayout(Context context, ViewGroup parent, int layout) {
        final LayoutInflater inflater = LayoutInflater.from(context);
        return inflater.inflate(layout, parent, false);
    }

    interface ClickListener {
        public void onClick(DocumentHolder doc);
    }
}
+35 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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.dirlist;

import android.content.Context;
import android.database.Cursor;
import android.view.View;

import com.android.documentsui.State;

final class EmptyDocumentHolder extends DocumentHolder {
    public EmptyDocumentHolder(Context context) {
        super(context, new View(context), null);
        itemView.setVisibility(View.GONE);
    }

    public void bind(Cursor cursor, String modelId, State state) {
        // Nothing to bind.
        return;
    }
}
+112 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 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.dirlist;

import static com.android.documentsui.model.DocumentInfo.getCursorInt;
import static com.android.documentsui.model.DocumentInfo.getCursorLong;
import static com.android.documentsui.model.DocumentInfo.getCursorString;
import static com.android.internal.util.Preconditions.checkNotNull;

import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.text.format.Formatter;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.android.documentsui.R;
import com.android.documentsui.RootCursorWrapper;
import com.android.documentsui.Shared;
import com.android.documentsui.State;

final class GridDocumentHolder extends DocumentHolder {
    private static boolean mHideTitles;

    public GridDocumentHolder(
            Context context, ViewGroup parent, IconHelper thumbnailLoader, int viewType) {
        super(context, parent, R.layout.item_doc_grid, thumbnailLoader);
    }

    /**
     * Bind this view to the given document for display.
     * @param cursor Pointing to the item to be bound.
     * @param modelId The model ID of the item.
     * @param state Current display state.
     */
    public void bind(Cursor cursor, String modelId, State state) {
        this.modelId = modelId;

        checkNotNull(cursor, "Cursor cannot be null.");

        final String docAuthority = getCursorString(cursor, RootCursorWrapper.COLUMN_AUTHORITY);
        final String docId = getCursorString(cursor, Document.COLUMN_DOCUMENT_ID);
        final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
        final String docDisplayName = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME);
        final long docLastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED);
        final int docIcon = getCursorInt(cursor, Document.COLUMN_ICON);
        final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
        final long docSize = getCursorLong(cursor, Document.COLUMN_SIZE);

        final TextView title = (TextView) itemView.findViewById(android.R.id.title);
        final TextView date = (TextView) itemView.findViewById(R.id.date);
        final TextView size = (TextView) itemView.findViewById(R.id.size);
        final ImageView iconMime = (ImageView) itemView.findViewById(R.id.icon_mime);
        final ImageView iconThumb = (ImageView) itemView.findViewById(R.id.icon_thumb);

        mIconHelper.stopLoading(iconThumb);

        iconMime.animate().cancel();
        iconMime.setAlpha(1f);
        iconThumb.animate().cancel();
        iconThumb.setAlpha(0f);

        final Uri uri = DocumentsContract.buildDocumentUri(docAuthority, docId);
        mIconHelper.loadThumbnail(uri, docMimeType, docFlags, docIcon, iconThumb, iconMime);

        if (mHideTitles) {
            title.setVisibility(View.GONE);
        } else {
            title.setText(docDisplayName);
            title.setVisibility(View.VISIBLE);
        }

        if (docLastModified == -1) {
            date.setText(null);
        } else {
            date.setText(Shared.formatTime(mContext, docLastModified));
        }

        if (!state.showSize || Document.MIME_TYPE_DIR.equals(docMimeType) || docSize == -1) {
            size.setVisibility(View.GONE);
        } else {
            size.setVisibility(View.VISIBLE);
            size.setText(Formatter.formatFileSize(mContext, docSize));
        }
    }

    /**
     * Sets whether to hide titles on subsequently created GridDocumentHolder items.
     * @param hideTitles
     */
    public static void setHideTitles(boolean hideTitles) {
        mHideTitles = hideTitles;
    }
}
Loading