package com.rackspacecloud.android; import java.util.ArrayList; import java.util.Arrays; import org.apache.http.HttpResponse; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; 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.EditText; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import com.rackspace.cloud.android.R; import com.rackspace.cloud.files.api.client.Container; import com.rackspace.cloud.files.api.client.ContainerManager; import com.rackspace.cloud.files.api.client.ContainerObjectManager; import com.rackspace.cloud.files.api.client.ContainerObjects; import com.rackspace.cloud.servers.api.client.CloudServersException; import com.rackspace.cloud.servers.api.client.http.HttpBundle; /** * * @author Phillip Toohill * */ public class ContainerObjectsActivity extends CloudListActivity { private static final int deleteContainer = 0; private static final int deleteFolder = 1; private ContainerObjects[] files; private static Container container; public String LOG = "viewFilesActivity"; private String cdnEnabledIs; public Object megaBytes; public Object kiloBytes; public int bConver = 1048576; public int kbConver = 1024; private boolean loadingFiles; private String currentPath; private AndroidCloudApplication app; private AddObjectListenerTask task; private DeleteObjectListenerTask deleteObjTask; private DeleteContainerListenerTask deleteContainerTask; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); trackPageView(GoogleAnalytics.PAGE_FOLDER); container = (Container) this.getIntent().getExtras().get("container"); if (container.isCdnEnabled() == true) { cdnEnabledIs = "true"; } else { cdnEnabledIs = "false"; } restoreState(savedInstanceState); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); //files stores all the files in the container outState.putSerializable("container", files); //current path represents where you have "navigated" to outState.putString("path", currentPath); outState.putBoolean("loadingFiles", loadingFiles); } protected void restoreState(Bundle state) { super.restoreState(state); /* * need reference to the app so you can access curDirFiles * as well as processing status */ app = (AndroidCloudApplication)this.getApplication(); if(state != null){ if(state.containsKey("path")){ currentPath = state.getString("path"); } else{ currentPath = ""; } if(state.containsKey("loadingFiles") && state.getBoolean("loadingFiles")){ loadFiles(); } else if(state.containsKey("container")){ files = (ContainerObjects[]) state.getSerializable("container"); if (app.getCurFiles() == null || app.getCurFiles().size() == 0) { displayNoFilesCell(); } else { getListView().setDividerHeight(1); // restore divider lines setListAdapter(new FileAdapter()); } } } else { currentPath = ""; loadFiles(); } /* * if the app is process when we enter the activity * we must listen for the new curDirFiles list */ if(app.isAddingObject()){ task = new AddObjectListenerTask(); task.execute(); } if(app.isDeletingObject()){ displayNoFilesCell(); deleteObjTask = new DeleteObjectListenerTask(); deleteObjTask.execute(); } if(app.isDeletingContainer()){ displayNoFilesCell(); deleteContainerTask = new DeleteContainerListenerTask(); deleteContainerTask.execute(); } } @Override protected void onStop(){ super.onStop(); /* * Need to stop running listener task * if we exit */ if(task != null){ task.cancel(true); } if(deleteObjTask != null){ deleteObjTask.cancel(true); } if(deleteContainerTask != null){ deleteContainerTask.cancel(true); } } /* * overriding back button press, because we are not actually changing * activities when we navigate the file structure */ public void onBackPressed() { if(currentPath.equals("")){ finish(); } else{ goUpDirectory(); } } /* * go to the current directory's parent and display that data */ private void goUpDirectory(){ currentPath = currentPath.substring(0, currentPath.substring(0, currentPath.length()-2).lastIndexOf("/")+1); loadCurrentDirectoryFiles(); displayCurrentFiles(); } /* * load all file that are in the container */ private void loadFiles() { //displayLoadingCell(); new LoadFilesTask().execute(); } /* load only the files that should display for the * current directory in the curDirFiles[] */ private void loadCurrentDirectoryFiles(){ ArrayList<ContainerObjects> curFiles = new ArrayList<ContainerObjects>(); if(files != null){ for(int i = 0 ; i < files.length; i ++){ if(fileBelongsInDir(files[i])){ curFiles.add(files[i]); } } app.setCurFiles(curFiles); } } /* * determines if a file should be displayed in current * directory */ private Boolean fileBelongsInDir(ContainerObjects obj){ String objPath = obj.getCName(); if(!objPath.startsWith(currentPath)){ return false; } else{ objPath = objPath.substring(currentPath.length()); return !objPath.contains("/"); } } /* * loads all the files that are in the container * into one array */ private void setFileList(ArrayList<ContainerObjects> files) { if (files == null) { files = new ArrayList<ContainerObjects>(); } String[] fileNames = new String[files.size()]; this.files = new ContainerObjects[files.size()]; if (files != null) { for (int i = 0; i < files.size(); i++) { ContainerObjects file = files.get(i); this.files[i] = file; fileNames[i] = file.getName(); } } } private void displayCurrentFiles(){ if (app.getCurFiles().size() == 0) { displayNoFilesCell(); } else { ArrayList<ContainerObjects> tempList = new ArrayList<ContainerObjects>(); for(int i = 0; i < app.getCurFiles().size(); i++){ tempList.add(app.getCurFiles().get(i)); } getListView().setDividerHeight(1); // restore divider lines setListAdapter(new FileAdapter()); } } /* * display a different empty page depending * of if you are at top of container or * in a folder */ private void displayNoFilesCell() { String a[] = new String[1]; if(currentPath.equals("")){ a[0] = "Empty Container"; setListAdapter(new ArrayAdapter<String>(this, R.layout.noobjectscell, R.id.no_files_label, a)); } else{ a[0] = "No Files"; setListAdapter(new ArrayAdapter<String>(this, R.layout.nofilescell, R.id.no_files_label, a)); } getListView().setTextFilterEnabled(true); getListView().setDividerHeight(0); // hide the dividers so it won't look // like a list row getListView().setItemsCanFocus(false); } /* just get the last part of the filename * so the entire file path does not show */ private String getShortName(String longName){ String s = longName; if(!s.contains("/")){ return s; } else { return s.substring(s.lastIndexOf('/')+1); } } /* * removed a specified object from the array of * all the files in the container */ private void removeFromList(String path){ ArrayList<ContainerObjects> temp = new ArrayList<ContainerObjects>(Arrays.asList(files)); for(int i = 0; i < files.length; i++){ if(files[i].getCName().equals(path.substring(0, path.length()-1))){ temp.remove(i); } } files = new ContainerObjects[temp.size()]; for(int i = 0; i < temp.size(); i++){ files[i] = temp.get(i); } } protected void onListItemClick(ListView l, View v, int position, long id) { if (app.getCurFiles() != null && app.getCurFiles().size() > 0) { Intent viewIntent; if(app.getCurFiles().get(position).getContentType().equals("application/directory")){ currentPath = app.getCurFiles().get(position).getCName() + "/"; loadCurrentDirectoryFiles(); displayCurrentFiles(); } else{ viewIntent = new Intent(this, ContainerObjectDetails.class); viewIntent.putExtra("container", app.getCurFiles().get(position)); viewIntent.putExtra("cdnUrl", container.getCdnUrl()); viewIntent.putExtra("containerNames", container.getName()); viewIntent.putExtra("isCdnEnabled", cdnEnabledIs); startActivityForResult(viewIntent, 55); // arbitrary number; never // used again } } } /* * Create the Menu options */ @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.view_container_object_list_menu, menu); return true; } @Override /* * option performed for delete depends on if you * are at the top of a container or in a folder */ public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.delete_container: if(currentPath.equals("")){ showDialog(deleteContainer); } else{ showDialog(deleteFolder); } return true; case R.id.enable_cdn: Intent viewIntent1 = new Intent(this, EnableCDNActivity.class); viewIntent1.putExtra("Cname", container.getName()); startActivityForResult(viewIntent1, 56); return true; case R.id.refresh: loadFiles(); return true; case R.id.add_folder: showDialog(R.id.add_folder); return true; case R.id.add_file: Intent viewIntent2 = new Intent(this, AddFileActivity.class); viewIntent2.putExtra("Cname", container.getName()); viewIntent2.putExtra("curPath", currentPath); startActivityForResult(viewIntent2, 56); return true; } return false; } @Override protected Dialog onCreateDialog(int id) { switch (id) { case deleteContainer: if(app.getCurFiles().size() == 0){ return new AlertDialog.Builder(ContainerObjectsActivity.this) .setIcon(R.drawable.alert_dialog_icon) .setTitle("Delete Container") .setMessage( "Are you sure you want to delete this Container?") .setPositiveButton("Delete Container", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // User clicked OK so do some stuff trackEvent(GoogleAnalytics.CATEGORY_CONTAINER, GoogleAnalytics.EVENT_DELETE, "", -1); new DeleteContainerTask() .execute(currentPath); } }) .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // User clicked Cancel so do some stuff } }).create(); } else{ return new AlertDialog.Builder(ContainerObjectsActivity.this) .setIcon(R.drawable.alert_dialog_icon) .setTitle("Delete Container") .setMessage("Container must be empty to delete") .setNegativeButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // User clicked Cancel so do some stuff } }).create(); } case deleteFolder: if(app.getCurFiles().size() == 0){ return new AlertDialog.Builder(ContainerObjectsActivity.this) .setIcon(R.drawable.alert_dialog_icon) .setTitle("Delete Folder") .setMessage( "Are you sure you want to delete this Folder?") .setPositiveButton("Delete Folder", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // User clicked OK so do some stuff new DeleteObjectTask().execute(); } }) .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // User clicked Cancel so do some stuff } }).create(); } else{ return new AlertDialog.Builder(ContainerObjectsActivity.this) .setIcon(R.drawable.alert_dialog_icon) .setTitle("Delete Folder") .setMessage( "Folder must be empty to delete") .setNegativeButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // User clicked Cancel so do some stuff } }).create(); } case R.id.add_folder: final EditText input = new EditText(this); return new AlertDialog.Builder(ContainerObjectsActivity.this) .setIcon(R.drawable.alert_dialog_icon) .setView(input) .setTitle("Add Folder") .setMessage("Enter new name for folder: ") .setPositiveButton("Add", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { //User clicked OK so do some stuff String[] info = {input.getText().toString(), "application/directory"}; new AddFolderTask().execute(info); } }) .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { // User clicked Cancel so do some stuff } }) .create(); } return null; } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK && requestCode == 56) { // a sub-activity kicked back, so we want to refresh the server list loadFiles(); } // deleted file so need to update the list if (requestCode == 55 && resultCode == 99) { loadFiles(); } } class FileAdapter extends ArrayAdapter<ContainerObjects> { FileAdapter() { super(ContainerObjectsActivity.this, R.layout.listcontainerobjectcell, app.getCurFiles()); } public View getView(int position, View convertView, ViewGroup parent) { ContainerObjects file = app.getCurFiles().get(position); LayoutInflater inflater = getLayoutInflater(); View row = inflater.inflate(R.layout.listcontainerobjectcell, parent, false); TextView label = (TextView) row.findViewById(R.id.label); label.setText(getShortName(file.getCName())); ImageView objectImage = (ImageView) row.findViewById(R.id.file_type_image); if(file.getContentType().equals("application/directory")){ objectImage.setImageResource(R.drawable.folder); } else { objectImage.setImageResource(R.drawable.file); } if (file.getBytes() >= bConver) { megaBytes = Math.abs(file.getBytes() / bConver + 0.2); TextView sublabel = (TextView) row.findViewById(R.id.sublabel); sublabel.setText(megaBytes + " MB"); } else if (file.getBytes() >= kbConver) { kiloBytes = Math.abs(file.getBytes() / kbConver + 0.2); TextView sublabel = (TextView) row.findViewById(R.id.sublabel); sublabel.setText(kiloBytes + " KB"); } else { TextView sublabel = (TextView) row.findViewById(R.id.sublabel); sublabel.setText(file.getBytes() + " B"); } return (row); } } private class LoadFilesTask extends AsyncTask<String, Void, ArrayList<ContainerObjects>> { private CloudServersException exception; protected void onPreExecute(){ showDialog(); loadingFiles = true; } @Override protected ArrayList<ContainerObjects> doInBackground(String... path) { ArrayList<ContainerObjects> files = null; try { files = (new ContainerObjectManager(getContext())).createList(true, container.getName()); } catch (CloudServersException e) { exception = e; e.printStackTrace(); } return files; } @Override protected void onPostExecute(ArrayList<ContainerObjects> result) { loadingFiles = false; hideDialog(); if (exception != null) { showAlert("Error", exception.getMessage()); } setFileList(result); loadCurrentDirectoryFiles(); displayCurrentFiles(); } } private class AddFolderTask extends AsyncTask<String, Void, HttpBundle> { private CloudServersException exception; @Override protected void onPreExecute(){ showDialog(); app.setAddingObject(true); task = new AddObjectListenerTask(); task.execute(); } @Override protected HttpBundle doInBackground(String... data) { HttpBundle bundle = null; try { bundle = (new ContainerObjectManager(getContext())).addObject(container.getName(), currentPath, data[0], data[1]); } catch (CloudServersException e) { exception = e; } return bundle; } @Override protected void onPostExecute(HttpBundle bundle) { app.setAddingObject(false); hideDialog(); if (bundle != null) { //Otherwise a listener later on shows an error message HttpResponse response = bundle.getResponse(); if (response != null) { int statusCode = response.getStatusLine().getStatusCode(); if (statusCode == 201) { setResult(Activity.RESULT_OK); //loading the new files is done by ListenerTask } else { CloudServersException cse = parseCloudServersException(response); if ("".equals(cse.getMessage())) { showError("There was a problem deleting your folder.", bundle); } else { showError("There was a problem deleting your folder: " + cse.getMessage(), bundle); } } } else if (exception != null) { showError("There was a problem deleting your folder: " + exception.getMessage(), bundle); } } } } private class DeleteObjectTask extends AsyncTask<Void, Void, HttpBundle> { private CloudServersException exception; @Override protected void onPreExecute(){ showDialog(); app.setDeleteingObject(true); deleteObjTask = new DeleteObjectListenerTask(); deleteObjTask.execute(); } @Override protected HttpBundle doInBackground(Void... arg0) { HttpBundle bundle = null; try { //subtring because the current directory contains a "/" at the end of the string bundle = (new ContainerObjectManager(getContext())).deleteObject(container.getName(), currentPath.substring(0, currentPath.length()-1)); } catch (CloudServersException e) { exception = e; } return bundle; } @Override protected void onPostExecute(HttpBundle bundle) { app.setDeleteingObject(false); hideDialog(); if (bundle != null) { //Otherwise a listener later on shows an error message HttpResponse response = bundle.getResponse(); if (response != null) { int statusCode = response.getStatusLine().getStatusCode(); if (statusCode == 409) { showAlert("Error", "Folder must be empty in order to delete"); } if (statusCode == 204) { setResult(Activity.RESULT_OK); } else { CloudServersException cse = parseCloudServersException(response); if ("".equals(cse.getMessage())) { showError("There was a problem deleting your folder.", bundle); } else { showError("There was a problem deleting your folder: " + cse.getMessage(), bundle); } } } else if (exception != null) { showError("There was a problem deleting your folder: " + exception.getMessage(), bundle); } } } } private class DeleteContainerTask extends AsyncTask<String, Void, HttpBundle> { private CloudServersException exception; @Override protected void onPreExecute(){ showDialog(); app.setDeletingContainer(true); deleteContainerTask = new DeleteContainerListenerTask(); deleteContainerTask.execute(); } @Override protected HttpBundle doInBackground(String... object) { HttpBundle bundle = null; try { bundle = (new ContainerManager(getContext())).delete(container.getName()); } catch (CloudServersException e) { exception = e; } return bundle; } @Override protected void onPostExecute(HttpBundle bundle) { hideDialog(); app.setDeletingContainer(false); if (bundle != null) { //Otherwise a listener later on shows an error message HttpResponse response = bundle.getResponse(); if (response != null) { int statusCode = response.getStatusLine().getStatusCode(); if (statusCode == 409) { showError("Container must be empty in order to delete", bundle); } if (statusCode == 204) { setResult(Activity.RESULT_OK); } else { CloudServersException cse = parseCloudServersException(response); if ("".equals(cse.getMessage())) { showError("There was a problem deleting your container.", bundle); } else { showError("There was a problem deleting your container: " + cse.getMessage(), bundle); } } } else if (exception != null) { showError("There was a problem deleting your server: " + exception.getMessage(), bundle); } } } } /* * listens for the application to change isProcessing * listens so activity knows when it should display * the new curDirFiles */ private class AddObjectListenerTask extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... arg1) { while(app.isAddingObject()){ // wait for process to finish // or have it be canceled if(task.isCancelled()){ return null; } } return null; } /* * when no longer processing, time to load * the new files */ @Override protected void onPostExecute(Void arg1) { hideDialog(); loadFiles(); } } private class DeleteObjectListenerTask extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... arg1) { while(app.isDeletingObject()){ // wait for process to finish // or have it be canceled if(deleteObjTask.isCancelled()){ return null; } } return null; } /* * when no longer processing, time to load * the new files */ @Override protected void onPostExecute(Void arg1) { hideDialog(); removeFromList(currentPath); goUpDirectory(); } } private class DeleteContainerListenerTask extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... arg1) { while(app.isDeletingContainer()){ // wait for process to finish // or have it be canceled if(deleteContainerTask.isCancelled()){ return null; } } return null; } /* * when no longer processing, time to load * the new files */ @Override protected void onPostExecute(Void arg1) { hideDialog(); setResult(RESULT_OK); finish(); } } }