ro.catalin.prata.testflightuploader.view.TFUploader.java Source code

Java tutorial

Introduction

Here is the source code for ro.catalin.prata.testflightuploader.view.TFUploader.java

Source

package ro.catalin.prata.testflightuploader.view;

import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.project.ProjectManagerListener;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowFactory;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentFactory;
import com.jgoodies.common.collect.ArrayListModel;
import ro.catalin.prata.testflightuploader.Model.Team;
import ro.catalin.prata.testflightuploader.controller.KeysManager;
import ro.catalin.prata.testflightuploader.controller.ModulesManager;
import ro.catalin.prata.testflightuploader.provider.UploadService;
import ro.catalin.prata.testflightuploader.utils.Utils;

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

/*  Copyright 2013 Catalin Prata
    
    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. */

/**
 * Description
 *
 * @author Catalin Prata
 *         Date: 6/1/13
 */
public class TFUploader implements ToolWindowFactory, UploadService.UploadServiceDelegate {

    private JButton uploadButton;
    private JPanel mainPanel;
    private JTextArea whatIsNewTextField;
    private JCheckBox notifyTeamCheckBox;
    private JProgressBar progressBar;
    private JList teamList;
    private JButton deleteTeamButton;
    private JButton browseButton;
    private JTextField apkFilePathTextField;
    private JButton setApiKeyButton;
    private JTextArea distributionsListTextArea;
    private JTextField buildVNameTextField;
    private JTextField buildVCodeTextField;
    private JButton buildVersionHelpBtn;
    private JCheckBox buildVersionCheck;
    private JLabel bVersionCodeLbl;
    private JLabel bVersionNameLbl;
    private JComboBox moduleCombo;
    private JTextPane pleaseNoteThatOnlyTextPane;
    private ToolWindow toolWindow;

    public TFUploader() {

        // update the list
        updateListOfTeams(KeysManager.instance().getTeamList());

        uploadButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {

                performUploadValidation();

            }
        });

        teamList.addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {

                // if the Add new Team item is selected, open a dialog with name and token input fields
                if (teamList.getSelectedIndex() == 0) {

                    AddTeamDialog dialog = new AddTeamDialog(new AddTeamDialog.AddTeamListener() {
                        @Override
                        public void onTeamAdded(Team newTeam) {

                            // add the new team to the list
                            KeysManager.instance().addTeam(newTeam);

                            // update the list
                            updateListOfTeams(KeysManager.instance().getTeamList());

                        }
                    });
                    dialog.pack();
                    dialog.setVisible(true);

                }

            }
        });

        deleteTeamButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {

                if (teamList.getSelectedIndex() > 0) {

                    // remove the selected team from the list
                    KeysManager.instance().removeTeamAtIndex(teamList.getSelectedIndex());
                    // update the list
                    updateListOfTeams(KeysManager.instance().getTeamList());

                }

            }
        });

        browseButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {

                // create a new file type with the apk extension to be used for file type filtering
                FileType type = FileTypeManager.getInstance().getFileTypeByExtension("apk");

                // create a descriptor for the file chooser
                FileChooserDescriptor descriptor = Utils.createSingleFileDescriptor(type);
                descriptor.setTitle("Android Apk File");
                descriptor.setDescription("Please chose the project Apk file to be uploaded to Test Flight");

                // by default open the first opened project root directory
                VirtualFile fileToSelect = ProjectManager.getInstance().getOpenProjects()[0].getBaseDir();

                // open the file chooser
                FileChooser.chooseFiles(descriptor, null, fileToSelect, new FileChooser.FileChooserConsumer() {
                    @Override
                    public void cancelled() {

                        // do nothing for now...

                    }

                    @Override
                    public void consume(List<VirtualFile> virtualFiles) {

                        String filePath = virtualFiles.get(0).getPath();

                        // the file was selected so add it to the text field
                        apkFilePathTextField.setText(filePath);

                        // save the file path
                        KeysManager.instance().setApkFilePath(filePath);

                    }
                });

            }
        });

        setApiKeyButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {

                // open an input dialog for the api key
                String apiKey = Messages.showInputDialog(ProjectManager.getInstance().getOpenProjects()[0],
                        "<HTML>This token gives you access to the upload API. You can get it from <a href=\"https://testflightapp.com/account/#api\">here</a>.</HTML>",
                        "Upload API Token", null, KeysManager.instance().getApiKey(), null);

                // save the api key after a minor validation
                if (apiKey != null && apiKey.length() > 3) {
                    KeysManager.instance().setApiKey(apiKey);
                }

            }
        });

        teamList.addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {

                distributionsListTextArea.setText("");

                if (teamList.getSelectedIndex() > 0) {

                    distributionsListTextArea.setText(KeysManager.instance().getTeamList()
                            .get(teamList.getSelectedIndex()).getDistributionList());
                    distributionsListTextArea.setEnabled(true);

                } else {

                    distributionsListTextArea.setEnabled(false);

                }

            }
        });

        distributionsListTextArea.addFocusListener(new FocusAdapter() {
            @Override
            public void focusLost(FocusEvent e) {
                super.focusLost(e);

                if (teamList.getSelectedIndex() > 0) {

                    KeysManager.instance().getTeamList().get(teamList.getSelectedIndex())
                            .setDistributionList(distributionsListTextArea.getText());

                }

            }
        });

        buildVersionHelpBtn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {

                // build version info button was pressed, display the about text...
                Messages.showInfoMessage(
                        "This feature let you change the version code/name of the build after it is sent to Test Flight.\n"
                                + "If you change the values of the build version code or name, it will be saved in your main manifest file. \n"
                                + "This can be useful to remind you to increment the build number after sending the apk to TestFlight. \n \n"
                                + "Please note that the change is made after the build is sent to Test Flight.",
                        "Android Build Version Code/Name Update");

            }
        });

        buildVersionCheck.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e) {

                if (buildVersionCheck.isSelected()) {

                    setBuildFeatureComponentsVisible(true);

                } else {

                    setBuildFeatureComponentsVisible(false);

                }

            }
        });

        moduleCombo.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e) {

                // if a module is selected, save the module
                KeysManager.instance().setSelectedModuleName((String) moduleCombo.getSelectedItem());

                // update the apk path
                Module module = ModulesManager.instance().getModuleByName((String) moduleCombo.getSelectedItem());
                apkFilePathTextField.setText(ModulesManager.instance().getAndroidApkPath(module));

                // update the build version fields too
                updateBuildVersionFields();

            }
        });

        // setup the previously saved values on the UI or the default ones
        setupValuesOnUI();

    }

    /**
     * Updates the build version(code and name) fields
     */
    public void updateBuildVersionFields() {
        Module module = ModulesManager.instance().getModuleByName((String) moduleCombo.getSelectedItem());
        // update the code and name text fields manifest build version code and name values
        buildVCodeTextField.setText(Utils.validateString(ModulesManager.instance()
                .getBuildVersionCode(ModulesManager.instance().getManifestForModule(module))));

        buildVNameTextField.setText(Utils.validateString(ModulesManager.instance()
                .getBuildVersionName(ModulesManager.instance().getManifestForModule(module))));

    }

    /**
     * Performs validation before uploading the build to test flight, if everything is in order, the build is sent
     */
    public void performUploadValidation() {

        if (Calendar.getInstance().getTimeInMillis() - KeysManager.getLastCompileTime()
                .getTimeInMillis() > KeysManager.MAX_MILLISECONDS_SINCE_LAST_COMPILE) {

            Messages.showErrorDialog("Please note that the project was not compiled since at least "
                    + (KeysManager.MAX_MILLISECONDS_SINCE_LAST_COMPILE / 1000 / 60) + " minutes ago. "
                    + "If you made changes since then, please rebuild the project to generate the new APK file and upload the latest build to Test Flight.",
                    "Possible Old Build");

            // display this message only once to the user
            KeysManager.setLastCompileTime(Calendar.getInstance());

        } else if (KeysManager.instance().getApiKey() == null) {

            Messages.showErrorDialog(
                    "The Test Flight API token is not set. Please press the 'Set Api Token' button to add your Test Flight API token.",
                    "Invalid Test Flight API Token");

        } else if (teamList.getSelectedIndex() < 1) {

            Messages.showErrorDialog("Please add/select a team to send the build to.", "Invalid Test Flight Team");

        } else if (apkFilePathTextField.getText().length() < 3) {

            Messages.showErrorDialog("Please select the apk file to be sent to Test Flight.",
                    "Invalid Test Flight APK File");

        } else if (whatIsNewTextField.getText().length() < 2) {

            Messages.showErrorDialog("Please add a release note text.", "Invalid Build Release Notes");

        } else {

            if (buildVersionCheck.isSelected()) {

                Module module = ModulesManager.instance().getModuleByName((String) moduleCombo.getSelectedItem());

                ModulesManager.instance().setBuildVersionNameAndCode(
                        ModulesManager.instance().getManifestForModule(module), buildVNameTextField.getText(),
                        buildVCodeTextField.getText(), new ModulesManager.ManifestChangesDelegate() {
                            @Override
                            public void onVersionValueFinishedUpdate() {

                                uploadBuild();

                            }
                        });

            } else {

                uploadBuild();

            }

        }

    }

    /**
     * Uploads the build to test flight, it updates also the UI
     */
    public void uploadBuild() {

        progressBar.setVisible(true);
        uploadButton.setEnabled(false);
        uploadButton.setText("Uploading...");

        // upload the build
        new UploadService().sendBuild(null, apkFilePathTextField.getText(), KeysManager.instance().getApiKey(),
                KeysManager.instance().getTeamList().get(teamList.getSelectedIndex()).getToken(),
                whatIsNewTextField.getText(),
                KeysManager.instance().getTeamList().get(teamList.getSelectedIndex()).getDistributionList(),
                notifyTeamCheckBox.isSelected(), TFUploader.this);

    }

    /**
     * Set the default or previously saved values on the UI components
     */
    public void setupValuesOnUI() {

        Module previouslySelectedModule;

        // if the apk file path was not saved previously by the user, set the saved module apk file path or the best matching module
        previouslySelectedModule = ModulesManager.instance()
                .getModuleByName(KeysManager.instance().getSelectedModuleName());
        if (previouslySelectedModule != null) {

            apkFilePathTextField.setText(ModulesManager.instance().getAndroidApkPath(previouslySelectedModule));

        } else {

            // get the best matching module for this project and set it's file path
            previouslySelectedModule = ModulesManager.instance().getMostImportantModule();
            apkFilePathTextField.setText(ModulesManager.instance().getAndroidApkPath(previouslySelectedModule));

        }

        // set the model of the modules
        moduleCombo.setModel(new DefaultComboBoxModel(ModulesManager.instance().getAllModuleNames()));

        // set the selection
        moduleCombo.setSelectedIndex(
                ModulesManager.instance().getSelectedModuleIndex(previouslySelectedModule.getName()));

        // set the distribution list input disabled by default
        distributionsListTextArea.setEnabled(false);

        // hide the build version change feature components by default
        setBuildFeatureComponentsVisible(false);

        // update the build version fields
        updateBuildVersionFields();

    }

    /**
     * Changes the components visibility of the build version changing feature
     *
     * @param visible true if the components should be displayed, false otherwise
     */
    public void setBuildFeatureComponentsVisible(boolean visible) {

        bVersionCodeLbl.setVisible(visible);
        bVersionNameLbl.setVisible(visible);
        buildVCodeTextField.setVisible(visible);
        buildVNameTextField.setVisible(visible);

    }

    /**
     * Updates the list of teams
     *
     * @param teams list of teams to be displayed on the screen
     */
    public void updateListOfTeams(ArrayList<Team> teams) {

        // create a new list model
        ArrayListModel<String> model = new ArrayListModel<String>();

        for (Team team : teams) {
            // add all the teams in the list model
            model.add(team.getName());
        }

        // set the new model containing the new team
        teamList.setModel(model);

    }

    public void createToolWindowContent(Project project, ToolWindow toolWindow) {

        ContentFactory contentFactory = ContentFactory.SERVICE.getInstance();
        Content content = contentFactory.createContent(mainPanel, "", false);
        toolWindow.getContentManager().addContent(content);

        this.toolWindow = toolWindow;

        ProjectManager.getInstance().addProjectManagerListener(new ProjectManagerListener() {
            @Override
            public void projectOpened(Project project) {

                // get the best matching module for this project and set it's file path
                Module previouslySelectedModule = ModulesManager.instance().getMostImportantModule();
                apkFilePathTextField.setText(Utils
                        .validateString(ModulesManager.instance().getAndroidApkPath(previouslySelectedModule)));

                KeysManager.instance().setSelectedModuleName(previouslySelectedModule.getName());

                String[] modules = ModulesManager.instance().getAllModuleNames();
                if (modules != null) {
                    // set the model of the modules
                    moduleCombo.setModel(new DefaultComboBoxModel(ModulesManager.instance().getAllModuleNames()));
                }

                // set the selection
                moduleCombo.setSelectedIndex(
                        ModulesManager.instance().getSelectedModuleIndex(previouslySelectedModule.getName()));

            }

            @Override
            public boolean canCloseProject(Project project) {
                return true;
            }

            @Override
            public void projectClosed(Project project) {

            }

            @Override
            public void projectClosing(Project project) {

            }
        });

    }

    private void createUIComponents() {

    }

    @Override
    public void onUploadFinished(final boolean finishedSuccessful) {

        // upload is now finished, run some UI updates on the UI thread
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {

                if (!finishedSuccessful) {

                    Messages.showErrorDialog("Build upload failed. Reason: Unknown", "Build Upload Failed");

                }

                progressBar.setVisible(false);
                uploadButton.setEnabled(true);
                uploadButton.setText("Upload");

            }
        });

    }

    @Override
    public void onPackageSizeComputed(final long totalSize) {

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {

                progressBar.setMaximum((int) totalSize);

            }
        });

    }

    @Override
    public void onProgressChanged(final long progress) {

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {

                progressBar.setValue((int) progress);

            }
        });

    }

}