de.stkl.gbgvertretungsplan.fragments.MainFragment.java Source code

Java tutorial

Introduction

Here is the source code for de.stkl.gbgvertretungsplan.fragments.MainFragment.java

Source

/*
 * Copyright (c) 2014 Steffen Klee
 * 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 de.stkl.gbgvertretungsplan.fragments;

import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBar.OnNavigationListener;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.util.Pair;
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.Window;
import android.webkit.WebView;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.SpinnerAdapter;
import android.widget.TextView;

import com.inqbarna.tablefixheaders.TableFixHeaders;
import com.inqbarna.tablefixheaders.adapters.BaseTableAdapter;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import de.stkl.gbgvertretungsplan.BuildConfig;
import de.stkl.gbgvertretungsplan.R;
import de.stkl.gbgvertretungsplan.activities.MainActivity;
import de.stkl.gbgvertretungsplan.adapters.NavigationListAdapter;
import de.stkl.gbgvertretungsplan.authenticator.Authenticator;
import de.stkl.gbgvertretungsplan.content.SubstitutionTable;
import de.stkl.gbgvertretungsplan.content.SubstitutionTableStudent;
import de.stkl.gbgvertretungsplan.content.SubstitutionTableTeacher;
import de.stkl.gbgvertretungsplan.errorreporting.ErrorReporter;
import de.stkl.gbgvertretungsplan.sync.Storage;
import de.stkl.gbgvertretungsplan.values.Account;
import de.stkl.gbgvertretungsplan.values.Sync;
import de.stkl.gbgvertretungsplan.views.LayoutMeasureView;

/**
 * Created by Steffen Klee on 22.01.14.
 */
/**
 * Fragment containing the main_activity view ("Vertretungsplan")
 */
public class MainFragment extends PlaceholderFragment {
    private WebView webView = null;
    private Boolean loaded = false;

    private Bundle savedState = null;
    private boolean saveIndicatorObj = false;
    private boolean createStateInDestroyView;
    private static final String SAVED_BUNDLE_TAG = "saved_bundle";
    private static final String SAVED_INDICATOR = "saved_indicator";

    private static final String LOG_TAG = "MainFragment";

    private SpinnerAdapter mSpinnerAdapter = null;
    private ArrayList<String> mSpinnerList;
    private int mSpinnerPos = 0;

    private int lastCId = -1;
    private int lastDay = -1;

    ComInterface mCallback;

    public interface ComInterface {
        Sync.SYNC_STATUS getStatus();

        NavigationDrawerFragment getNavigationDrawerFragment();

        void setTitle(String title);
    }

    final OnNavigationListener mOnNavigationListener = new OnNavigationListener() {
        // Get the same strings provided for the drop-down's ArrayAdapter
        //String[] strings = getResources().getStringArray(R.array.action_spinner_mainview);
        @Override
        public boolean onNavigationItemSelected(int position, long itemId) {
            mSpinnerPos = position;
            if (BuildConfig.DEBUG)
                Log.d(LOG_TAG, "onNavigationItemSelected: " + String.valueOf(position));
            // reload the substitution table view
            updateTable(getView(), CId, position);
            return true;
        }
    };

    /**
     * Returns a new instance of this fragment for the given section
     * number.
     */
    public static MainFragment newInstance(int sectionNumber, NavigationDrawerFragment drawerFragment, int CId) {
        MainFragment fragment = new MainFragment();
        Bundle args = new Bundle();
        args.putInt(ARG_SECTION_NUMBER, sectionNumber);
        args.putInt(ARG_CID, CId);
        fragment.setArguments(args);
        return fragment;
    }

    public MainFragment() {
    }

    /*public MainFragment(NavigationDrawerFragment drawerFragment) {
    mDrawerFragment = drawerFragment;
    }*/

    private Bundle saveState() {
        if (BuildConfig.DEBUG)
            Log.d(LOG_TAG, "saveState");
        Bundle state = new Bundle();
        state.putBoolean(SAVED_INDICATOR, saveIndicatorObj);
        // ***** //
        // save objects
        state.putInt("cid", CId);
        state.putBoolean("loaded", loaded);
        state.putInt("spinner_pos", mSpinnerPos);
        // ***** //
        return state;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        if (!saveIndicatorObj)
            outState.putBundle(SAVED_BUNDLE_TAG, savedState);
        else
            outState.putBundle(SAVED_BUNDLE_TAG, createStateInDestroyView ? savedState : saveState());
        createStateInDestroyView = false;

        super.onSaveInstanceState(outState);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mCallback = (ComInterface) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement ComInterface");
        }
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        updateTable(getView(), CId, mSpinnerPos, true);
    }

    private ActionBar getActionBar() {
        return ((ActionBarActivity) getActivity()).getSupportActionBar();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        CId = getArguments().getInt(ARG_CID, -1);
        if (BuildConfig.DEBUG)
            Log.d(LOG_TAG, "onCreate");
        // restore instance state
        if (savedInstanceState != null) {
            savedState = savedInstanceState.getBundle(SAVED_BUNDLE_TAG);
            //loaded = savedState.getBoolean("loaded", false);
        }

        setHasOptionsMenu(true);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        saveIndicatorObj = true;
        if (BuildConfig.DEBUG)
            Log.d(LOG_TAG, "onCreateView");
        //mSpinnerAdapter = ArrayAdapter.createFromResource(getActivity(), R.array.action_spinner_mainview, android.R.layout.simple_spinner_dropdown_item);

        mSpinnerList = new ArrayList<String>();
        mSpinnerList.add(getString(R.string.action_spinner_mainview_today));
        mSpinnerList.add(getString(R.string.action_spinner_mainview_tomorrow));
        mSpinnerAdapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_spinner_dropdown_item,
                mSpinnerList);

        // restore instance state
        if (savedState != null) {
            loaded = savedState.getBoolean("loaded", false);
            if (CId == -1)
                CId = savedState.getInt("cid", CId);
            if (mSpinnerPos == 0)
                mSpinnerPos = savedState.getInt("spinner_pos", 0);
        }

        View rootView = inflater.inflate(R.layout.fragment_main, container, false);

        getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
        getActionBar().setListNavigationCallbacks(mSpinnerAdapter, mOnNavigationListener);
        getActionBar().setSelectedNavigationItem(mSpinnerPos); // does not trigger callback!

        return rootView;
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        if (BuildConfig.DEBUG)
            Log.d(LOG_TAG, "onCreateOptionsMenu");
        if (((MainActivity) getActivity()).getNavigationDrawerFragment() != null
                && !((MainActivity) getActivity()).getNavigationDrawerFragment().isDrawerOpen()) {
            // Only show items in the action bar relevant to this screen
            // if the drawer is not showing. Otherwise, let the drawer
            // decide what to show in the action bar.
            inflater.inflate(R.menu.fragment_main, menu);
            getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
        }
        super.onCreateOptionsMenu(menu, inflater);

        //inflater.inflate(R.menu.fragment_main, menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        getActivity();
        switch (id) {
        // request a sync
        case R.id.action_refresh:
            actionRefresh();
            break;
        // show help dialogue for colours
        case R.id.action_legend:
            DialogFragment newFragment = new LegendDialogFragment();
            newFragment.show(getActivity().getSupportFragmentManager(), "legend");
            break;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        savedState = saveState();
        createStateInDestroyView = true;
        saveIndicatorObj = false;
    }

    public void actionRefresh() {
        // only if neither pending nor active
        if (!ContentResolver.isSyncActive(((MainActivity) getActivity()).mDefaultAccount,
                getString(Account.CONTENT_AUTHORITY))
                && !ContentResolver.isSyncPending(((MainActivity) getActivity()).mDefaultAccount,
                        getString(Account.CONTENT_AUTHORITY))) {
            Bundle settingsBundle = new Bundle();
            settingsBundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); // this is a manual sync
            settingsBundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); // start immediately
            ContentResolver.requestSync(Authenticator.getDefaultAccount(getActivity()),
                    getString(Account.CONTENT_AUTHORITY), settingsBundle);
        }
    }

    public void updateTable(View rootView, int CId, int day) {
        updateTable(rootView, CId, day, false);
    }

    public void updateTable(View rootView, int CId, final int day, boolean remoteUpdate) {
        if (BuildConfig.DEBUG)
            Log.d(LOG_TAG, "updateTable(..)");
        if (mCallback.getStatus() == Sync.SYNC_STATUS.START)
            return;
        if (CId == -1 && this.CId != -1)
            CId = this.CId;
        setCId(CId);
        if (rootView == null)
            rootView = getView();

        if (day == lastDay && CId == lastCId && !remoteUpdate)
            return;

        TableFixHeaders tableFixHeaders = (TableFixHeaders) rootView.findViewById(R.id.table2);
        if (tableFixHeaders != null)
            tableFixHeaders.setVisibility(View.GONE);

        lastDay = day;
        lastCId = CId;

        boolean error = false;
        SubstitutionTable.Table today = null, tomorrow = null;
        try {
            today = Storage.loadFromDisk2(getActivity(), 0); // today
            tomorrow = Storage.loadFromDisk2(getActivity(), 1); // tomorrow
        } catch (IOException e) {
            if (!(e instanceof FileNotFoundException))
                ErrorReporter.reportError(e, getActivity());
            error = true;
        } catch (ClassNotFoundException e) {
            ErrorReporter.reportError(e, getActivity());
            error = true;
        } finally {
            if (!error) {
                // if new data from server has arrived, update spinner items for today AND tomorrow; it is independent from currently selected day!
                if (remoteUpdate) {
                    SubstitutionTable.GeneralData generalData = null;
                    for (int i = 0; i < 2; i++) {
                        if (i == 0)
                            generalData = today.generalData;
                        else if (i == 1)
                            generalData = tomorrow.generalData;

                        if (!generalData.date.equals("")) {
                            // set spinner item (if generalData['date']) if present; index is the current day, so this is "just" a text change
                            mSpinnerList.set(i, generalData.date);
                        }
                    }
                    if (mSpinnerAdapter instanceof ArrayAdapter)
                        ((ArrayAdapter) mSpinnerAdapter).notifyDataSetChanged();
                }
                // append info to view (only selected day!)
                final View finRootView = rootView;
                final SubstitutionTable.Table finToday = today, finTomorrow = tomorrow;
                if (day == 0)
                    fillTable(finRootView, finToday);
                else if (day == 1)
                    fillTable(finRootView, finTomorrow);
            }
        }
    }

    private class SubstitutionPlanAdapter extends BaseTableAdapter implements TextCalculation {
        private final float density;
        private final Context context;
        private final LayoutInflater inflater;
        private String headers[];
        private List<Pair<List<String>, Integer>> rows;
        private Storage.FilterType filterType;
        private String filterString;
        private final int[] columnWidths;
        private Map<String, String> generalData;

        private SubstitutionTable.Table table;

        public SubstitutionPlanAdapter(Context context, String headers[], List<Pair<List<String>, Integer>> rows,
                int[] columnWidths, Map<String, String> generalData) {
            this.context = context;
            this.headers = headers;
            this.rows = rows;
            this.density = context.getResources().getDisplayMetrics().density;
            this.inflater = getActivity().getLayoutInflater();
            this.columnWidths = columnWidths;
            this.generalData = generalData;
        }

        public SubstitutionPlanAdapter(Context context, SubstitutionTable.Table table, int[] columnWidths) {
            this.context = context;
            this.density = context.getResources().getDisplayMetrics().density;
            this.inflater = getActivity().getLayoutInflater();
            this.columnWidths = columnWidths;
            this.table = table;
        }

        @Override
        public int getRowCount() {
            //            return rows.size();
            return table.getEntryCount();
        }

        @Override
        public int getColumnCount() {
            //            return headers.length;
            return table.getHeaderCount();
        }

        @Override
        public View getView(int row, int col, View convertView, ViewGroup parent) {
            final View view;
            switch (getItemViewType(row, col)) {
            // top-left
            case 0:
                view = getFirstHeader(col, convertView, parent);
                break;
            // header top
            case 1:
                view = getHeader(col, convertView, parent);
                break;
            // first col: dont need a first column as header
            case -1:
                view = new View(context);
                break;
            // the rest
            case 3:
                view = getItem(row, col, convertView, parent);
                break;
            default:
                view = null;
                break;
            }
            return view;
        }

        private View getItem(int row, int col, View convertView, ViewGroup parent) {
            if (convertView == null) {
                convertView = inflater.inflate(R.layout.table_item, parent, false);
                if (convertView == null)
                    return null;
            }

            // assign onClick handler
            final int finalRow = row;
            convertView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Map<String, String> data = new HashMap<String, String>();
                    String date = table.generalData.date;
                    String[] dateArr = date.split(",");
                    if (dateArr.length > 0)
                        date = dateArr[0];
                    data.put("date", date);

                    if (table.generalData.dataType == 0) {
                        SubstitutionTableStudent.Entry entry = (SubstitutionTableStudent.Entry) table
                                .getEntry(finalRow);
                        data.put("className", entry.className);
                        data.put("lesson", "Stunde(n): " + entry.lesson);
                        data.put("oldRoomSubject", entry.oldSubject + " in " + entry.oldRoom);
                        data.put("newRoomSubject", entry.newSubject + " in " + entry.newRoom);
                        data.put("type", entry.type);
                        data.put("substitutionInfo", entry.substitutionInfo);
                    } else if (table.generalData.dataType == 1) {
                        SubstitutionTableTeacher.Entry entry = (SubstitutionTableTeacher.Entry) table
                                .getEntry(finalRow);
                        data.put("teacher", entry.teacher + " -> " + entry.className);
                        data.put("type", entry.type);
                        data.put("lesson", "Stunde(n): " + entry.lesson);
                        data.put("oldTeacherSubjectRoom",
                                entry.oldSubject + "(" + entry.oldTeacher + ")" + " in " + entry.oldRoom);
                        data.put("newTeacherSubjectRoom", entry.newSubject + " in " + entry.newRoom);
                        data.put("type", entry.type);
                        data.put("substitutionInfo", entry.substitutionInfo);
                    }

                    DialogFragment newFragment = new SubstitutionDetailDialog(data, table.generalData.dataType);
                    newFragment.show(getActivity().getSupportFragmentManager(), "subst_detail");
                }
            });

            int padLeft = convertView.getPaddingLeft(), padTop = convertView.getPaddingTop(),
                    padRight = convertView.getPaddingRight(), padBot = convertView.getPaddingBottom();

            convertView.setBackgroundResource(table.getEntry(row).backgroundRID);
            convertView.setPadding(padLeft, padTop, padRight, padBot);

            ((TextView) convertView)
                    .setText(table.getEntry(row).get(col)/*+"("+String.valueOf(row)+"-"+String.valueOf(col)+")"*/);

            return convertView;
        }

        private View getFirstHeader(int col, View convertView, ViewGroup parent) {
            if (convertView == null) {
                convertView = new TextView(context);
            }
            ((TextView) convertView).setText(headers[col + 1]/*+"("+String.valueOf(col)+")"*/);
            return convertView;
        }

        private View getHeader(int col, View convertView, ViewGroup parent) {
            if (convertView == null) {
                convertView = inflater.inflate(R.layout.table_header, parent, false);
            }
            //((TextView)convertView).setText(headers[col]/*+"("+String.valueOf(col)+")"*/);
            ((TextView) convertView).setText(table.getHeader(col));
            return convertView;
        }

        @Override
        public int getWidth(int column) {
            if (column == -1)
                return 0;
            // calculate a columns' width based on its content
            //return calculateTableColWidth(headers, rows, this, column) + Math.round(density*5);
            return columnWidths[column] + Math.round(density * 5);
        }

        @Override
        public int getHeight(int row) {
            return Math.round(30 * density);
        }

        @Override
        public int getItemViewType(int row, int col) {
            // top-left
            if (row == -1 && col == -1)
                return -1;
            // first row: header
            else if (row == -1)
                return 1;
            // first column: don't need as header
            else if (col == -1)
                return -1;
            // rest
            else
                return 3;
        }

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

    private int calculateTableCellWidth(String content, View textContainer) {
        if (!(textContainer instanceof TextView))
            return 0;
        Rect bounds = new Rect();
        Paint textPaint = ((TextView) textContainer).getPaint();
        textPaint.getTextBounds(content, 0, content.length(), bounds);
        //Log.d("calculateTableCellWidth", content+": "+bounds.width());
        //RelativeLayout.MarginLayoutParams params = ((RelativeLayout.MarginLayoutParams)((TextView) textContainer).getLayoutParams());
        int width = /*params.leftMargin + params.rightMargin + */((TextView) textContainer).getTotalPaddingLeft()
                + ((TextView) textContainer).getTotalPaddingRight() + bounds.width();
        return width;
    }

    public interface TextCalculation {
        public View getView(int row, int col, View convertView, ViewGroup parent);
    }

    private int calculateTableColWidth(SubstitutionTable.Table day, TextCalculation tc, int col, ViewGroup parent) {
        if (parent == null)
            parent = (ViewGroup) getView().findViewById(R.id.table2);
        String curContent;
        int width = 0;
        for (int iRow = 0; iRow < day.getEntryCount() + 1; iRow++) {
            if (iRow > 0)
                curContent = day.getEntry(iRow - 1).get(col);
            else {
                if (day.generalData.dataType == 0)
                    curContent = SubstitutionTableStudent.Header.get(col);
                else if (day.generalData.dataType == 1)
                    curContent = SubstitutionTableTeacher.Header.get(col);
                else
                    curContent = "";
            }
            int cWidth;
            if (iRow == 0)
                cWidth = calculateTableCellWidth(curContent, tc.getView(-1, col, null, parent));
            else
                cWidth = calculateTableCellWidth(curContent, tc.getView(iRow - 1, col, null, parent));
            if (cWidth > width)
                width = cWidth;
        }
        return width;
    }

    private int calculateTableWidth(SubstitutionTable.Table day, TextCalculation tc) {
        int width = 0;

        for (int iCol = 0; iCol < day.getHeaderCount(); iCol++)
            width += calculateTableColWidth(day, tc, iCol, null);

        return width;
    }

    private void fillTable(View view, SubstitutionTable.Table day) {
        NavigationDrawerFragment ndf = mCallback.getNavigationDrawerFragment();
        NavigationListAdapter.SubItem o = (NavigationListAdapter.SubItem) ndf.getItemByCId(CId);

        TextView textPlaceholder = (TextView) view.findViewById(R.id.placeholder);

        TableFixHeaders tableFixHeaders = (TableFixHeaders) view.findViewById(R.id.table2);
        TextView updateTime = (TextView) view.findViewById(R.id.updateTime);

        // init values
        SubstitutionTable.Table newDay;

        // filter, if a particular class is selected
        if (o != null)
            newDay = Storage.filter(day, Storage.FilterType.FILTER_CLASS_TEACHER, o.text);
        else
            newDay = Storage.filter(day, Storage.FilterType.FILTER_NONE, null);

        // display substitution info only if display width is greater than treshold
        int width;
        int threshold = getResources().getDimensionPixelSize(R.dimen.table_substitutioninfo_threshold);
        // LayoutMeasureView.w is ALWAYS the width in portrait mode!
        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
            width = LayoutMeasureView.screenH;
        else
            width = LayoutMeasureView.screenW;

        if (width < threshold)
            newDay.generalData.flags.add(SubstitutionTable.GeneralData.Flags.HIDE_SUBSTITUTIONINFO);

        int columnWidth[] = new int[day.getHeaderCount()];

        if (newDay.getEntryCount() > 0) {
            SubstitutionPlanAdapter adapter = new SubstitutionPlanAdapter(getActivity(), newDay, columnWidth);

            // initial calculation of column width
            for (int i = 0; i < day.getHeaderCount(); i++) {
                columnWidth[i] = calculateTableColWidth(newDay, adapter, i, tableFixHeaders);
            }

            // assign adapter
            tableFixHeaders.setAdapter(adapter);
            tableFixHeaders.setVisibility(View.VISIBLE);
            textPlaceholder.setVisibility(View.GONE);
        } else {
            tableFixHeaders.setVisibility(View.GONE);
            textPlaceholder.setVisibility(View.VISIBLE);
        }

        // set update time
        updateTime.setText(newDay.generalData.updateTime);

        ViewGroup container = (ViewGroup) view.findViewById(R.id.dailyInfos);
        container.removeAllViews();

        // set daily infos
        int i = 0;
        int c = newDay.generalData.dailyInfos.size();
        View v = null;
        for (SubstitutionTable.GeneralData.DailyInfo info : newDay.generalData.dailyInfos) {
            v = getActivity().getLayoutInflater().inflate(R.layout.table_dailyinfo, container, false);
            TextView tvTitle = (TextView) v.findViewById(R.id.title);
            TextView tvDescription = (TextView) v.findViewById(R.id.description);
            tvTitle.setText(info.title);

            if (info.description.equals("")) {
                ((ViewGroup) v).removeViewInLayout(tvDescription);
                LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) tvTitle.getLayoutParams();
                params.weight = 1f;
            } else
                tvDescription.setText(info.description);

            container.addView(v);
            i++;
        }
    }

    @Override
    public void setCId(int CId) {
        super.setCId(CId);

        // set action bar title to the subItem's name
        if (CId != -1 && mCallback != null)
            mCallback.setTitle(((NavigationListAdapter.SubItem) mCallback.getNavigationDrawerFragment()
                    .getItemByCId(CId)).text);
    }
}