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

Commit 265d36fc authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "[multi part] Moving Empty/Error/No Results view and Info/Error bar into...

Merge "[multi part] Moving Empty/Error/No Results view and Info/Error bar into DocsHolders." into nyc-andromeda-dev
parents 4e96f47b 7b38f346
Loading
Loading
Loading
Loading
+0 −8
Original line number Diff line number Diff line
@@ -31,14 +31,6 @@
        style="@style/TrimmedHorizontalProgressBar"
        android:visibility="gone"/>

    <FrameLayout
        android:id="@+id/container_message_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:elevation="1dp"
        android:background="@color/material_grey_50"
        android:visibility="gone" />

    <com.android.documentsui.dirlist.DocumentsSwipeRefreshLayout
        android:id="@+id/refresh_layout"
        android:layout_width="match_parent"
+5 −35
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2015 The Android Open Source Project
<!-- Copyright (C) 2016 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.
@@ -25,40 +25,10 @@
    android:paddingTop="@dimen/list_item_padding">

    <LinearLayout
        android:id="@+id/container_info"
        android:id="@+id/message_container"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:orientation="horizontal"
        android:visibility="gone">

        <FrameLayout
            android:layout_height="@dimen/icon_size"
            android:layout_width="@dimen/icon_size">

            <ImageView
                android:contentDescription="@null"
                android:id="@+id/icon_info"
                android:layout_height="match_parent"
                android:layout_width="wrap_content"
                android:scaleType="centerInside"/>

        </FrameLayout>

        <TextView
            android:id="@+id/textview_info"
            android:layout_gravity="center_vertical"
            android:layout_height="wrap_content"
            android:layout_width="match_parent"
            android:selectAllOnFocus="true"/>

    </LinearLayout>

    <LinearLayout
        android:id="@+id/container_error"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:orientation="horizontal"
        android:visibility="gone">
        android:orientation="horizontal">

        <FrameLayout
            android:layout_height="@dimen/icon_size"
@@ -66,7 +36,7 @@

            <ImageView
                android:contentDescription="@null"
                android:id="@+id/icon_error"
                android:id="@+id/message_icon"
                android:layout_height="match_parent"
                android:layout_width="wrap_content"
                android:scaleType="centerInside"/>
@@ -74,7 +44,7 @@
        </FrameLayout>

        <TextView
            android:id="@+id/textview_error"
            android:id="@+id/message_textview"
            android:layout_gravity="center_vertical"
            android:layout_height="wrap_content"
            android:layout_width="match_parent"
+54 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 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.
-->

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/empty"
    android:gravity="center"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@color/directory_background"
    android:focusable="true"
    android:focusableInTouchMode="true"
    android:clickable="true">

    <LinearLayout
        android:id="@+id/content"
        android:gravity="center"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <ImageView
            android:id="@+id/artwork"
            android:adjustViewBounds="true"
            android:layout_height="250dp"
            android:layout_width="fill_parent"
            android:alpha="1"
            android:layout_centerVertical="true"
            android:layout_marginBottom="25dp"
            android:scaleType="fitCenter"
            android:contentDescription="@null"/>

        <TextView
            android:id="@+id/message"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            style="@android:style/TextAppearance.Material.Subhead"/>

    </LinearLayout>
</FrameLayout>
+0 −129
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;

import android.annotation.Nullable;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

/**
 * A message bar displaying some info/error messages and a Dismiss button.
 */
public class MessageBar extends Fragment {
    private View mView;
    private ViewGroup mContainer;

    /**
     * Creates an instance of a MessageBar. Note that the new MessagBar is not visible by default,
     * and has to be shown by calling MessageBar.show.
     */
    public static MessageBar create(FragmentManager fm) {
        final MessageBar fragment = new MessageBar();

        final FragmentTransaction ft = fm.beginTransaction();
        ft.replace(R.id.container_message_bar, fragment);
        ft.commitAllowingStateLoss();

        return fragment;
    }

    /**
     * Sets the info message. Can be null, in which case no info message will be displayed. The
     * message bar layout will be adjusted accordingly.
     */
    public void setInfo(@Nullable String info) {
        View infoContainer = mView.findViewById(R.id.container_info);
        if (info != null) {
            TextView infoText = (TextView) mView.findViewById(R.id.textview_info);
            infoText.setText(info);
            infoContainer.setVisibility(View.VISIBLE);
        } else {
            infoContainer.setVisibility(View.GONE);
        }
    }

    /**
     * Sets the error message. Can be null, in which case no error message will be displayed. The
     * message bar layout will be adjusted accordingly.
     */
    public void setError(@Nullable String error) {
        View errorView = mView.findViewById(R.id.container_error);
        if (error != null) {
            TextView errorText = (TextView) mView.findViewById(R.id.textview_error);
            errorText.setText(error);
            errorView.setVisibility(View.VISIBLE);
        } else {
            errorView.setVisibility(View.GONE);
        }
    }

    @Override
    public View onCreateView(
            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        mView = inflater.inflate(R.layout.fragment_message_bar, container, false);

        ImageView infoIcon = (ImageView) mView.findViewById(R.id.icon_info);
        infoIcon.setImageResource(R.drawable.ic_dialog_info);

        ImageView errorIcon = (ImageView) mView.findViewById(R.id.icon_error);
        errorIcon.setImageResource(R.drawable.ic_dialog_alert);

        Button dismiss = (Button) mView.findViewById(R.id.button_dismiss);
        dismiss.setOnClickListener(
                new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        hide();
                    }
                });

        mContainer = container;

        return mView;
    }

    public void hide() {
        // The container view is used to show/hide the error bar. If a container is not provided,
        // fall back to showing/hiding the error bar View, which also works, but does not provide
        // the same animated transition.
        if (mContainer != null) {
            mContainer.setVisibility(View.GONE);
        } else {
            mView.setVisibility(View.GONE);
        }
    }

    public void show() {
        // The container view is used to show/hide the error bar. If a container is not provided,
        // fall back to showing/hiding the error bar View, which also works, but does not provide
        // the same animated transition.
        if (mContainer != null) {
            mContainer.setVisibility(View.VISIBLE);
        } else {
            mView.setVisibility(View.VISIBLE);
        }
    }
}
+111 −78
Original line number Diff line number Diff line
@@ -16,57 +16,46 @@

package com.android.documentsui.dirlist;

import android.content.Context;
import android.database.Cursor;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView.AdapterDataObserver;
import android.view.ViewGroup;
import android.widget.Space;

import com.android.documentsui.R;
import com.android.documentsui.base.EventListener;
import com.android.documentsui.base.State;
import com.android.documentsui.dirlist.Message.HeaderMessage;
import com.android.documentsui.dirlist.Message.InflateMessage;
import com.android.documentsui.dirlist.Model.Update;

import java.util.List;

/**
 * Adapter wrapper that inserts a sort of line break item between directories and regular files.
 * Only needs to be used in GRID mode...at this time.
 * Adapter wrapper that embellishes the directory list by inserting Holder views inbetween
 * items.
 */
final class SectionBreakDocumentsAdapterWrapper extends DocumentsAdapter {
final class DirectoryAddonsAdapter extends DocumentsAdapter {

    private static final String TAG = "SectionBreakDocumentsAdapterWrapper";
    private static final int ITEM_TYPE_SECTION_BREAK = Integer.MAX_VALUE;
    private static final String TAG = "SectioningDocumentsAdapterWrapper";

    private final Environment mEnv;
    private final DocumentsAdapter mDelegate;
    private final EventListener<Update> mModelUpdateListener;

    private int mBreakPosition = -1;
    // TODO: There should be two header messages (or more here). Defaulting to showing only one for
    // now.
    private final Message mHeaderMessage;
    private final Message mInflateMessage;

    SectionBreakDocumentsAdapterWrapper(Environment environment, DocumentsAdapter delegate) {
    DirectoryAddonsAdapter(Environment environment, DocumentsAdapter delegate) {
        mEnv = environment;
        mDelegate = delegate;
        mHeaderMessage = new HeaderMessage(environment);
        mInflateMessage = new InflateMessage(environment);

        // Relay events published by our delegate to our listeners (presumably RecyclerView)
        // with adjusted positions.
        mDelegate.registerAdapterDataObserver(new EventRelay());

        mModelUpdateListener = new EventListener<Model.Update>() {
            @Override
            public void accept(Update event) {
                // make sure the delegate handles the update before we do.
                // This isn't ideal since the delegate might be listening
                // the updates itself. But this is the safe thing to do
                // since we read model ids from the delegate
                // in our update handler.
                mDelegate.getModelUpdateListener().accept(event);
                if (!event.hasError()) {
                    onModelUpdate(mEnv.getModel());
                }
            }
        };
        mModelUpdateListener = this::onModelUpdate;
    }

    @Override
@@ -81,7 +70,9 @@ final class SectionBreakDocumentsAdapterWrapper extends DocumentsAdapter {
            public int getSpanSize(int position) {
                // Make layout whitespace span the grid. This has the effect of breaking
                // grid rows whenever layout whitespace is encountered.
                if (getItemViewType(position) == ITEM_TYPE_SECTION_BREAK) {
                if (getItemViewType(position) == ITEM_TYPE_SECTION_BREAK
                        || getItemViewType(position) == ITEM_TYPE_HEADER_MESSAGE
                        || getItemViewType(position) == ITEM_TYPE_INFLATED_MESSAGE) {
                    return mEnv.getColumnCount();
                } else {
                    return 1;
@@ -92,53 +83,101 @@ final class SectionBreakDocumentsAdapterWrapper extends DocumentsAdapter {

    @Override
    public DocumentHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == ITEM_TYPE_SECTION_BREAK) {
            return new EmptyDocumentHolder(mEnv.getContext());
        } else {
        switch (viewType) {
            case ITEM_TYPE_SECTION_BREAK:
                return new TransparentDividerDocumentHolder(mEnv.getContext());
            case ITEM_TYPE_HEADER_MESSAGE:
                return new HeaderMessageDocumentHolder(mEnv.getContext(), parent,
                        this::onDismissHeaderMessage);
            case ITEM_TYPE_INFLATED_MESSAGE:
                return new InflateMessageDocumentHolder(mEnv.getContext(), parent);
            default:
                return mDelegate.createViewHolder(parent, viewType);
        }
    }

    private void onDismissHeaderMessage() {
        mHeaderMessage.reset();
        if (mBreakPosition > 0) {
            mBreakPosition--;
        }
        notifyItemRemoved(0);
    }

    @Override
    public void onBindViewHolder(DocumentHolder holder, int p, List<Object> payload) {
        if (holder.getItemViewType() != ITEM_TYPE_SECTION_BREAK) {
        switch (holder.getItemViewType()) {
            case ITEM_TYPE_SECTION_BREAK:
                ((TransparentDividerDocumentHolder) holder).bind(mEnv.getDisplayState());
                break;
            case ITEM_TYPE_HEADER_MESSAGE:
                ((HeaderMessageDocumentHolder) holder).bind(mHeaderMessage);
                break;
            case ITEM_TYPE_INFLATED_MESSAGE:
                ((InflateMessageDocumentHolder) holder).bind(mInflateMessage);
                break;
            default:
                mDelegate.onBindViewHolder(holder, toDelegatePosition(p), payload);
        } else {
            ((EmptyDocumentHolder)holder).bind(mEnv.getDisplayState());
                break;
        }
    }

    @Override
    public void onBindViewHolder(DocumentHolder holder, int p) {
        if (holder.getItemViewType() != ITEM_TYPE_SECTION_BREAK) {
        switch (holder.getItemViewType()) {
            case ITEM_TYPE_SECTION_BREAK:
                ((TransparentDividerDocumentHolder) holder).bind(mEnv.getDisplayState());
                break;
            case ITEM_TYPE_HEADER_MESSAGE:
                ((HeaderMessageDocumentHolder) holder).bind(mHeaderMessage);
                break;
            case ITEM_TYPE_INFLATED_MESSAGE:
                ((InflateMessageDocumentHolder) holder).bind(mInflateMessage);
                break;
            default:
                mDelegate.onBindViewHolder(holder, toDelegatePosition(p));
        } else {
            ((EmptyDocumentHolder)holder).bind(mEnv.getDisplayState());
                break;
        }
    }

    @Override
    public int getItemCount() {
        int addons = mHeaderMessage.shouldShow() ? 1 : 0;
        addons += mInflateMessage.shouldShow() ? 1 : 0;
        return mBreakPosition == -1
                ? mDelegate.getItemCount()
                : mDelegate.getItemCount() + 1;
                ? mDelegate.getItemCount() + addons
                : mDelegate.getItemCount() + addons + 1;
    }

    private void onModelUpdate(Model model) {
    private void onModelUpdate(Update event) {
        // make sure the delegate handles the update before we do.
        // This isn't ideal since the delegate might be listening
        // the updates itself. But this is the safe thing to do
        // since we read model ids from the delegate
        // in our update handler.
        mDelegate.getModelUpdateListener().accept(event);

        mBreakPosition = -1;
        mInflateMessage.update(event);
        mHeaderMessage.update(event);
        // If there's any fatal error (exceptions), then no need to update the rest.
        if (event.hasError()) {
            return;
        }


        // Walk down the list of IDs till we encounter something that's not a directory, and
        // insert a whitespace element - this introduces a visual break in the grid between
        // folders and documents.
        // TODO: This code makes assumptions about the model, namely, that it performs a
        // bucketed sort where directories will always be ordered before other files. CBB.
        List<String> modelIds = mDelegate.getModelIds();
        for (int i = 0; i < modelIds.size(); i++) {
        Model model = mEnv.getModel();
        for (int i = 0; i < model.getModelIds().length; i++) {
            if (!isDirectory(model, i)) {
                // If the break is the first thing in the list, then there are actually no
                // directories. In that case, don't insert a break at all.
                if (i > 0) {
                    mBreakPosition = i;
                    mBreakPosition = i + (mHeaderMessage.shouldShow() ? 1 : 0);
                }
                break;
            }
@@ -147,11 +186,19 @@ final class SectionBreakDocumentsAdapterWrapper extends DocumentsAdapter {

    @Override
    public int getItemViewType(int p) {
        if (p == 0 && mHeaderMessage.shouldShow()) {
            return ITEM_TYPE_HEADER_MESSAGE;
        }

        if (p == mBreakPosition) {
            return ITEM_TYPE_SECTION_BREAK;
        } else {
            return mDelegate.getItemViewType(toDelegatePosition(p));
        }

        if (p == getItemCount() - 1 && mInflateMessage.shouldShow()) {
            return ITEM_TYPE_INFLATED_MESSAGE;
        }

        return mDelegate.getItemViewType(toDelegatePosition(p));
    }

    /**
@@ -162,7 +209,8 @@ final class SectionBreakDocumentsAdapterWrapper extends DocumentsAdapter {
     * @return Position within the delegate
     */
    private int toDelegatePosition(int p) {
        return (mBreakPosition != -1 && p > mBreakPosition) ? p - 1 : p;
        int topOffset = mHeaderMessage.shouldShow() ? 1 : 0;
        return (mBreakPosition != -1 && p > mBreakPosition) ? p - 1 - topOffset : p - topOffset;
    }

    /**
@@ -173,6 +221,9 @@ final class SectionBreakDocumentsAdapterWrapper extends DocumentsAdapter {
     * @return Position within the view
     */
    private int toViewPosition(int p) {
        int topOffset = mHeaderMessage.shouldShow() ? 1 : 0;
        // Offset it first so we can compare break position correctly
        p += topOffset;
        // If position is greater than or equal to the break, increase by one.
        return (mBreakPosition != -1 && p >= mBreakPosition) ? p + 1 : p;
    }
@@ -184,7 +235,19 @@ final class SectionBreakDocumentsAdapterWrapper extends DocumentsAdapter {

    @Override
    public String getModelId(int p) {
        return (p == mBreakPosition) ? null : mDelegate.getModelId(toDelegatePosition(p));
        if (p == mBreakPosition) {
            return null;
        }

        if (p == 0 && mHeaderMessage.shouldShow()) {
            return null;
        }

        if (p == getItemCount() - 1 && mInflateMessage.shouldShow()) {
            return null;
        }

        return mDelegate.getModelId(toDelegatePosition(p));
    }

    @Override
@@ -234,34 +297,4 @@ final class SectionBreakDocumentsAdapterWrapper extends DocumentsAdapter {
            throw new UnsupportedOperationException();
        }
    }

    /**
     * The most elegant transparent blank box that spans N rows ever conceived.
     */
    private static final class EmptyDocumentHolder extends DocumentHolder {
        final int mVisibleHeight;
        private State mState;

        public EmptyDocumentHolder(Context context) {
            super(context, new Space(context));

            mVisibleHeight = context.getResources().getDimensionPixelSize(
                    R.dimen.grid_section_separator_height);
        }

        public void bind(State state) {
            mState = state;
            bind(null, null);
        }

        @Override
        public void bind(Cursor cursor, String modelId) {
            if (mState.derivedMode == State.MODE_GRID) {
                itemView.setMinimumHeight(mVisibleHeight);
            } else {
                itemView.setMinimumHeight(0);
            }
            return;
        }
    }
}
Loading