com.madgag.agit.CloneLauncherActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.madgag.agit.CloneLauncherActivity.java

Source

/*
 * Copyright (c) 2011, 2012 Roberto Tyley
 *
 * This file is part of 'Agit' - an Android Git client.
 *
 * Agit 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.
 *
 * Agit 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.madgag.agit;

import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Build.VERSION_CODES.GINGERBREAD;
import static android.os.Build.VERSION_CODES.HONEYCOMB;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static android.widget.Toast.LENGTH_LONG;
import static android.widget.Toast.LENGTH_SHORT;
import static com.madgag.agit.GitIntents.EXTRA_TARGET_DIR;
import static com.madgag.agit.GitIntents.sourceUriFrom;
import static com.madgag.agit.GitOperationsService.cloneOperationIntentFor;
import static com.madgag.agit.R.string.clone_launcher_activity_title;
import static com.madgag.agit.R.string.clone_readiness_not_enough_room_on_partition;
import static com.madgag.agit.R.string.clone_readiness_repository_folder_already_exists;
import static com.madgag.agit.R.string.ssh_agent_not_correctly_installed;
import static com.madgag.agit.RepositoryViewerActivity.manageRepoIntent;
import static com.madgag.agit.git.TransportProtocols.niceProtocolNameFor;
import static com.madgag.android.ActionBarUtil.fixImageTilingOn;
import static com.madgag.android.ActionBarUtil.homewardsWith;
import static com.madgag.android.ClickableText.BOLD_LINK_STYLE;
import static org.apache.commons.io.FileUtils.byteCountToDisplaySize;
import static org.eclipse.jgit.lib.Constants.DOT_GIT_EXT;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.text.Editable;
import android.text.SpannableStringBuilder;
import android.text.TextWatcher;
import android.text.method.LinkMovementMethod;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.view.MenuItem;
import com.github.rtyley.android.sherlock.roboguice.activity.RoboSherlockActivity;
import com.madgag.android.ClickableText;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;

import org.eclipse.jgit.lib.RepositoryCache;
import org.eclipse.jgit.transport.URIish;
import org.eclipse.jgit.util.FS;

import roboguice.inject.InjectView;

public class CloneLauncherActivity extends RoboSherlockActivity {
    private final static String TAG = "CloneLauncherActivity";
    private static final long MIN_REQUIRED_SPACE = 128L * 1024L;

    public static Intent cloneLauncherIntentFor(String sourceUri) {
        return new GitIntentBuilder("clone.PREPARE").sourceUri(sourceUri).toIntent();
    }

    private final boolean layoutTransitionAvailable = Build.VERSION.SDK_INT >= HONEYCOMB;

    @InjectView(R.id.BareRepo)
    CheckBox bareRepoCheckbox;
    @InjectView(R.id.GoCloneButton)
    Button button;
    @InjectView(R.id.UseDefaultGitDirLocation)
    CheckBox useDefaultGitDirLocationButton;
    @InjectView(R.id.ProtocolLabel)
    TextView protocolLabel;

    @InjectView(R.id.secondary_details)
    ViewGroup secondaryDetailsGroup;

    @InjectView(R.id.CloneReadinessMessage)
    TextView cloneReadinessMessageView;
    @InjectView(R.id.GitDirEditText)
    EditText gitDirEditText;
    @InjectView(R.id.CloneUrlEditText)
    EditText cloneUrlEditText;

    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        fixImageTilingOn(getSupportActionBar());
        setContentView(R.layout.clone_launcher);
        ActionBar actionBar = getSupportActionBar();
        actionBar.setDisplayHomeAsUpEnabled(true);
        actionBar.setTitle(clone_launcher_activity_title);

        button.setOnClickListener(goCloneButtonListener);
        OnCheckedChangeListener checkBoxChangeListener = new OnCheckedChangeListener() {
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                updateUIWithValidation();
            }
        };
        useDefaultGitDirLocationButton.setOnCheckedChangeListener(checkBoxChangeListener);
        bareRepoCheckbox.setOnCheckedChangeListener(checkBoxChangeListener);
        TextWatcher watcher = new TextWatcher() {
            public void onTextChanged(CharSequence text, int arg1, int arg2, int arg3) {
            }

            public void beforeTextChanged(CharSequence text, int arg1, int arg2, int arg3) {
            }

            public void afterTextChanged(Editable gitDirEditText) {
                updateUIWithValidation();
            }
        };
        cloneUrlEditText.addTextChangedListener(watcher);
        gitDirEditText.addTextChangedListener(watcher);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case android.R.id.home:
            return homewardsWith(this, new Intent(this, DashboardActivity.class));
        }
        return super.onOptionsItemSelected(item);
    }

    protected void updateUIWithValidation() {
        boolean enableClone = true;

        String cloneUriText = getCloneUriText();
        Log.d(TAG, "cloneUriText=" + cloneUriText);
        CharSequence message = null;

        boolean cloneUrlPopulated = cloneUriText.length() > 3;

        // GONE causes jerky animation with the 1st layout transition here, I think because of the initial resize
        secondaryDetailsGroup
                .setVisibility((cloneUrlPopulated || !layoutTransitionAvailable) ? VISIBLE : INVISIBLE);

        if (!cloneUrlPopulated) {
            enableClone = false;
            message = getString(R.string.clone_readiness_needs_user_to_enter_a_url);
        }

        URIish cloneUri = null;
        String transportProtocol = null;
        try {
            cloneUri = getCloneUri();
            transportProtocol = niceProtocolNameFor(cloneUri);
        } catch (URISyntaxException e) {
            enableClone = false;
        }

        if (transportProtocol != null) {
            Log.w(TAG, "Smells like " + transportProtocol);
            protocolLabel.setText("Protocol: " + transportProtocol);
            protocolLabel.setVisibility(VISIBLE);
            if (transportProtocol.equals("SSH")) {
                boolean sshAgentAvailable = PERMISSION_GRANTED == checkCallingOrSelfPermission(
                        "org.openintents.ssh" + ".permission.ACCESS_SSH_AGENT");
                Log.d(TAG, "SSH good : " + sshAgentAvailable);
                if (!sshAgentAvailable) {
                    message = getString(ssh_agent_not_correctly_installed);
                }
            }
        } else {
            Log.w(TAG, "Don't recognise protocol");
            protocolLabel.setVisibility(INVISIBLE);
        }

        gitDirEditText.setEnabled(!useDefaultGitDirLocationButton.isChecked());
        if (useDefaultGitDirLocationButton.isChecked()) {
            String requiredText = (cloneUri == null) ? "" : defaultRepoDirFor(cloneUri).getAbsolutePath();
            String currentGitDirText = gitDirEditText.getText().toString();
            if (!currentGitDirText.equals(requiredText))
                gitDirEditText.setText(requiredText);
        }

        File f = getTargetFolder();
        boolean folderAlreadyExists = f.exists();
        if (folderAlreadyExists) {
            enableClone = false;
            if (existingRepoGitDir() != null) {
                message = getString(clone_readiness_repository_folder_already_exists);
            } else {
                message = getString(R.string.clone_readiness_folder_already_exists);
            }
        } else if (f.getPath().length() > 0) {
            if (Build.VERSION.SDK_INT >= GINGERBREAD) {
                long usableSpace = existingFileInPartitionContaining(f).getUsableSpace();
                String spaceText = byteCountToDisplaySize(usableSpace);
                Log.d(TAG, "usableSpace = " + spaceText);
                if (usableSpace < MIN_REQUIRED_SPACE) {
                    enableClone = false;
                    message = getString(clone_readiness_not_enough_room_on_partition, spaceText);
                }
            }
        }
        displayHelp(message);

        button.setEnabled(enableClone);
    }

    private File existingFileInPartitionContaining(File f) {
        while (!f.exists() && f.getParentFile() != null) {
            f = f.getParentFile();
        }
        return f;
    }

    private void displayHelp(CharSequence message) {
        cloneReadinessMessageView.setVisibility(message == null ? INVISIBLE : VISIBLE);
        if (message != null) {
            Editable spana = new SpannableStringBuilder(message);
            ClickableText.addLinks(spana, BOLD_LINK_STYLE, new ClickableText.Listener() {
                public void onClick(String command, View widget) {
                    if (command.equals("specify_target_dir")) {
                        useDefaultGitDirLocationButton.setChecked(false);
                        gitDirEditText.requestFocus();
                        setCursorToEnd(gitDirEditText);
                    } else if (command.equals("view_existing_repo")) {
                        startActivity(manageRepoIntent(existingRepoGitDir()));
                    } else if (command.equals("view_ssh_instructions")) {
                        startActivity(new Intent(CloneLauncherActivity.this, AboutUsingSshActivity.class));
                    } else if (command.equals("suggest_repo")) {
                        startActivityForResult(new GitIntentBuilder("repo.SUGGEST").toIntent(), 0);
                    }
                }
            });
            cloneReadinessMessageView.setText(spana);
            cloneReadinessMessageView.setMovementMethod(LinkMovementMethod.getInstance());
        }
    }

    private File existingRepoGitDir() {
        return RepositoryCache.FileKey.resolve(getTargetFolder(), FS.detect());
    }

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode == RESULT_OK) {
            setUpUIFromIntent(data);
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        Intent intent = getIntent();
        setUpUIFromIntent(intent);
    }

    private void setUpUIFromIntent(Intent intent) {
        Log.d(TAG, "setUpUIFromIntent with " + intent);
        if (intent != null) {
            setSourceUriFrom(intent);
            setGitDirFrom(intent);
        }
    }

    private void setSourceUriFrom(Intent intent) {
        String sourceUri = sourceUriFrom(intent);
        if (sourceUri != null) {
            cloneUrlEditText.setText(sourceUri);
            setCursorToEnd(cloneUrlEditText);
            Log.d(TAG, "Set cloneUrlEditText to " + sourceUri);
        }
    }

    private static void setCursorToEnd(EditText editText) {
        editText.setSelection(editText.getText().length());
    }

    private void setGitDirFrom(Intent intent) {
        String gitdir = intent.getStringExtra(EXTRA_TARGET_DIR);
        useDefaultGitDirLocationButton.setChecked(gitdir == null);
        if (gitdir != null) {
            gitDirEditText.setText(gitdir);
            Log.d(TAG, "Set gitdir to " + gitdir);
        }
    }

    ;

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume called");
        updateUIWithValidation();
    }

    public URIish getCloneUri() throws URISyntaxException {
        return new URIish(getCloneUriText());
    }

    private String getCloneUriText() {
        return cloneUrlEditText.getText().toString();
    }

    public File getTargetFolder() {
        return new File(gitDirEditText.getText().toString());
    }

    OnClickListener goCloneButtonListener = new OnClickListener() {
        public void onClick(View v) {
            URIish uri;
            try {
                uri = getCloneUri();
            } catch (URISyntaxException e) {
                Toast.makeText(v.getContext(), "bad dog", 10).show();
                return;
            }
            File checkoutLocation = getTargetFolder();
            boolean bare = bareRepoCheckbox.isChecked();
            try {
                launchClone(uri, checkoutLocation, bare);
            } catch (Exception e) {
                Toast.makeText(v.getContext(), "ARRG: " + e, 10).show();
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    };

    private void launchClone(URIish uri, File repoDir, boolean bare) throws IOException, URISyntaxException {
        if (!repoDir.mkdirs()) {
            String message = "Couldn't create " + repoDir;
            Toast.makeText(CloneLauncherActivity.this, message, LENGTH_LONG).show();
            throw new IOException(message);
        }

        startService(cloneOperationIntentFor(uri, repoDir, bare));
        Toast.makeText(getApplicationContext(), R.string.clone_launcher_farewell_due_to_clone_launched,
                LENGTH_SHORT).show();
        finish();
    }

    private File defaultRepoDirFor(URIish uri) {
        File reposDir = new File(Environment.getExternalStorageDirectory(), "git-repos");
        String suffix = bareRepoCheckbox.isChecked() ? DOT_GIT_EXT : "";
        return new File(reposDir, defaultRepoNameFor(uri) + suffix);
    }

    private String defaultRepoNameFor(URIish uri) {
        try {
            return uri.getHumanishName();
        } catch (Exception e) {
            return "repo";
        }
    }

}