diff --git a/shell/android-studio/reicast/src/main/AndroidManifest.xml b/shell/android-studio/reicast/src/main/AndroidManifest.xml index 932c166f2..8300bd58c 100644 --- a/shell/android-studio/reicast/src/main/AndroidManifest.xml +++ b/shell/android-studio/reicast/src/main/AndroidManifest.xml @@ -101,11 +101,11 @@ android:theme="@android:style/Theme.NoTitleBar.Fullscreen" > + android:launchMode="singleTask"> - + diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/dropbox/ClientFactory.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/dropbox/ClientFactory.java new file mode 100644 index 000000000..c944340fd --- /dev/null +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/dropbox/ClientFactory.java @@ -0,0 +1,32 @@ +package com.reicast.emulator.dropbox; + +import com.dropbox.core.DbxRequestConfig; +import com.dropbox.core.http.StandardHttpRequestor; +import com.dropbox.core.v2.DbxClientV2; + +/** + * Singleton instance of {@link DbxClientV2} and friends + */ +public class ClientFactory { + + private static DbxClientV2 sDbxClient; + + public static void init(String accessToken) { + if (sDbxClient == null) { + StandardHttpRequestor requestor = new StandardHttpRequestor( + StandardHttpRequestor.Config.DEFAULT_INSTANCE); + DbxRequestConfig requestConfig = DbxRequestConfig + .newBuilder("ReicastCloud") + .withHttpRequestor(requestor).build(); + + sDbxClient = new DbxClientV2(requestConfig, accessToken); + } + } + + public static DbxClientV2 getClient() { + if (sDbxClient == null) { + throw new IllegalStateException("Client not initialized."); + } + return sDbxClient; + } +} diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/dropbox/CloudFragment.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/dropbox/CloudFragment.java index 1436c2d51..af26833d0 100644 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/dropbox/CloudFragment.java +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/dropbox/CloudFragment.java @@ -27,9 +27,7 @@ import android.widget.Button; import android.widget.Toast; import com.dropbox.core.DbxException; -import com.dropbox.core.DbxRequestConfig; import com.dropbox.core.android.Auth; -import com.dropbox.core.http.StandardHttpRequestor; import com.dropbox.core.v2.DbxClientV2; import com.dropbox.core.v2.files.FileMetadata; import com.dropbox.core.v2.files.ListFolderResult; @@ -45,13 +43,13 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.ref.WeakReference; public class CloudFragment extends Fragment { - final static private String APP_KEY = "7d7tw1t57sbzrj5"; - final static private String APP_SECRET = "5xxqa2uctousyi2"; - private String mPath = ""; // Dropbox Path + private final static String APP_KEY = "lowa9ps6h5k7zbo"; + private String mPath = ""; // Dropbox folder Button uploadBtn; Button downloadBtn; @@ -75,20 +73,26 @@ public class CloudFragment extends Fragment { Environment.getExternalStorageDirectory().getAbsolutePath()); buttonListener(); confirmDialog = new AlertDialog.Builder(getActivity()); -// Auth.startOAuth2Authentication(getActivity(), APP_KEY); + } + + @Override + public void onResume() { + super.onResume(); + SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); String accessToken = mPrefs.getString("access-token", null); - if (accessToken != null) { - DropboxClientFactory.init(accessToken); - } else { + if (accessToken == null) { accessToken = Auth.getOAuth2Token(); if (accessToken != null) { mPrefs.edit().putString("access-token", accessToken).apply(); - DropboxClientFactory.init(accessToken); + ClientFactory.init(accessToken); } + } else { + ClientFactory.init(accessToken); } } public void buttonListener() { + Auth.startOAuth2Authentication(getActivity(), APP_KEY); uploadBtn = (Button) getView().findViewById(R.id.uploadBtn); uploadBtn.setOnClickListener(new View.OnClickListener() { @Override @@ -96,7 +100,7 @@ public class CloudFragment extends Fragment { confirmDialog.setMessage(R.string.uploadWarning); confirmDialog.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { - getUriForFiles("Upload"); + getPathForFiles("Upload"); } }); confirmDialog.setNegativeButton(R.string.cancel, null); @@ -113,7 +117,7 @@ public class CloudFragment extends Fragment { confirmDialog.setMessage(R.string.downloadWarning); confirmDialog.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { - getUriForFiles("Download"); + getPathForFiles("Download"); } }); confirmDialog.setNegativeButton(R.string.cancel, null); @@ -122,6 +126,37 @@ public class CloudFragment extends Fragment { }); } + private void uploadFile(String filePath) { + final ProgressDialog dialog = new ProgressDialog(getActivity()); + dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); + dialog.setCancelable(false); + dialog.setMessage("Uploading"); + dialog.show(); + + try { + new UploadFileTask(getActivity(), ClientFactory.getClient(), new UploadFileTask.Callback() { + @Override + public void onUploadComplete(FileMetadata result) { + dialog.dismiss(); + } + + @Override + public void onError(Exception e) { + dialog.dismiss(); + + Log.e(getActivity().getLocalClassName(), "Failed to upload file.", e); + Toast.makeText(getActivity(), + "Failed to upload file", Toast.LENGTH_SHORT).show(); + } + }).execute(filePath, mPath); + } catch (IllegalStateException s) { + dialog.dismiss(); + Log.e(getActivity().getLocalClassName(), "Failed to upload file.", s); + Toast.makeText(getActivity(), + "Failed to upload file", Toast.LENGTH_SHORT).show(); + } + } + private void retrieveFiles(final String vmu) { final ProgressDialog dialog = new ProgressDialog(getActivity()); dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); @@ -130,7 +165,7 @@ public class CloudFragment extends Fragment { dialog.show(); try { - new ListFolderTask(DropboxClientFactory.getClient(), new ListFolderTask.Callback() { + new ListFolderTask(ClientFactory.getClient(), new ListFolderTask.Callback() { @Override public void onDataLoaded(ListFolderResult result) { dialog.dismiss(); @@ -160,37 +195,6 @@ public class CloudFragment extends Fragment { } } - private void uploadFile(String fileUri) { - final ProgressDialog dialog = new ProgressDialog(getActivity()); - dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); - dialog.setCancelable(false); - dialog.setMessage("Uploading"); - dialog.show(); - - try { - new UploadFileTask(getActivity(), DropboxClientFactory.getClient(), new UploadFileTask.Callback() { - @Override - public void onUploadComplete(FileMetadata result) { - dialog.dismiss(); - } - - @Override - public void onError(Exception e) { - dialog.dismiss(); - - Log.e(getActivity().getLocalClassName(), "Failed to upload file.", e); - Toast.makeText(getActivity(), - "Failed to upload file", Toast.LENGTH_SHORT).show(); - } - }).execute(fileUri, mPath); - } catch (IllegalStateException s) { - dialog.dismiss(); - Log.e(getActivity().getLocalClassName(), "Failed to upload file.", s); - Toast.makeText(getActivity(), - "Failed to upload file", Toast.LENGTH_SHORT).show(); - } - } - private void downloadFile(FileMetadata file) { final ProgressDialog dialog = new ProgressDialog(getActivity()); dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); @@ -199,7 +203,7 @@ public class CloudFragment extends Fragment { dialog.show(); try { - new DownloadFileTask(getActivity(), DropboxClientFactory.getClient(), new DownloadFileTask.Callback() { + new DownloadFileTask(this, ClientFactory.getClient(), new DownloadFileTask.Callback() { @Override public void onDownloadComplete(File result) { dialog.dismiss(); @@ -211,7 +215,7 @@ public class CloudFragment extends Fragment { Log.e(getActivity().getLocalClassName(), "Failed to download file.", e); Toast.makeText(getActivity(), - "Failed to download file", Toast.LENGTH_SHORT).show(); + "Failed to download file\n" + e, Toast.LENGTH_SHORT).show(); } }).execute(file); } catch (IllegalStateException s) { @@ -222,6 +226,58 @@ public class CloudFragment extends Fragment { } } + static class UploadFileTask extends AsyncTask { + + private WeakReference mContext; + private final DbxClientV2 mDbxClient; + private final Callback mCallback; + private Exception mException; + + public interface Callback { + void onUploadComplete(FileMetadata result); + void onError(Exception e); + } + + UploadFileTask(Context context, DbxClientV2 dbxClient, Callback callback) { + mContext = new WeakReference<>(context); + mDbxClient = dbxClient; + mCallback = callback; + } + + @Override + protected void onPostExecute(FileMetadata result) { + super.onPostExecute(result); + if (mException != null) { + mCallback.onError(mException); + } else if (result == null) { + mCallback.onError(null); + } else { + mCallback.onUploadComplete(result); + } + } + + @Override + protected FileMetadata doInBackground(String... params) { + File localFile = new File(params[0]); + String remoteFolderPath = params[1]; + + // Note - does not verify a valid dropbox file name + String remoteFileName = localFile.getName(); + + try { + InputStream inputStream = new FileInputStream(localFile); + return mDbxClient.files().uploadBuilder( + remoteFolderPath + "/" + remoteFileName) + .withMode(WriteMode.OVERWRITE) + .uploadAndFinish(inputStream); + } catch (DbxException | IOException e) { + mException = e; + } + + return null; + } + } + static class ListFolderTask extends AsyncTask { private final DbxClientV2 mDbxClient; @@ -233,7 +289,7 @@ public class CloudFragment extends Fragment { void onError(Exception e); } - public ListFolderTask(DbxClientV2 dbxClient, Callback callback) { + ListFolderTask(DbxClientV2 dbxClient, Callback callback) { mDbxClient = dbxClient; mCallback = callback; } @@ -261,65 +317,9 @@ public class CloudFragment extends Fragment { } } - static class UploadFileTask extends AsyncTask { - - private final Context mContext; - private final DbxClientV2 mDbxClient; - private final Callback mCallback; - private Exception mException; - - public interface Callback { - void onUploadComplete(FileMetadata result); - void onError(Exception e); - } - - UploadFileTask(Context context, DbxClientV2 dbxClient, Callback callback) { - mContext = context; - mDbxClient = dbxClient; - mCallback = callback; - } - - @Override - protected void onPostExecute(FileMetadata result) { - super.onPostExecute(result); - if (mException != null) { - mCallback.onError(mException); - } else if (result == null) { - mCallback.onError(null); - } else { - mCallback.onUploadComplete(result); - } - } - - @Override - protected FileMetadata doInBackground(String... params) { - String localUri = params[0]; - File localFile = UriHelpers.getFileForUri(mContext, Uri.parse(localUri)); - - if (localFile != null) { - String remoteFolderPath = params[1]; - - // Note - this is not ensuring the name is a valid dropbox file name - String remoteFileName = localFile.getName(); - - try { - InputStream inputStream = new FileInputStream(localFile); - return mDbxClient.files().uploadBuilder( - remoteFolderPath + "/" + remoteFileName) - .withMode(WriteMode.OVERWRITE) - .uploadAndFinish(inputStream); - } catch (DbxException | IOException e) { - mException = e; - } - } - - return null; - } - } - static class DownloadFileTask extends AsyncTask { - private final Context mContext; + private WeakReference mCloud; private final DbxClientV2 mDbxClient; private final Callback mCallback; private Exception mException; @@ -329,8 +329,8 @@ public class CloudFragment extends Fragment { void onError(Exception e); } - DownloadFileTask(Context context, DbxClientV2 dbxClient, Callback callback) { - mContext = context; + DownloadFileTask(CloudFragment context, DbxClientV2 dbxClient, Callback callback) { + mCloud = new WeakReference<>(context); mDbxClient = dbxClient; mCallback = callback; } @@ -349,8 +349,7 @@ public class CloudFragment extends Fragment { protected File doInBackground(FileMetadata... params) { FileMetadata metadata = params[0]; try { - File path = Environment.getExternalStoragePublicDirectory( - Environment.DIRECTORY_DOWNLOADS); + File path = new File(mCloud.get().home_directory); File file = new File(path, metadata.getName()); // Make sure the Downloads directory exists. @@ -371,7 +370,7 @@ public class CloudFragment extends Fragment { // Tell android about the file Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); intent.setData(Uri.fromFile(file)); - mContext.sendBroadcast(intent); + mCloud.get().getActivity().sendBroadcast(intent); return file; } catch (DbxException | IOException e) { @@ -382,39 +381,18 @@ public class CloudFragment extends Fragment { } } - static class DropboxClientFactory { - - private static DbxClientV2 sDbxClient; - - public static void init(String accessToken) { - if (sDbxClient == null) { - StandardHttpRequestor requestor = new StandardHttpRequestor( - StandardHttpRequestor.Config.DEFAULT_INSTANCE); - DbxRequestConfig requestConfig = DbxRequestConfig - .newBuilder("reicast.emulator") - .withHttpRequestor(requestor).build(); - sDbxClient = new DbxClientV2(requestConfig, accessToken); - } - } - - public static DbxClientV2 getClient() { - if (sDbxClient == null) { - throw new IllegalStateException("Client not initialized."); - } - return sDbxClient; - } - } - - private void getUriForFiles(String task) { + private void getPathForFiles(String task) { for (String vmu : vmus) { File vmuFile = new File(home_directory, vmu); if (task.equals("Download")) { if (vmuFile.exists()) createBackupOfVmu(vmuFile.getName()); - retrieveFiles(vmuFile.getName()); + else + retrieveFiles(vmuFile.getName()); } if (task.equals("Upload")) { - uploadFile(Uri.parse(vmuFile.toString()).toString()); + if (vmuFile.exists()) + uploadFile(vmuFile.toString()); } } } @@ -449,5 +427,6 @@ public class CloudFragment extends Fragment { } catch (IOException e) { e.printStackTrace(); } + retrieveFiles(vmuName); } } diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/dropbox/UriHelpers.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/dropbox/UriHelpers.java deleted file mode 100644 index 0ee61923e..000000000 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/dropbox/UriHelpers.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.reicast.emulator.dropbox; - -import android.annotation.TargetApi; -import android.content.ContentUris; -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.os.Build; -import android.os.Environment; -import android.provider.DocumentsContract; -import android.provider.MediaStore; - -import java.io.File; -import java.lang.annotation.Target; - -/** - * Utility functions to support Uri conversion and processing. - */ -public final class UriHelpers { - - private UriHelpers() {} - - /** - * Get the file path for a uri. This is a convoluted way to get the path for an Uri created using the - * StorageAccessFramework. This in no way is the official way to do this but there does not seem to be a better - * way to do this at this point. It is taken from https://github.com/iPaulPro/aFileChooser. - * - * @param context The context of the application - * @param uri The uri of the saved file - * @return The file with path pointing to the saved file. It can return null if we can't resolve the uri properly. - */ - @TargetApi(Build.VERSION_CODES.JELLY_BEAN) - public static File getFileForUri(final Context context, final Uri uri) { - String path = null; - // DocumentProvider - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN - && DocumentsContract.isDocumentUri(context, uri)) { - // ExternalStorageProvider - if (isExternalStorageDocument(uri)) { - final String docId = DocumentsContract.getDocumentId(uri); - final String[] split = docId.split(":"); - final String type = split[0]; - - if ("primary".equalsIgnoreCase(type)) { - path = Environment.getExternalStorageDirectory() + "/" + split[1]; - } - } else if (isDownloadsDocument(uri)) { - // DownloadsProvider - final String id = DocumentsContract.getDocumentId(uri); - final Uri contentUri = ContentUris - .withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); - - path = getDataColumn(context, contentUri, null, null); - } else if (isMediaDocument(uri)) { - // MediaProvider - final String docId = DocumentsContract.getDocumentId(uri); - final String[] split = docId.split(":"); - final String type = split[0]; - - Uri contentUri = null; - if ("image".equals(type)) { - contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; - } else if ("video".equals(type)) { - contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; - } else if ("audio".equals(type)) { - contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; - } - - final String selection = "_id=?"; - final String[] selectionArgs = new String[] { - split[1] - }; - - path = getDataColumn(context, contentUri, selection, selectionArgs); - } - } else if ("content".equalsIgnoreCase(uri.getScheme())) { - // MediaStore (and general) - path = getDataColumn(context, uri, null, null); - } else if ("file".equalsIgnoreCase(uri.getScheme())) { - // File - path = uri.getPath(); - } - - if (path != null) { - return new File(path); - } - return null; - } - - private static String getDataColumn(Context context, Uri uri, String selection, - String[] selectionArgs) { - - Cursor cursor = null; - final String column = "_data"; - final String[] projection = { - column - }; - - try { - cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, - null); - if (cursor != null && cursor.moveToFirst()) { - final int column_index = cursor.getColumnIndexOrThrow(column); - return cursor.getString(column_index); - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - return null; - } - - - private static boolean isExternalStorageDocument(Uri uri) { - return "com.android.externalstorage.documents".equals(uri.getAuthority()); - } - - private static boolean isDownloadsDocument(Uri uri) { - return "com.android.providers.downloads.documents".equals(uri.getAuthority()); - } - - private static boolean isMediaDocument(Uri uri) { - return "com.android.providers.media.documents".equals(uri.getAuthority()); - } -}