gt: 		String inputPath = dxDexerInputFile.getText().toString();
context: package com.calsignlabs.apde;
  
  import android.annotation.SuppressLint;
  import android.app.ProgressDialog;
  import android.content.Context;
  import android.content.DialogInterface;
  import android.content.Intent;
  import android.content.res.Resources;
  import android.net.Uri;
  import android.os.Bundle;
  import android.os.Handler;
  import android.os.Message;
  
  import androidx.annotation.Nullable;
  import androidx.appcompat.app.AlertDialog;
  import androidx.appcompat.app.AppCompatActivity;
  import androidx.appcompat.widget.PopupMenu;
  import androidx.appcompat.widget.Toolbar;
  
  import android.os.ParcelFileDescriptor;
  import android.text.Editable;
  import android.text.Html;
  import android.text.TextWatcher;
  import android.text.method.LinkMovementMethod;
  import android.util.Log;
  import android.view.ContextThemeWrapper;
  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.widget.ArrayAdapter;
  import android.widget.Button;
  import android.widget.EditText;
  import android.widget.ImageButton;
  import android.widget.ListView;
  import android.widget.RelativeLayout;
  import android.widget.ScrollView;
  import android.widget.TextView;
  
  import com.calsignlabs.apde.contrib.ContributionManager;
  import com.calsignlabs.apde.contrib.Library;
  import com.calsignlabs.apde.support.CustomProgressDialog;
  import com.calsignlabs.apde.support.FileSelection;
  
  import java.io.File;
  import java.util.List;
  import java.util.regex.Matcher;
  import java.util.regex.Pattern;
  
  public class LibraryManagerActivity extends AppCompatActivity {
  	//All potential archive / compressed file extensions (hopefully) TODO Yes, I realize that the list currently contains one item.
  	public static String[] zipExtensions = {".zip"};
  	
  	//This is a number, that's all that matters
  	private static final int REQUEST_CHOOSER = 6283;
  	
  	//File select request codes for the DX Dexer tool
  	private static final int DX_DEXER_SELECT_INPUT_FILE = 7501;
  	private static final int DX_DEXER_SELECT_OUTPUT_FILE = 7502;
  	
  	private EditText dxDexerInputFile;
  	private EditText dxDexerOutputFile;
  	private Button dxDexerDexButton;
  	private TextView dxDexerErrorMessage;
  	
  	//Whether or not we are currently doing something
  	private boolean working = false;
  	
  	@Override
  	protected void onCreate(Bundle savedInstanceState) {
  		super.onCreate(savedInstanceState);
  		setContentView(R.layout.activity_library_manager);
  		
  		Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
  		toolbar.setBackgroundColor(getResources().getColor(R.color.bar_overlay));
  		setSupportActionBar(toolbar);
  		
  		getSupportActionBar().setHomeButtonEnabled(true);
  		getSupportActionBar().setDisplayHomeAsUpEnabled(true);
  		
  		getWindow().getDecorView().setBackgroundColor(getResources().getColor(R.color.activity_background));
  	}
  	
  	@Override
  	public void onResume() {
  		super.onResume();
  		
  		refreshLibraryList();
  	}
  	
  	@Override
  	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  		switch(requestCode) {
  		case REQUEST_CHOOSER:
  			if(resultCode == RESULT_OK) {
  				Uri uri = FileSelection.getSelectedUri(data);
  				String libraryName = ContributionManager.detectLibraryName(this, uri);
  				if (libraryName == null) {
  					alert(getResources().getString(R.string.library_manager_install_invalid_file_error_title), "Could not detect library name");
  				} else if (new Library(libraryName).getLibraryFolder(((APDE) getApplicationContext()).getLibrariesFolder()).exists()) {
  					alert(getResources().getString(R.string.library_manager_install_invalid_file_error_title), getResources().getString(R.string.library_manager_install_invalid_file_error_message_already_installed));
  				} else {
  					addZipLibrary(libraryName, uri);
  				}
  			}
  			break;
  		case DX_DEXER_SELECT_INPUT_FILE:
  			if(resultCode == RESULT_OK) {
  				Uri uri = FileSelection.getSelectedUri(data);
  				if (uri != null) {
  					dxDexerInputFile.setText(uri.toString());
  				}
  			}
  			
  			break;
  		case DX_DEXER_SELECT_OUTPUT_FILE:
  			if(resultCode == RESULT_OK) {
  				Uri uri = FileSelection.getSelectedUri(data);
  				if (uri != null) {
  					dxDexerOutputFile.setText(uri.toString());
  				}
  			}
  			
  			break;
  		}
  		
  		super.onActivityResult(requestCode, resultCode, data);
  	}
  	
  	private void alert(String title, String message) {
  		AlertDialog.Builder builder = new AlertDialog.Builder(this);
  		
  		builder.setTitle(title);
  		builder.setMessage(message);
  		
  		builder.setCancelable(true);
  		builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
  			@Override
  			public void onClick(DialogInterface dialog, int which) {}
  		});
  		
  		builder.create().show();
  	}
  	
  	public static boolean isZipExtension(String ext) {
  		for(String zip : zipExtensions) {
  			if(zip.equals(ext)) {
  				return true;
  			}
  		}
  		
  		return false;
  	}
  	
  	@Override
  	public boolean onCreateOptionsMenu(Menu menu) {
  		// Inflate the menu; this adds items to the action bar if it is present.
  		getMenuInflater().inflate(R.menu.library_manager, menu);
  		return true;
  	}
  	
  	@Override
      public boolean onOptionsItemSelected(MenuItem item) {
          switch(item.getItemId()) {
              case android.R.id.home:
              	finish();
                  return true;
              case R.id.action_install_zip_library:
              	launchInstallZipLibrary();
              	return true;
              case R.id.action_get_libraries:
              	launchGetLibraries();
              	return true;
              case R.id.action_settings:
              	launchSettings();
              	return true;
              case R.id.action_dx_dexer_tool:
              	launchDexerTool();
              	return true;
              default:
                  return super.onOptionsItemSelected(item);
          }
      }
  	
  	private void launchInstallZipLibrary() {
  		Intent intent = FileSelection.createFileSelectorIntent(false,
  				new String[] {"application/zip", "application/octet-stream", "application/x-zip-compressed", "multipart/x-zip"});
  		startActivityForResult(intent, REQUEST_CHOOSER);
  	}
  	
  	private void launchGetLibraries() {
  		//Open a browser to Processing's libraries page so that the user can download libraries manually
  		//This is until we figure out what to do about downloading and installing libraries directly
  		
  		Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://processing.org/reference/libraries/"));
  		startActivity(browserIntent);
  	}
  	
  	@SuppressLint("InlinedApi")
  	private void launchDexerTool() {
  		//Show a DX Dexer dialog
  		
  		AlertDialog.Builder builder = new AlertDialog.Builder(this);
  		builder.setTitle(R.string.library_manager_menu_dx_dexer);
  		
  		ScrollView layout;
  		
  		layout = (ScrollView) View.inflate(new ContextThemeWrapper(this, R.style.Theme_AppCompat_Dialog), R.layout.dx_dexer_tool, null);
  		
  		dxDexerInputFile = layout.findViewById(R.id.dx_dexer_input_file);
  		dxDexerOutputFile = layout.findViewById(R.id.dx_dexer_output_file);
  		
  		dxDexerErrorMessage = layout.findViewById(R.id.dx_dexer_error_message);
  		
  		layout.findViewById(R.id.dx_dexer_input_file_select).setOnClickListener(v -> {
  			Intent intent = FileSelection.createFileSelectorIntent(false,
  					new String[] {"application/java-archive"});
  			startActivityForResult(intent, DX_DEXER_SELECT_INPUT_FILE);
  		});
  		
  		layout.findViewById(R.id.dx_dexer_output_file_select).setOnClickListener(v -> {
  			String inputFilename = null;
  			Uri uri = FileSelection.uriStringOrPathToUri(dxDexerInputFile.getText().toString());
  			if (uri != null) {
  				inputFilename = FileSelection.uriToFilename(LibraryManagerActivity.this, uri);
  			}
  			if (inputFilename == null || inputFilename.equals("")) {
  				inputFilename = "output";
  			}
  			String outputFilename;
  			if (inputFilename.endsWith(".jar")) {
  				outputFilename = inputFilename.substring(0, inputFilename.length() - 4) + "-dex.jar";
  			} else {
  				outputFilename = inputFilename + "-dex.jar";
  			}
  			
  			Intent intent = FileSelection.createFileCreatorIntent(outputFilename);
  			startActivityForResult(intent, DX_DEXER_SELECT_OUTPUT_FILE);
  		});
  		
  		builder.setView(layout);
  		
  		builder.setPositiveButton(R.string.dx_dexer_dex_button, (dialog, which) -> dxDexerRun());
  		
  		builder.setNegativeButton(R.string.cancel, (dialog, which) -> {});
  		
  		final AlertDialog dialog = builder.create();
  		dialog.show();
  		
  		dxDexerDexButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
  		dxDexerDexButton.setEnabled(false);
  		
  		dxDexerInputFile.addTextChangedListener(new TextWatcher() {
  			@Override
  			public void afterTextChanged(Editable arg0) {
  				validateDxDexerFiles();
  			}
  			
  			@Override
  			public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {}
  			
  			@Override
  			public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {}
  		});
  		
  		dxDexerOutputFile.addTextChangedListener(new TextWatcher() {
  			@Override
  			public void afterTextChanged(Editable arg0) {
  				validateDxDexerFiles();
  			}
  			
  			@Override
  			public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {}
  			
  			@Override
  			public void onTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) {}
  		});
  		
  		dxDexerDexButton.setOnClickListener(new Button.OnClickListener() {
  			@Override
  			public void onClick(View v) {
  				dialog.dismiss();
  				dxDexerRun();
  			}
  		});
  		
  		validateDxDexerFiles();
  	}
  	
  	private void validateDxDexerFiles() {
  		dxDexerDexButton.setEnabled(false);
  		
  