mobile/android/base/home/ReadingListPanel.java

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/mobile/android/base/home/ReadingListPanel.java	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,252 @@
     1.4 +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
     1.5 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +package org.mozilla.gecko.home;
    1.10 +
    1.11 +import java.util.EnumSet;
    1.12 +
    1.13 +import org.mozilla.gecko.R;
    1.14 +import org.mozilla.gecko.ReaderModeUtils;
    1.15 +import org.mozilla.gecko.Telemetry;
    1.16 +import org.mozilla.gecko.TelemetryContract;
    1.17 +import org.mozilla.gecko.db.BrowserContract.ReadingListItems;
    1.18 +import org.mozilla.gecko.db.BrowserDB;
    1.19 +import org.mozilla.gecko.db.BrowserDB.URLColumns;
    1.20 +import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
    1.21 +
    1.22 +import android.app.Activity;
    1.23 +import android.content.Context;
    1.24 +import android.content.res.Configuration;
    1.25 +import android.database.Cursor;
    1.26 +import android.os.Bundle;
    1.27 +import android.support.v4.app.LoaderManager.LoaderCallbacks;
    1.28 +import android.support.v4.content.Loader;
    1.29 +import android.support.v4.widget.CursorAdapter;
    1.30 +import android.text.SpannableStringBuilder;
    1.31 +import android.text.Spanned;
    1.32 +import android.text.style.ImageSpan;
    1.33 +import android.view.LayoutInflater;
    1.34 +import android.view.View;
    1.35 +import android.view.ViewGroup;
    1.36 +import android.view.ViewStub;
    1.37 +import android.widget.AdapterView;
    1.38 +import android.widget.TextView;
    1.39 +
    1.40 +/**
    1.41 + * Fragment that displays reading list contents in a ListView.
    1.42 + */
    1.43 +public class ReadingListPanel extends HomeFragment {
    1.44 +
    1.45 +    // Cursor loader ID for reading list
    1.46 +    private static final int LOADER_ID_READING_LIST = 0;
    1.47 +
    1.48 +    // Formatted string in hint text to be replaced with an icon.
    1.49 +    private final String MATCH_STRING = "%I";
    1.50 +
    1.51 +    // Adapter for the list of reading list items
    1.52 +    private ReadingListAdapter mAdapter;
    1.53 +
    1.54 +    // The view shown by the fragment
    1.55 +    private HomeListView mList;
    1.56 +
    1.57 +    // Reference to the View to display when there are no results.
    1.58 +    private View mEmptyView;
    1.59 +
    1.60 +    // Reference to top view.
    1.61 +    private View mTopView;
    1.62 +
    1.63 +    // Callbacks used for the reading list and favicon cursor loaders
    1.64 +    private CursorLoaderCallbacks mCursorLoaderCallbacks;
    1.65 +
    1.66 +    // On URL open listener
    1.67 +    private OnUrlOpenListener mUrlOpenListener;
    1.68 +
    1.69 +    @Override
    1.70 +    public void onAttach(Activity activity) {
    1.71 +        super.onAttach(activity);
    1.72 +
    1.73 +        try {
    1.74 +            mUrlOpenListener = (OnUrlOpenListener) activity;
    1.75 +        } catch (ClassCastException e) {
    1.76 +            throw new ClassCastException(activity.toString()
    1.77 +                    + " must implement HomePager.OnUrlOpenListener");
    1.78 +        }
    1.79 +    }
    1.80 +
    1.81 +    @Override
    1.82 +    public void onDetach() {
    1.83 +        super.onDetach();
    1.84 +
    1.85 +        mUrlOpenListener = null;
    1.86 +    }
    1.87 +
    1.88 +    @Override
    1.89 +    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    1.90 +        return inflater.inflate(R.layout.home_reading_list_panel, container, false);
    1.91 +    }
    1.92 +
    1.93 +    @Override
    1.94 +    public void onViewCreated(View view, Bundle savedInstanceState) {
    1.95 +        super.onViewCreated(view, savedInstanceState);
    1.96 +
    1.97 +        mTopView = view;
    1.98 +
    1.99 +        mList = (HomeListView) view.findViewById(R.id.list);
   1.100 +        mList.setTag(HomePager.LIST_TAG_READING_LIST);
   1.101 +
   1.102 +        mList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
   1.103 +            @Override
   1.104 +            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
   1.105 +                final Cursor c = mAdapter.getCursor();
   1.106 +                if (c == null || !c.moveToPosition(position)) {
   1.107 +                    return;
   1.108 +                }
   1.109 +
   1.110 +                String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL));
   1.111 +                url = ReaderModeUtils.getAboutReaderForUrl(url);
   1.112 +
   1.113 +                Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL);
   1.114 +
   1.115 +                // This item is a TwoLinePageRow, so we allow switch-to-tab.
   1.116 +                mUrlOpenListener.onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
   1.117 +            }
   1.118 +        });
   1.119 +
   1.120 +        mList.setContextMenuInfoFactory(new HomeContextMenuInfo.Factory() {
   1.121 +            @Override
   1.122 +            public HomeContextMenuInfo makeInfoForCursor(View view, int position, long id, Cursor cursor) {
   1.123 +                final HomeContextMenuInfo info = new HomeContextMenuInfo(view, position, id);
   1.124 +                info.url = cursor.getString(cursor.getColumnIndexOrThrow(ReadingListItems.URL));
   1.125 +                info.title = cursor.getString(cursor.getColumnIndexOrThrow(ReadingListItems.TITLE));
   1.126 +                info.readingListItemId = cursor.getInt(cursor.getColumnIndexOrThrow(ReadingListItems._ID));
   1.127 +                return info;
   1.128 +            }
   1.129 +        });
   1.130 +        registerForContextMenu(mList);
   1.131 +    }
   1.132 +
   1.133 +    @Override
   1.134 +    public void onDestroyView() {
   1.135 +        super.onDestroyView();
   1.136 +        mList = null;
   1.137 +        mTopView = null;
   1.138 +        mEmptyView = null;
   1.139 +    }
   1.140 +
   1.141 +    @Override
   1.142 +    public void onConfigurationChanged(Configuration newConfig) {
   1.143 +        super.onConfigurationChanged(newConfig);
   1.144 +
   1.145 +        // Detach and reattach the fragment as the layout changes.
   1.146 +        if (isVisible()) {
   1.147 +            getFragmentManager().beginTransaction()
   1.148 +                                .detach(this)
   1.149 +                                .attach(this)
   1.150 +                                .commitAllowingStateLoss();
   1.151 +        }
   1.152 +    }
   1.153 +
   1.154 +    @Override
   1.155 +    public void onActivityCreated(Bundle savedInstanceState) {
   1.156 +        super.onActivityCreated(savedInstanceState);
   1.157 +
   1.158 +        mAdapter = new ReadingListAdapter(getActivity(), null);
   1.159 +        mList.setAdapter(mAdapter);
   1.160 +
   1.161 +        // Create callbacks before the initial loader is started.
   1.162 +        mCursorLoaderCallbacks = new CursorLoaderCallbacks();
   1.163 +        loadIfVisible();
   1.164 +    }
   1.165 +
   1.166 +    @Override
   1.167 +    protected void load() {
   1.168 +        getLoaderManager().initLoader(LOADER_ID_READING_LIST, null, mCursorLoaderCallbacks);
   1.169 +    }
   1.170 +
   1.171 +    private void updateUiFromCursor(Cursor c) {
   1.172 +        // We delay setting the empty view until the cursor is actually empty.
   1.173 +        // This avoids image flashing.
   1.174 +        if ((c == null || c.getCount() == 0) && mEmptyView == null) {
   1.175 +            final ViewStub emptyViewStub = (ViewStub) mTopView.findViewById(R.id.home_empty_view_stub);
   1.176 +            mEmptyView = emptyViewStub.inflate();
   1.177 +
   1.178 +            final TextView emptyHint = (TextView) mEmptyView.findViewById(R.id.home_empty_hint);
   1.179 +            String readingListHint = emptyHint.getText().toString();
   1.180 +
   1.181 +            // Use an ImageSpan to include the reader icon in the "Tip".
   1.182 +            int imageSpanIndex = readingListHint.indexOf(MATCH_STRING);
   1.183 +            if (imageSpanIndex != -1) {
   1.184 +                final ImageSpan readingListIcon = new ImageSpan(getActivity(), R.drawable.reader_cropped, ImageSpan.ALIGN_BOTTOM);
   1.185 +                final SpannableStringBuilder hintBuilder = new SpannableStringBuilder(readingListHint);
   1.186 +
   1.187 +                // Add additional spacing.
   1.188 +                hintBuilder.insert(imageSpanIndex + MATCH_STRING.length(), " ");
   1.189 +                hintBuilder.insert(imageSpanIndex, " ");
   1.190 +
   1.191 +                // Add icon.
   1.192 +                hintBuilder.setSpan(readingListIcon, imageSpanIndex + 1, imageSpanIndex + MATCH_STRING.length() + 1, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
   1.193 +
   1.194 +                emptyHint.setText(hintBuilder, TextView.BufferType.SPANNABLE);
   1.195 +            }
   1.196 +
   1.197 +            mList.setEmptyView(mEmptyView);
   1.198 +        }
   1.199 +    }
   1.200 +
   1.201 +    /**
   1.202 +     * Cursor loader for the list of reading list items.
   1.203 +     */
   1.204 +    private static class ReadingListLoader extends SimpleCursorLoader {
   1.205 +        public ReadingListLoader(Context context) {
   1.206 +            super(context);
   1.207 +        }
   1.208 +
   1.209 +        @Override
   1.210 +        public Cursor loadCursor() {
   1.211 +            return BrowserDB.getReadingList(getContext().getContentResolver());
   1.212 +        }
   1.213 +    }
   1.214 +
   1.215 +    /**
   1.216 +     * Cursor adapter for the list of reading list items.
   1.217 +     */
   1.218 +    private class ReadingListAdapter extends CursorAdapter {
   1.219 +        public ReadingListAdapter(Context context, Cursor cursor) {
   1.220 +            super(context, cursor, 0);
   1.221 +        }
   1.222 +
   1.223 +        @Override
   1.224 +        public void bindView(View view, Context context, Cursor cursor) {
   1.225 +            final ReadingListRow row = (ReadingListRow) view;
   1.226 +            row.updateFromCursor(cursor);
   1.227 +        }
   1.228 +
   1.229 +        @Override
   1.230 +        public View newView(Context context, Cursor cursor, ViewGroup parent) {
   1.231 +            return LayoutInflater.from(parent.getContext()).inflate(R.layout.reading_list_item_row, parent, false);
   1.232 +        }
   1.233 +    }
   1.234 +
   1.235 +    /**
   1.236 +     * LoaderCallbacks implementation that interacts with the LoaderManager.
   1.237 +     */
   1.238 +    private class CursorLoaderCallbacks implements LoaderCallbacks<Cursor> {
   1.239 +        @Override
   1.240 +        public Loader<Cursor> onCreateLoader(int id, Bundle args) {
   1.241 +            return new ReadingListLoader(getActivity());
   1.242 +        }
   1.243 +
   1.244 +        @Override
   1.245 +        public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
   1.246 +            mAdapter.swapCursor(c);
   1.247 +            updateUiFromCursor(c);
   1.248 +        }
   1.249 +
   1.250 +        @Override
   1.251 +        public void onLoaderReset(Loader<Cursor> loader) {
   1.252 +            mAdapter.swapCursor(null);
   1.253 +        }
   1.254 +    }
   1.255 +}

mercurial