com.jbirdvegas.mgerrit.PatchSetViewerFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.jbirdvegas.mgerrit.PatchSetViewerFragment.java

Source

package com.jbirdvegas.mgerrit;

/*
 * Copyright (C) 2013 Android Open Kang Project (AOKP)
 *  Author: Jon Stanford (JBirdVegas), 2013
 *
 *  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.
 */

import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.util.Pair;
import android.view.ActionMode;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ExpandableListView;

import com.google.analytics.tracking.android.EasyTracker;
import com.jbirdvegas.mgerrit.adapters.CommitDetailsAdapter;
import com.jbirdvegas.mgerrit.cards.PatchSetChangesCard;
import com.jbirdvegas.mgerrit.database.Changes;
import com.jbirdvegas.mgerrit.database.Config;
import com.jbirdvegas.mgerrit.database.FileChanges;
import com.jbirdvegas.mgerrit.database.Revisions;
import com.jbirdvegas.mgerrit.database.SelectedChange;
import com.jbirdvegas.mgerrit.database.UserChanges;
import com.jbirdvegas.mgerrit.database.UserMessage;
import com.jbirdvegas.mgerrit.database.UserReviewers;
import com.jbirdvegas.mgerrit.helpers.AnalyticsHelper;
import com.jbirdvegas.mgerrit.helpers.Tools;
import com.jbirdvegas.mgerrit.message.ChangeLoadingFinished;
import com.jbirdvegas.mgerrit.message.NewChangeSelected;
import com.jbirdvegas.mgerrit.message.StatusSelected;
import com.jbirdvegas.mgerrit.objects.FilesCAB;
import com.jbirdvegas.mgerrit.objects.GerritURL;
import com.jbirdvegas.mgerrit.objects.JSONCommit;
import com.jbirdvegas.mgerrit.search.ChangeSearch;
import com.jbirdvegas.mgerrit.tasks.GerritService;

import org.jetbrains.annotations.Nullable;

import de.greenrobot.event.EventBus;

/**
 * Class handles populating the screen with several
 * cards each giving more information about the patchset
 * <p/>
 * All cards are located at jbirdvegas.mgerrit.cards.*
 */
public class PatchSetViewerFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {

    private View disconnectedView;
    private Activity mParent;
    private Context mContext;

    private GerritURL mUrl;
    private String mSelectedChange;
    private String mStatus;
    private int mChangeNumber;
    // Whether the server supports the new change details endpoint (false if so)
    private boolean sIsLegacyVersion;

    private CommitDetailsAdapter mAdapter;
    private FilesCAB mFilesCAB;

    public static final String CHANGE_ID = "changeID";
    public static final String CHANGE_NO = "changeNo";
    public static final String STATUS = "queryStatus";

    public static final int LOADER_PROPERTIES = 0;
    public static final int LOADER_MESSAGE = 1;
    public static final int LOADER_FILES = 2;
    public static final int LOADER_REVIEWERS = 3;
    public static final int LOADER_COMMENTS = 4;

    private EventBus mEventBus;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.patchset_list, container, false);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        init();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mParent = this.getActivity();
        mContext = mParent.getApplicationContext();
    }

    private void init() {
        View currentFragment = this.getView();

        ExpandableListView mListView = (ExpandableListView) currentFragment.findViewById(R.id.commit_cards);
        disconnectedView = currentFragment.findViewById(R.id.disconnected_view);

        sIsLegacyVersion = !Config.isDiffSupported(mParent);

        mAdapter = new CommitDetailsAdapter(mParent);
        mListView.setAdapter(mAdapter);

        // Child click listeners (relevant for the changes cards)
        mListView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
        mFilesCAB = new FilesCAB(mParent, !sIsLegacyVersion);
        mAdapter.setContextualActionBar(mFilesCAB);
        mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
                ExpandableListView listView = (ExpandableListView) parent;
                long pos = listView.getExpandableListPosition(position);
                int groupPos = ExpandableListView.getPackedPositionGroup(pos);
                int childPos = ExpandableListView.getPackedPositionChild(pos);

                if (!mAdapter.isLongClickSupported(groupPos, childPos)) {
                    return false;
                }

                // In case this is a group view and does not have the change number tagged
                view.setTag(R.id.changeID, mSelectedChange);
                FilesCAB.TagHolder holder = new FilesCAB.TagHolder(view, mContext, groupPos, childPos >= 0);

                // Set the title to be shown in the action bar
                if (holder.filePath != null) {
                    mFilesCAB.setTitle(holder.filePath);
                } else {
                    String s = mParent.getResources().getString(R.string.change_detail_heading);
                    mFilesCAB.setTitle(String.format(s, holder.changeNumber.intValue()));
                }

                mFilesCAB.setActionMode(getActivity().startActionMode(mFilesCAB));
                ActionMode actionMode = mFilesCAB.getActionMode();

                // Call requires API 14 (ICS)
                actionMode.setTag(holder);
                view.setSelected(true);
                return true;
            }
        });
        mListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
            @Override
            public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition,
                    long id) {
                // This is only valid for the changed files group
                int childItemType = mAdapter.getChildType(groupPosition, childPosition);
                if (childItemType != CommitDetailsAdapter.Cards.CHANGED_FILES.ordinal()) {
                    return false;
                }
                // View the diff and close the CAB if a change diff could be viewed
                boolean diffLaunched = PatchSetChangesCard.onViewClicked(mParent, v);
                if (diffLaunched) {
                    ActionMode mode = mFilesCAB.getActionMode();
                    if (mode != null)
                        mode.finish();
                }
                return diffLaunched;
            }
        });

        mUrl = new GerritURL();

        Button retryButton = (Button) currentFragment.findViewById(R.id.btn_retry);
        retryButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (sIsLegacyVersion)
                    sendRequest(GerritService.DataType.Commit);
                else
                    sendRequest(GerritService.DataType.LegacyCommitDetails);
            }
        });

        if (getArguments() == null) {
            /** This should be the default value of {@link ChangeListFragment.mSelectedStatus } */
            setStatus(JSONCommit.Status.NEW.toString());
            loadChange(true);
        } else {
            Bundle args = getArguments();
            setStatus(args.getString(STATUS));
            String changeid = args.getString(CHANGE_ID);
            mChangeNumber = args.getInt(CHANGE_NO);

            if (changeid != null && !changeid.isEmpty()) {
                loadChange(changeid);
            }
        }

        mEventBus = EventBus.getDefault();
    }

    /**
     * Start the updater to check for an update if necessary
     */
    private void sendRequest(GerritService.DataType dataType) {

        // If we aren't connected, there's nothing to do here
        if (!switchViews())
            return;

        /*
         * Requires Gerrit version 2.8
         * /changes/{change-id}/detail with arguments was introduced in version 2.8,
         * so this will not be able to get the files changed or the full commit message
         * in prior Gerrit versions.
         */
        GerritService.sendRequest(mParent, dataType, mUrl);
    }

    private void restartLoaders(String changeID) {
        Bundle args = new Bundle();
        args.putString(CHANGE_ID, changeID);

        for (int i = LOADER_PROPERTIES; i <= LOADER_COMMENTS; i++) {
            getLoaderManager().restartLoader(i, args, this);
        }
    }

    /**
     * Determine the changeid to load and send an intent to load the change.
     * By sending an intent, the main activity is notified (GerritControllerActivity
     * on tablets. This can then tell the change list adapter that we have selected
     * a change.
     *
     * @param direct true: load this change directly, false: send out an intent
     */
    private void loadChange(boolean direct) {
        if (mStatus == null) {
            // Without the status we cannot find a changeid to load data for
            return;
        }

        Pair<String, Integer> change = SelectedChange.getSelectedChange(mContext, mStatus);
        String changeID;
        int changeNumber;

        if (change == null || change.first.isEmpty()) {
            change = Changes.getMostRecentChange(mParent, mStatus);
            if (change == null || change.first.isEmpty()) {
                // No changes to load data from
                AnalyticsHelper.sendAnalyticsEvent(mParent, "PatchSetViewerFragment", "load_change",
                        "null_changeID", null);
                return;
            }
        }

        changeID = change.first;
        mChangeNumber = change.second;

        if (direct)
            loadChange(changeID);
        else {
            mEventBus.post(new NewChangeSelected(changeID, mChangeNumber, mStatus, this));
        }
    }

    /**
     * Set the change id to load details for and load the change
     *
     * @param changeID A valid change id
     */
    public void loadChange(String changeId) {
        this.mSelectedChange = changeId;

        mUrl.addSearchKeyword(new ChangeSearch(mSelectedChange));
        mUrl.setChangeNumber(mChangeNumber);
        mUrl.requestChangeDetail(true, sIsLegacyVersion);

        if (sIsLegacyVersion)
            sendRequest(GerritService.DataType.LegacyCommitDetails);
        else
            sendRequest(GerritService.DataType.CommitDetails);

        restartLoaders(changeId);
    }

    /**
     * Use this to set the status to ensure we only use the database status
     *
     * @param status A valid change status string (database or web format)
     */
    public void setStatus(String status) {
        this.mStatus = JSONCommit.Status.getStatusString(status);
    }

    public boolean compareStatus(String status1, String status2) {
        return (JSONCommit.Status.getStatusString(status1)).equals(JSONCommit.Status.getStatusString(status2));
    }

    /**
     * Helper function to get the selected status in the change list fragment.
     * If it is phone mode (the parent is not the main activity), then this will
     * return null.
     *
     * @return The selected status in the change list fragment, or null if not available
     */
    @Nullable
    private String getStatus() {
        if (mParent instanceof GerritControllerActivity) {
            GerritControllerActivity controllerActivity = (GerritControllerActivity) mParent;
            return controllerActivity.getChangeList().getStatus();
        }
        return null;
    }

    @Override
    public void onResume() {
        super.onResume();
        mEventBus.register(this);
    }

    @Override
    public void onStart() {
        super.onStart();
        EasyTracker.getInstance(mParent).activityStart(mParent);
    }

    @Override
    public void onStop() {
        super.onStop();
        EasyTracker.getInstance(mParent).activityStop(mParent);
    }

    @Override
    public void onPause() {
        super.onPause();
        mEventBus.unregister(this);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString(CHANGE_ID, mSelectedChange);
        outState.putInt(CHANGE_NO, mChangeNumber);
        outState.putString(STATUS, mStatus);
    }

    @Override
    public void onViewStateRestored(Bundle savedInstanceState) {
        if (savedInstanceState != null) {
            mSelectedChange = savedInstanceState.getString(CHANGE_ID);
            mChangeNumber = savedInstanceState.getInt(CHANGE_NO);
            setStatus(savedInstanceState.getString(STATUS));
            restartLoaders(mSelectedChange);
        }
        super.onViewStateRestored(savedInstanceState);
    }

    /*
    Possible cards
        
    --Patch Set--
    Select patchset number to display in these cards
    -------------
        
    --Times Card--
    Original upload time
    Most recent update
    --------------
        
    --Inline comments Card?--
    Show all comments inlined on code view pages
    **may be kind of pointless without context of surrounding code**
    * maybe a webview for each if possible? *
    -------------------------
        
     */

    private boolean switchViews() {
        boolean isconn = Tools.isConnected(mParent);
        if (isconn) {
            disconnectedView.setVisibility(View.GONE);
        } else {
            disconnectedView.setVisibility(View.VISIBLE);
        }
        return isconn;
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {

        String changeID = args.getString(PatchSetViewerFragment.CHANGE_ID);

        switch (id) {
        case LOADER_PROPERTIES:
            return UserChanges.getCommitProperties(mContext, changeID);
        case LOADER_MESSAGE:
            return Revisions.getCommitMessage(mContext, changeID);
        case LOADER_FILES:
            return FileChanges.getFileChanges(mContext, changeID);
        case LOADER_REVIEWERS:
            return UserReviewers.getReviewersForChange(mContext, changeID);
        case LOADER_COMMENTS:
            return UserMessage.getMessagesForChange(mContext, changeID);
        }

        return null;
    }

    @Override
    public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
        CommitDetailsAdapter.Cards cardType = null;

        switch (cursorLoader.getId()) {
        case LOADER_PROPERTIES:
            cardType = CommitDetailsAdapter.Cards.PROPERTIES;
            break;
        case LOADER_MESSAGE:
            cardType = CommitDetailsAdapter.Cards.COMMIT_MSG;
            break;
        case LOADER_FILES:
            cardType = CommitDetailsAdapter.Cards.CHANGED_FILES;
            break;
        case LOADER_REVIEWERS:
            cardType = CommitDetailsAdapter.Cards.REVIEWERS;
            break;
        case LOADER_COMMENTS:
            cardType = CommitDetailsAdapter.Cards.COMMENTS;
            break;
        }
        if (cardType != null)
            mAdapter.setCursor(cardType, cursor);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> cursorLoader) {
        onLoadFinished(cursorLoader, null);
    }

    public void onEventMainThread(StatusSelected ev) {
        setStatus(ev.getStatus());
        loadChange(false);
    }

    public void onEventMainThread(ChangeLoadingFinished ev) {
        String status = ev.getStatus();

        /* We may have got a broadcast saying that data from another tab
         *  has been loaded. */
        if (compareStatus(status, getStatus())) {
            setStatus(status);
            loadChange(false);
        }
    }
}