com.nttec.everychan.ui.BoardsListFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.nttec.everychan.ui.BoardsListFragment.java

Source

/*
 * Everychan Android (Meta Imageboard Client)
 * Copyright (C) 2014-2016  miku-nyan <https://github.com/miku-nyan>
 *     
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.nttec.everychan.ui;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import com.nttec.everychan.R;
import com.nttec.everychan.api.ChanModule;
import com.nttec.everychan.api.interfaces.CancellableTask;
import com.nttec.everychan.api.models.SimpleBoardModel;
import com.nttec.everychan.api.models.UrlPageModel;
import com.nttec.everychan.cache.PagesCache;
import com.nttec.everychan.cache.SerializableBoardsList;
import com.nttec.everychan.common.Async;
import com.nttec.everychan.common.Logger;
import com.nttec.everychan.common.MainApplication;
import com.nttec.everychan.http.interactive.InteractiveException;
import com.nttec.everychan.lib.ClickableToast;
import com.nttec.everychan.ui.settings.ApplicationSettings;
import com.nttec.everychan.ui.tabs.TabModel;
import com.nttec.everychan.ui.tabs.TabsState;
import com.nttec.everychan.ui.tabs.UrlHandler;
import com.nttec.everychan.ui.theme.ThemeUtils;
import android.content.Context;
import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.ContextMenu;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class BoardsListFragment extends Fragment implements AdapterView.OnItemClickListener, View.OnClickListener {
    public static final String TAG = "BoardsListFragment";

    private boolean isFailInstance = false;

    private PagesCache pagesCache = MainApplication.getInstance().pagesCache;
    private ChanModule chan;

    private MainActivity activity;
    private Resources resources;
    private ApplicationSettings settings;
    private Database database;
    private CancellableTask currentTask;

    private TabModel tabModel;
    private String startItem;
    private int startItemTop;

    private View rootView;
    private View loadingView;
    private View errorView;
    private TextView errorTextView;
    private ListView listView;
    private EditText boardField;
    private Button buttonGo;

    private SimpleBoardModel[] boardsList;
    private BoardsListAdapter adapter;

    private List<QuickAccess.Entry> quickAccessList;
    private ClickableToast toast;

    public static BoardsListFragment newInstance(long tabId) {
        TabsState tabsState = MainApplication.getInstance().tabsState;
        if (tabsState == null)
            throw new IllegalStateException("tabsState was not initialized in the MainApplication singleton");
        TabModel model = tabsState.findTabById(tabId);
        if (model == null)
            throw new IllegalArgumentException("cannot find tab with id " + tabId);

        if (model.pageModel.type != UrlPageModel.TYPE_INDEXPAGE) {
            throw new IllegalArgumentException(
                    "pageModel.type != INDEXPAGE (this fragment can show only boardslists)");
        }
        BoardsListFragment fragment = new BoardsListFragment();
        Bundle args = new Bundle(1);
        args.putLong("TabModelId", tabId);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        activity = (MainActivity) getActivity();
        resources = MainApplication.getInstance().resources;
        settings = MainApplication.getInstance().settings;
        database = MainApplication.getInstance().database;

        TabsState tabsState = MainApplication.getInstance().tabsState;
        if (tabsState == null)
            throw new IllegalStateException("tabsState was not initialized in the MainApplication singleton");
        tabModel = tabsState.findTabById(getArguments().getLong("TabModelId"));
        if (tabModel == null) {
            isFailInstance = true;
            return;
        }

        if (tabModel.forceUpdate) {
            tabModel.forceUpdate = false;
            MainApplication.getInstance().serializer.serializeTabsState(tabsState);
            saveHistory();
        }

        chan = MainApplication.getInstance().getChanModule(tabModel.pageModel.chanName);
        setHasOptionsMenu(true);

        quickAccessList = QuickAccess.getQuickAccessFromPreferences();
        if (quickAccessList.isEmpty()) {
            toast = ClickableToast.showText(activity, resources.getString(R.string.boardslist_quickaccess_tip),
                    new ClickableToast.OnClickListener() {
                        @Override
                        public void onClick() {
                            try {
                                activity.openOptionsMenu();
                            } catch (Exception e) {
                                Logger.e(TAG, e);
                            }
                        }
                    }, Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB);
        }
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        MenuItem itemUpdate = menu.add(Menu.NONE, R.id.menu_update, 101, resources.getString(R.string.menu_update));
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            itemUpdate.setIcon(ThemeUtils.getActionbarIcon(activity.getTheme(), resources, R.attr.actionRefresh));
            CompatibilityImpl.setShowAsActionIfRoom(itemUpdate);
        } else {
            itemUpdate.setIcon(R.drawable.ic_menu_refresh);
        }

        if (quickAccessList == null)
            return;
        for (QuickAccess.Entry entry : quickAccessList)
            if (entry.boardName == null && entry.chan != null
                    && entry.chan.getChanName().equals(chan.getChanName()))
                return;
        menu.add(Menu.NONE, R.id.menu_quickaccess_add, 102, resources.getString(R.string.menu_quickaccess_add))
                .setIcon(R.drawable.ic_menu_add_bookmark);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        switch (id) {
        case R.id.menu_update:
            update(true);
            return true;
        case R.id.menu_quickaccess_add:
            QuickAccess.Entry newEntry = new QuickAccess.Entry();
            newEntry.chan = chan;
            quickAccessList.add(0, newEntry);
            QuickAccess.saveQuickAccessToPreferences(quickAccessList);
            item.setVisible(false);
            return true;
        }
        return super.onContextItemSelected(item);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        if (isFailInstance) {
            Toast.makeText(activity, R.string.error_unknown, Toast.LENGTH_LONG).show();
            return new View(activity);
        }
        startItem = tabModel.startItemNumber;
        startItemTop = tabModel.startItemTop;
        rootView = inflater.inflate(R.layout.boardslist_fragment, container, false);
        loadingView = rootView.findViewById(R.id.boardslist_loading);
        errorView = rootView.findViewById(R.id.boardslist_error);
        errorTextView = (TextView) errorView.findViewById(R.id.frame_error_text);
        listView = (ListView) rootView.findViewById(android.R.id.list);
        listView.setOnItemClickListener(this);
        registerForContextMenu(listView);
        boardField = (EditText) rootView.findViewById(R.id.boardslist_board_field);
        boardField.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
                    onClick(v);
                    return true;
                }
                return false;
            }
        });
        buttonGo = (Button) rootView.findViewById(R.id.boardslist_btn_go);
        buttonGo.setOnClickListener(this);
        activity.setTitle(chan.getDisplayingName());
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
            CompatibilityImpl.setActionBarCustomFavicon(activity, chan.getChanFavicon());
        update(false);
        return rootView;
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        if (adapter != null) {
            hideKeyboard(listView);
            SimpleBoardModel boardModel = adapter.getItem(position).model;
            if (boardModel != null) {
                UrlPageModel model = getUrlModel(boardModel.boardName);
                String url = chan.buildUrl(model);
                UrlHandler.open(url, activity);
            }
        }

    }

    @Override
    public void onClick(View v) {
        String boardName = boardField.getText().toString();
        if (boardName.length() == 0)
            return;
        hideKeyboard(v);
        UrlPageModel model = getUrlModel(boardName);
        try {
            String url = chan.buildUrl(model);
            UrlHandler.open(url, activity);
        } catch (Exception e) {
            Logger.e(TAG, e);
        }
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        if (adapter == null)
            return;
        try {
            if (v.getId() == android.R.id.list) {
                AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
                SimpleBoardModel boardModel = adapter.getItem(info.position).model;
                if (boardModel != null) {
                    UrlPageModel model = getUrlModel(boardModel.boardName);
                    model = UrlHandler.getPageModel(chan.buildUrl(model));
                    if (model != null) {
                        boolean isFavorite = database.isFavorite(model.chanName, model.boardName,
                                Integer.toString(model.boardPage), null);
                        menu.add(Menu.NONE, R.id.context_menu_favorites_from_fragment, 1,
                                isFavorite ? R.string.context_menu_remove_favorites
                                        : R.string.context_menu_add_favorites);

                        for (QuickAccess.Entry entry : quickAccessList)
                            if (entry.boardName != null && entry.chan != null)
                                if (entry.chan.getChanName().equals(model.chanName)
                                        && entry.boardName.equals(model.boardName))
                                    return;
                        menu.add(Menu.NONE, R.id.context_menu_quickaccess_add, 2,
                                R.string.context_menu_quickaccess_add);
                    }
                }
            }
        } catch (Exception e) {
            Logger.e(TAG, e);
        }
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
        try {
            if (item.getItemId() == R.id.context_menu_favorites_from_fragment) {
                String url = chan.buildUrl(getUrlModel(adapter.getItem(menuInfo.position).model.boardName));
                UrlPageModel model = UrlHandler.getPageModel(url);
                if (model != null) {
                    if (database.isFavorite(model.chanName, model.boardName, Integer.toString(model.boardPage),
                            null)) {
                        database.removeFavorite(model.chanName, model.boardName, Integer.toString(model.boardPage),
                                null);
                    } else {
                        database.addFavorite(model.chanName, model.boardName, Integer.toString(model.boardPage),
                                null, getString(R.string.tabs_title_boardpage_first, model.boardName), url);
                    }
                    updateListSavePosition();
                    return true;
                }
            } else if (item.getItemId() == R.id.context_menu_quickaccess_add) {
                QuickAccess.Entry newEntry = new QuickAccess.Entry();
                newEntry.chan = chan;
                SimpleBoardModel simleBoardModel = adapter.getItem(menuInfo.position).model;
                newEntry.boardName = simleBoardModel.boardName;
                newEntry.boardDescription = simleBoardModel.boardDescription;
                quickAccessList.add(0, newEntry);
                QuickAccess.saveQuickAccessToPreferences(quickAccessList);
                return true;
            }
        } catch (Exception e) {
            Logger.e(TAG, e);
        }
        return false;
    }

    private UrlPageModel getUrlModel(String boardName) {
        UrlPageModel model = new UrlPageModel();
        model.chanName = chan.getChanName();
        model.type = UrlPageModel.TYPE_BOARDPAGE;
        model.boardName = boardName;
        model.boardPage = UrlPageModel.DEFAULT_FIRST_PAGE;
        return model;
    }

    private void hideKeyboard(View v) {
        try {
            InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
        } catch (Exception e) {
            Logger.e(TAG, e);
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        saveCurrentPostPosition();
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        saveCurrentPostPosition();
        if (toast != null)
            toast.hide();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (listView != null) {
            listView.setOnLongClickListener(null);
            listView.setAdapter(null);
        }
    }

    private void saveCurrentPostPosition() {
        if (tabModel != null && listView != null && listView.getChildCount() > 0 && adapter != null) {
            View v = listView.getChildAt(0);
            int position = listView.getPositionForView(v);
            BoardsListEntry model = adapter.getItem(position);
            tabModel.startItemNumber = model.isSeparator ? model.category : model.model.boardName;
            tabModel.startItemTop = v == null ? 0 : v.getTop();
            MainApplication.getInstance().serializer.serializeTabsState(MainApplication.getInstance().tabsState);
        }
    }

    private void switchToLoadingView() {
        loadingView.setVisibility(View.VISIBLE);
        errorView.setVisibility(View.GONE);
        listView.setVisibility(View.GONE);
    }

    private String fixErrorMessage(String message) {
        if (message == null || message.length() == 0) {
            return resources.getString(R.string.error_unknown);
        }
        return message;
    }

    private void switchToErrorView(String message) {
        loadingView.setVisibility(View.GONE);
        errorView.setVisibility(View.VISIBLE);
        listView.setVisibility(View.GONE);
        errorTextView.setText(fixErrorMessage(message));
    }

    private void switchToListView() {
        loadingView.setVisibility(View.GONE);
        errorView.setVisibility(View.GONE);
        listView.setVisibility(View.VISIBLE);
    }

    private void showToastError(String message) {
        Toast.makeText(activity, fixErrorMessage(message), Toast.LENGTH_LONG).show();
    }

    private void saveHistory() {
        MainApplication.getInstance().database.addHistory(tabModel.pageModel.chanName, null, null, null,
                tabModel.title, tabModel.webUrl);
    }

    /**
     *  ?? ( ? ?  ? NSFW ?)
     */
    public void updateList() {
        update(false);
    }

    private void updateListSavePosition() {
        try {
            View v = listView.getChildAt(0);
            int position = listView.getPositionForView(v);
            BoardsListEntry model = adapter.getItem(position);
            startItem = model.isSeparator ? model.category : model.model.boardName;
            startItemTop = v == null ? 0 : v.getTop();
        } catch (Exception e) {
            Logger.e(TAG, e);
        }
        updateList();
    }

    private void update(boolean forceUpdate) {
        listView.setAdapter(null);
        adapter = null;
        switchToLoadingView();
        if (currentTask != null) {
            currentTask.cancel();
        }
        BoardsListGetter boardsListGetter = new BoardsListGetter(forceUpdate);
        currentTask = boardsListGetter;
        Async.runAsync(boardsListGetter);
    }

    private class BoardsListGetter extends CancellableTask.BaseCancellableTask implements Runnable {
        private final boolean forceUpdate;

        public BoardsListGetter(boolean forceUpdate) {
            this.forceUpdate = forceUpdate;
        }

        @Override
        public void run() {
            if (forceUpdate)
                saveHistory();
            SerializableBoardsList fromCache = pagesCache.getSerializableBoardsList(tabModel.hash);
            if (fromCache == null || fromCache.chanName == null || !fromCache.chanName.equals(chan.getChanName()))
                fromCache = null;
            if (fromCache != null)
                boardsList = fromCache.boards;
            if (fromCache == null || forceUpdate) {
                try {
                    SimpleBoardModel[] fromChan = chan.getBoardsList(null, this, boardsList);
                    SerializableBoardsList serializableBoardsList = new SerializableBoardsList();
                    serializableBoardsList.boards = fromChan;
                    serializableBoardsList.chanName = chan.getChanName();
                    pagesCache.putSerializableBoardsList(tabModel.hash, serializableBoardsList);
                    boardsList = fromChan;
                } catch (Exception e) {
                    Logger.e(TAG, e);
                    if (isCancelled())
                        return;
                    if (e instanceof InteractiveException) {
                        ((InteractiveException) e).handle(activity, BoardsListGetter.this,
                                new InteractiveException.Callback() {
                                    @Override
                                    public void onSuccess() {
                                        update(true);
                                    }

                                    @Override
                                    public void onError(String message) {
                                        if (boardsList == null) {
                                            switchToErrorView(message);
                                        } else {
                                            showToastError(message);
                                            update(false);
                                        }
                                    }
                                });
                    } else {
                        final String message = e.getMessage();
                        Async.runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                if (boardsList == null) {
                                    switchToErrorView(message);
                                } else {
                                    showToastError(message);
                                }
                            }
                        });
                    }
                }
            }
            if (isCancelled())
                return;
            if (boardsList == null)
                return;

            adapter = new BoardsListAdapter(BoardsListFragment.this);
            int startItemSearch = -1;
            if (startItem != null) {
                for (int i = 0; i < adapter.getCount(); ++i) {
                    String curItem = adapter.getItem(i).isSeparator ? adapter.getItem(i).category
                            : adapter.getItem(i).model.boardName;
                    if (curItem.equals(startItem)) {
                        startItemSearch = i;
                        break;
                    }
                }
                startItem = null;
            }
            final int startItemPosition = startItemSearch;
            Async.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    listView.setAdapter(adapter);
                    listView.setSelectionFromTop(startItemPosition, startItemTop);
                    switchToListView();
                }
            });
        }
    }

    static class BoardsListEntry {
        public final SimpleBoardModel model;
        public final String category;
        public final boolean isSeparator;

        public BoardsListEntry(SimpleBoardModel model) {
            this.model = model;
            this.category = null;
            isSeparator = false;
        }

        public BoardsListEntry(String category) {
            this.model = null;
            this.category = category;
            isSeparator = true;
        }
    }

    static class BoardsListAdapter extends ArrayAdapter<BoardsListEntry> {
        private static final int ITEM_VIEW_TYPE_BOARD = 0;
        private static final int ITEM_VIEW_TYPE_SEPARATOR = 1;

        private LayoutInflater inflater;
        private Resources resources;

        public BoardsListAdapter(BoardsListFragment fragment) {
            super(fragment.activity, 0);
            this.inflater = LayoutInflater.from(fragment.activity);
            this.resources = fragment.resources;
            String lastCategory = "";

            LinkedHashMap<String, String> favBoards = new LinkedHashMap<>();
            for (String board : fragment.database.getFavoriteBoards(fragment.chan))
                favBoards.put(board, "");
            if (!favBoards.isEmpty()) {
                lastCategory = resources.getString(R.string.boardslist_favorite_boards);
                add(new BoardsListEntry(lastCategory));
                for (int i = 0; i < fragment.boardsList.length; ++i)
                    if (favBoards.containsKey(fragment.boardsList[i].boardName))
                        favBoards.put(fragment.boardsList[i].boardName, fragment.boardsList[i].boardDescription);
                for (Map.Entry<String, String> entry : favBoards.entrySet()) {
                    SimpleBoardModel model = new SimpleBoardModel();
                    model.chan = fragment.chan.getChanName();
                    model.boardName = entry.getKey();
                    model.boardDescription = entry.getValue();
                    model.boardCategory = lastCategory;
                    add(new BoardsListEntry(model));
                }
            }
            for (int i = 0; i < fragment.boardsList.length; ++i) {
                if (!fragment.settings.showNSFWBoards() && fragment.boardsList[i].nsfw)
                    continue;
                String curCategory = fragment.boardsList[i].boardCategory != null
                        ? fragment.boardsList[i].boardCategory
                        : "";
                if (!curCategory.equals(lastCategory)) {
                    add(new BoardsListEntry(curCategory));
                    lastCategory = curCategory;
                }
                add(new BoardsListEntry(fragment.boardsList[i]));
            }
        }

        @Override
        public int getViewTypeCount() {
            return 2;
        }

        @Override
        public int getItemViewType(int position) {
            return this.getItem(position).isSeparator ? ITEM_VIEW_TYPE_SEPARATOR : ITEM_VIEW_TYPE_BOARD;
        }

        @Override
        public boolean isEnabled(int position) {
            return !this.getItem(position).isSeparator;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            BoardsListEntry item = this.getItem(position);
            if (convertView == null) {
                convertView = inflater.inflate(
                        item.isSeparator ? R.layout.list_separator : android.R.layout.simple_list_item_1, parent,
                        false);
            }
            if (item.isSeparator) {
                ((TextView) convertView).setText(item.category);
            } else {
                TextView text = (TextView) convertView.findViewById(android.R.id.text1);
                text.setText(resources.getString(R.string.boardslist_format, item.model.boardName,
                        item.model.boardDescription));
            }
            return convertView;
        }
    }

}