Permalink
Browse files

Add 'Open Repo' option to allow opening Git repos already on device

This is a follow on to issue #36, which allowed Agit to remember repos
located in non-default locations.

The file browsing is provided by OI File Manager, which the use is prompted
to install if it's not already present.
  • Loading branch information...
1 parent d9d7019 commit 645dc3224f2484ccebb5a7b1d17936a43d75d472 @rtyley committed Jun 10, 2012
@@ -6,6 +6,11 @@
android:title="@string/clone_menu_option">
</item>
<item
+ android:id="@+id/open_repo"
+ android:title="@string/open_menu_option"
+ android:showAsAction="never">
+ </item>
+ <item
android:id="@+id/about_app"
android:showAsAction="never"
android:title="@string/about_app_menu_option">
@@ -32,6 +32,7 @@
<string name="execute_clone_button_label">Clone!</string>
+ <string name="open_menu_option">Open...</string>
<string name="delete_menu_option">Delete</string>
<string name="delete_repo_menu_option">Delete Repo</string>
<string name="delete_tag_menu_option">Delete Tag</string>
@@ -51,13 +52,19 @@
<string name="ask_host_key_ok_ticker">"Host %1$s SSH fingerprint..."</string>
<string name="ask_host_key_ok">"%1$s has key fingerprint: %2$s OK?"</string>
+ <string name="open_git_repository">"Open Git repository"</string>
+ <string name="install_file_manager">"To open a Git repository already stored on your device, install OI File Manager to enable file browsing."</string>
+ <string name="can_not_open_non_git_folder">"Can't open %1$s - doesn't appear to be a Git repository?"</string>
+ <string name="market_button">"Google Play..."</string>
+
<string name="diff_seekbar_before">BEFORE</string>
<string name="diff_seekbar_after">AFTER</string>
<!-- Button label to answer "Yes" to a yes/no prompt -->
<string name="button_yes">"Yes"</string>
<!-- Button label to answer "No" to a yes/no prompt -->
<string name="button_no">"No"</string>
+ <string name="button_cancel">"Cancel"</string>
<string name="repo_deletion_dialog_confirmation_title">"Delete repo..."</string>
<string name="repo_deletion_dialog_confirmation_message">"Are you sure want to delete this Git repository?"</string>
@@ -1,19 +1,38 @@
package com.madgag.agit;
-
+import static android.text.Html.fromHtml;
+import static android.widget.Toast.LENGTH_LONG;
+import static com.madgag.agit.R.string.can_not_open_non_git_folder;
+import static com.madgag.agit.R.string.install_file_manager;
+import static com.madgag.agit.R.string.open_git_repository;
+import static com.madgag.agit.RepositoryViewerActivity.manageRepoIntent;
import static com.madgag.agit.sync.AccountAuthenticatorService.addAccount;
+import static com.madgag.android.ToastUtil.code;
import static com.madgag.android.jgit.HarmonyFixInflater.checkHarmoniousRepose;
+import static com.madgag.android.util.store.InstallAppDialogFragment.isIntentAvailable;
+import static org.eclipse.jgit.lib.RepositoryCache.FileKey.resolve;
import android.content.Intent;
+import android.net.Uri;
import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.text.Spanned;
import android.util.Log;
+import android.widget.Toast;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
import com.github.rtyley.android.sherlock.roboguice.activity.RoboSherlockFragmentActivity;
+import com.madgag.android.util.store.InstallAppDialogFragment;
+
+import java.io.File;
+
+import org.eclipse.jgit.util.FS;
public class DashboardActivity extends RoboSherlockFragmentActivity {
private static final String TAG = "DashboardActivity";
+ public static final String PICK_DIRECTORY_INTENT = "org.openintents.action.PICK_DIRECTORY";
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -40,10 +59,61 @@ public boolean onOptionsItemSelected(MenuItem item) {
case R.id.clone:
startActivity(new Intent(this, CloneLauncherActivity.class));
return true;
+ case R.id.open_repo:
+ if (isIntentAvailable(this, PICK_DIRECTORY_INTENT)) {
+ Intent intent = new Intent(PICK_DIRECTORY_INTENT);
+ intent.putExtra("org.openintents.extra.TITLE", "Select Git repository...");
+ startActivityForResult(intent, 0);
+ } else {
+ askUserToInstallFileManager();
+ }
+
+ return true;
case R.id.about_app:
startActivity(new Intent(this, AboutActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
}
+
+ void askUserToInstallFileManager() {
+ // DialogFragment.show() will take care of adding the fragment
+ // in a transaction. We also want to remove any currently showing
+ // dialog, so make our own transaction and take care of that here.
+ FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
+ Fragment prev = getSupportFragmentManager().findFragmentByTag("dialog");
+ if (prev != null) {
+ ft.remove(prev);
+ }
+ ft.addToBackStack(null);
+
+ InstallAppDialogFragment.newInstance(R.drawable.icon, open_git_repository, install_file_manager,
+ "org.openintents.filemanager").show(ft, "dialog");
+ }
+
+
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode == RESULT_OK) {
+ File repoDir = getFile(data.getData());
+ File gitdir = resolve(repoDir, FS.detect());
+ if (gitdir == null) {
+ Spanned messageHtml = fromHtml(getString(can_not_open_non_git_folder, code(repoDir.getAbsolutePath())));
+ Toast.makeText(this, messageHtml, LENGTH_LONG).show();
+ } else {
+ startActivity(manageRepoIntent(gitdir));
+ }
+ }
+ }
+
+
+ public static File getFile(Uri uri) {
+ if (uri != null) {
+ String filepath = uri.getPath();
+ if (filepath != null) {
+ return new File(filepath);
+ }
+ }
+ return null;
+ }
+
}
@@ -49,6 +49,7 @@
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import com.google.inject.Inject;
+import com.madgag.agit.db.ReposDataSource;
import com.madgag.agit.operation.lifecycle.CasualShortTermLifetime;
import com.madgag.agit.operations.GitAsyncTaskFactory;
import com.madgag.agit.operations.GitOperationExecutor;
@@ -74,7 +75,6 @@ public static Intent manageRepoIntent(File gitdir) {
return new GitIntentBuilder("repo.VIEW").gitdir(gitdir).toIntent();
}
- private final static int DELETE_ID = Menu.FIRST;
private final int DELETION_IN_PROGRESS_DIALOG = 3;
private final int DELETION_CONFIRMATION_DIALOG = DELETION_IN_PROGRESS_DIALOG + 1;
@@ -98,6 +98,9 @@ public void onClick(DialogInterface dialogInterface, int which) {
@Inject
GitOperationExecutor gitOperationExecutor;
+ @Inject
+ ReposDataSource reposDataSource;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -108,6 +111,8 @@ public void onCreate(Bundle savedInstanceState) {
actionBar.setDisplayHomeAsUpEnabled(true);
listView.setOnItemClickListener(summaryAdapter.getOnItemClickListener());
+
+ reposDataSource.registerRepo(gitdir());
}
@Override
@@ -9,6 +9,7 @@
import static com.madgag.agit.util.DigestUtils.encodeHex;
import static com.madgag.agit.util.DigestUtils.md5;
import static com.madgag.agit.views.TextUtil.centered;
+import static com.madgag.android.ToastUtil.code;
import static java.lang.Boolean.TRUE;
import android.app.Application;
@@ -57,10 +58,6 @@ private int userCheckKey(String host, byte[] key) {
}
}
- private String code(String s) {
- return "<b><tt>" + s + "</tt></b>";
- }
-
public void add(HostKey hostkey, UserInfo ui) {
}
@@ -0,0 +1,11 @@
+package com.madgag.android;
+
+public class ToastUtil {
+
+ /**
+ * Styling for an 'code'-like bit of text in a toast.
+ */
+ public static String code(String s) {
+ return "<b><tt>" + s + "</tt></b>";
+ }
+}
@@ -0,0 +1,76 @@
+package com.madgag.android.util.store;
+
+import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
+import static com.madgag.agit.R.string.button_cancel;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.app.FragmentActivity;
+
+import com.madgag.agit.R;
+
+
+public class InstallAppDialogFragment extends DialogFragment {
+
+ private static final String ICON = "icon", TITLE = "title", MESSAGE = "message", APP_ID = "appId";
+
+ public static InstallAppDialogFragment newInstance(int icon, int title, int message, String appId) {
+ InstallAppDialogFragment f = new InstallAppDialogFragment();
+
+ Bundle args = new Bundle();
+ args.putInt(ICON, icon);
+ args.putInt(TITLE, title);
+ args.putInt(MESSAGE, message);
+ args.putString(APP_ID, appId);
+ f.setArguments(args);
+
+ return f;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Bundle args = getArguments();
+ FragmentActivity activity = getActivity();
+ return new AlertDialog.Builder(activity)
+ .setIcon(args.getInt(ICON))
+ .setTitle(args.getInt(TITLE))
+ .setMessage(args.getInt(MESSAGE))
+ .setPositiveButton(activity.getString(R.string.market_button),
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ startActivity(marketDownloadIntentFor(args.getString(APP_ID)));
+ }
+ }
+ ).setNegativeButton(button_cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ }
+ })
+ .create();
+ }
+
+
+ private static final String MARKET_PACKAGE_DETAILS_PREFIX = "market://details?id=";
+
+ private static Intent marketDownloadIntentFor(String appPackageId) {
+ return new Intent(Intent.ACTION_VIEW, Uri.parse(MARKET_PACKAGE_DETAILS_PREFIX + appPackageId));
+ }
+
+ /**
+ * Indicates whether the specified action can be used as an intent.
+ * <p/>
+ * Adapted from http://android-developers.blogspot.com/2009/01/can-i-use-this-intent.html
+ */
+ public static boolean isIntentAvailable(Context context, String action) {
+ return isIntentAvailable(context, new Intent(action));
+ }
+
+ private static boolean isIntentAvailable(Context context, Intent intent) {
+ return !context.getPackageManager().queryIntentActivities(intent, MATCH_DEFAULT_ONLY).isEmpty();
+ }
+
+}

0 comments on commit 645dc32

Please sign in to comment.