diff --git a/shell/android-studio/reicast/build.gradle b/shell/android-studio/reicast/build.gradle index b51029928..d31bf537e 100644 --- a/shell/android-studio/reicast/build.gradle +++ b/shell/android-studio/reicast/build.gradle @@ -39,8 +39,6 @@ android { ndk { moduleName "dc" abiFilters 'armeabi-v7a', 'x86' - moduleName "sexplay" - abiFilters 'armeabi-v7a', 'x86' } } @@ -56,11 +54,13 @@ android { buildTypes { debug { debuggable true + minifyEnabled true zipAlignEnabled true + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' } release { debuggable false - minifyEnabled false + minifyEnabled true zipAlignEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' signingConfig signingConfigs.release @@ -101,6 +101,7 @@ dependencies { implementation 'org.bouncycastle:bcprov-jdk16:1.46' implementation 'commons-io:commons-io:2.6' implementation 'org.apache.commons:commons-lang3:3.7' + implementation 'com.dropbox.core:dropbox-core-sdk:3.0.9' implementation ('com.googlecode.json-simple:json-simple:1.1.1') { exclude module: 'junit' } diff --git a/shell/android-studio/reicast/libs/dropbox-android-sdk-1.6.1.jar b/shell/android-studio/reicast/libs/dropbox-android-sdk-1.6.1.jar deleted file mode 100644 index 5e666751a..000000000 Binary files a/shell/android-studio/reicast/libs/dropbox-android-sdk-1.6.1.jar and /dev/null differ diff --git a/shell/android-studio/reicast/proguard-rules.txt b/shell/android-studio/reicast/proguard-rules.txt new file mode 100644 index 000000000..7b3d38a1d --- /dev/null +++ b/shell/android-studio/reicast/proguard-rules.txt @@ -0,0 +1,69 @@ +# This is a configuration file for ProGuard. +# http://proguard.sourceforge.net/index.html#manual/usage.html +# Optimizations: If you don't want to optimize, use the +# proguard-android.txt configuration file instead of this one, which +# turns off the optimization flags. Adding optimization introduces +# certain risks, since for example not all optimizations performed by +# ProGuard works on all versions of Dalvik. The following flags turn +# off various optimizations known to have issues, but the list may not +# be complete or up to date. (The "arithmetic" optimization can be +# used if you are only targeting Android 2.0 or later.) Make sure you +# test thoroughly if you go this route. +-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/* +-optimizationpasses 5 +-allowaccessmodification +-dontpreverify +# The remainder of this file is identical to the non-optimized version +# of the Proguard configuration file (except that the other file has +# flags to turn off optimization). +-dontusemixedcaseclassnames +-dontskipnonpubliclibraryclasses +-verbose +-keepattributes *Annotation* +# For native methods, see http://proguard.sourceforge.net/manual/examples.html#native +-keepclasseswithmembernames class * { + native ; +} +# keep setters in Views so that animations can still work. +# see http://proguard.sourceforge.net/manual/examples.html#beans +-keepclassmembers public class * extends android.view.View { + void set*(***); + *** get*(); +} +# We want to keep methods in Activity that could be used in the XML attribute onClick +-keepclassmembers class * extends android.app.Activity { + public void *(android.view.View); +} +# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} +-keepclassmembers class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator CREATOR; +} +-keepclassmembers class **.R$* { + public static ; +} + +-keep class com.reicast.** {*;} +-keepclassmembers class com.reicast.** {*;} + +# The support library contains references to newer platform versions. +# Don't warn about those in case this app is linking against an older +# platform version. We know about them, and they are safe. +-dontwarn android.support.** +-dontwarn okio.** +-dontwarn okhttp3.** +-dontwarn com.squareup.okhttp.** +-dontwarn com.google.appengine.** +-dontwarn java.io.** +-dontwarn java.nio.file.** +-dontwarn javax.naming.** +-dontwarn javax.servlet.** +-dontwarn junit.textui.** + +-keepattributes Signature +-keepattributes InnerClasses + +-keepattributes Exceptions,SourceFile,LineNumberTable 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/CloudFragment.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/CloudFragment.java deleted file mode 100644 index 6df27828f..000000000 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/CloudFragment.java +++ /dev/null @@ -1,350 +0,0 @@ -package com.reicast.emulator; - -/* - * File: CloudFragment.java - * Author: Luca D'Amico (Luca91) - * Last Edit: 11 May 2014 - * - * Reference: http://forums.reicast.com/index.php?topic=160.msg422 - */ - -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.SharedPreferences; -import android.content.SharedPreferences.Editor; -import android.os.AsyncTask; -import android.os.Bundle; -import android.os.Environment; -import android.preference.PreferenceManager; -import android.support.v4.app.Fragment; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.Toast; - -import com.dropbox.client2.DropboxAPI; -import com.dropbox.client2.DropboxAPI.Entry; -import com.dropbox.client2.android.AndroidAuthSession; -import com.dropbox.client2.exception.DropboxException; -import com.dropbox.client2.session.AccessTokenPair; -import com.dropbox.client2.session.AppKeyPair; -import com.dropbox.client2.session.TokenPair; -import com.reicast.emulator.config.Config; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.concurrent.ExecutionException; - - -public class CloudFragment extends Fragment { - - Button uploadBtn; - Button downloadBtn; - AlertDialog.Builder confirmDialog = null; - boolean actionRequired=false; - public String task = ""; - DropBoxClient client = null; - private String home_directory; - - String[] vmus = {"vmu_save_A1.bin","vmu_save_A2.bin", - "vmu_save_B1.bin","vmu_save_B2.bin", - "vmu_save_C1.bin","vmu_save_C2.bin", - "vmu_save_D1.bin","vmu_save_D2.bin"}; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - return inflater.inflate(R.layout.cloud_fragment, container, false); - } - - @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); - home_directory = mPrefs.getString(Config.pref_home, - Environment.getExternalStorageDirectory().getAbsolutePath()); - buttonListener(); - confirmDialog = new AlertDialog.Builder(getActivity()); - setClient(); - } - - public void setClient(){ - if(client==null) - client = new DropBoxClient(getActivity()); - } - - - public void buttonListener() { - uploadBtn = (Button) getView().findViewById(R.id.uploadBtn); - uploadBtn.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View arg0) { - confirmDialog.setMessage(R.string.uploadWarning); - confirmDialog.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - setClient(); - task = "Upload"; - client.startLogin(); - actionRequired = true; - } - }); - confirmDialog.setNegativeButton(R.string.cancel, null); - confirmDialog.show(); - - } - }); - - - downloadBtn = (Button) getView().findViewById(R.id.downloadBtn); - downloadBtn.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View arg0) { - confirmDialog.setMessage(R.string.downloadWarning); - confirmDialog.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - setClient(); - task = "Download"; - client.startLogin(); - actionRequired = true; - } - }); - confirmDialog.setNegativeButton(R.string.cancel, null); - confirmDialog.show(); - } - }); - } - - - @Override - public void onResume(){ - super.onResume(); - if (client.mDBApi != null) { - if (client.mDBApi.getSession().authenticationSuccessful()) { - try { - client.mDBApi.getSession().finishAuthentication(); - TokenPair tokens = client.mDBApi.getSession(). getAccessTokenPair(); - if(tokens == null) - Toast.makeText(getActivity(), "Failed to save session token!", Toast.LENGTH_SHORT).show(); - else - client.storeKeys(tokens.key, tokens.secret); - } catch (IllegalStateException e) { - Log.i("Dropbox", "Error authenticating", e); - } - } - if(actionRequired){ - for(int k=0;k mDBApi; - AndroidAuthSession session; - - public DropBoxClient(Context context){ - this.context = context; - session = buildSession(); - mDBApi = new DropboxAPI(session); - } - - public void startLogin(){ - mDBApi.getSession().startOAuth2Authentication(context); - } - - public String[] getKeys() { - SharedPreferences prefs = context.getSharedPreferences("ReicastVMUUploader", 0); - String key = prefs.getString("DBoxKey", null); - String secret = prefs.getString("DBoxSecret", null); - if (key != null && secret != null) { - String[] ret = new String[2]; - ret[0] = key; - ret[1] = secret; - return ret; - } else { - return null; - } - } - - - - public void storeKeys(String key, String secret) { - SharedPreferences prefs = context.getSharedPreferences("ReicastVMUUploader", 0); - Editor edit = prefs.edit(); - edit.putString("DBoxKey", key); - edit.putString("DBoxSecret", secret); - edit.apply(); - } - - private AndroidAuthSession buildSession() { - AppKeyPair appKeyPair = new AppKeyPair(APP_KEY, APP_SECRET); - AndroidAuthSession session; - - String[] stored = getKeys(); - if (stored != null) { - AccessTokenPair accessToken = new AccessTokenPair(stored[0], stored[1]); - session = new AndroidAuthSession(appKeyPair, accessToken); - } else { - session = new AndroidAuthSession(appKeyPair); - } - - return session; - } - -} - -class netOperation extends AsyncTask { - - DropBoxClient client = null; - private String home_directory; - - public netOperation(DropBoxClient client, String home_directory){ - this.client = client; - this.home_directory = home_directory; - } - - public boolean uploadFile(String filePath, String fileName) { - File file = new File(filePath); - FileInputStream inputStream = null; - try { - inputStream = new FileInputStream(file); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } - - DropboxAPI.Entry response = null; - try { - response = client.mDBApi.putFileOverwrite("/"+fileName, inputStream, file.length(), null); - } catch (DropboxException e) { - e.printStackTrace(); - } - Log.i("FileInfos", "The uploaded file's rev is: "+ response); - return true; - } - - public boolean downloadFile(String filePath, String fileName) { - DropboxAPI.DropboxFileInfo info = null; - try { - Entry remoteFile = client.mDBApi.metadata("/"+fileName, 1, null, false, null); - if((remoteFile.rev != null) && (remoteFile.bytes > 0)){ // Avoid to download 0 bytes vmus! - File file = new File(filePath); - if(file.exists()) - createBackupOfVmu(fileName); - FileOutputStream out = null; - try { - out = new FileOutputStream(file); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } - info = client.mDBApi.getFile("/"+fileName,null,out,null); - } - } catch (DropboxException e) { - e.printStackTrace(); - } - Log.i("FileInfos", "The downloaded file's rev is: "+ info); - return true; - } - - - @Override - protected void onPostExecute(String result) { - - } - - @Override - protected String doInBackground(String... strings) { - if(strings[0].equals("Upload")){ - if(uploadFile(strings[1],strings[2])) - return "Ok"; - else - return "No"; - } - else if(strings[0].equals("Download")){ - if(downloadFile(strings[1],strings[2])) - return "Ok"; - else - return "No"; - } - else - return "Unknown"; - } - - @Override - protected void onPreExecute() {} - - @Override - protected void onProgressUpdate(Void... values) {} - - - void createBackupOfVmu(String vmuName){ - File backupDir = new File(home_directory+"/VmuBackups/"); - if(!backupDir.exists()) { - backupDir.mkdirs(); - } - - File source = new File(home_directory+"/"+vmuName); - File destination = new File(home_directory+"/VmuBackups/"+vmuName); - if(!destination.exists()) { - try { - destination.createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - } - } - try { - InputStream in = new FileInputStream(source); - OutputStream out = new FileOutputStream(destination); - byte[] buffer = new byte[1024]; - int length; - while ((length = in.read(buffer)) > 0){ - out.write(buffer, 0, length); - } - in.close(); - out.close(); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - } - -} diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/FileBrowser.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/FileBrowser.java index f80312032..f2692fd9a 100644 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/FileBrowser.java +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/FileBrowser.java @@ -194,6 +194,11 @@ public class FileBrowser extends Fragment { if (buttons != null && buttons.exists()) { in = new FileInputStream(buttons); } else if (!file.exists() || file.length() == 0) { + try { + file.createNewFile(); + } catch (Exception e) { + // N+ files be broken + } in = getActivity().getAssets().open("buttons.png"); } if (in != null) { @@ -321,17 +326,7 @@ public class FileBrowser extends Fragment { ((TextView) childview.findViewById(R.id.item_name)).setText(R.string.boot_bios); ImageView icon = (ImageView) childview.findViewById(R.id.item_icon); icon.setImageResource(R.mipmap.disk_bios); - int app_theme = mPrefs.getInt(Config.pref_app_theme, 0); - if (app_theme == 7) { - ImageViewCompat.setImageTintList(icon, ColorStateList.valueOf( - ContextCompat.getColor(getActivity(), R.color.colorDreamTint))); - } else if (app_theme == 1) { - ImageViewCompat.setImageTintList(icon, ColorStateList.valueOf( - ContextCompat.getColor(getActivity(), R.color.colorBlueTint))); - } else { - ImageViewCompat.setImageTintList(icon, ColorStateList.valueOf( - ContextCompat.getColor(getActivity(), R.color.colorDarkTint))); - } + configureTheme(childview, true); childview.setTag(null); @@ -352,17 +347,7 @@ public class FileBrowser extends Fragment { ((TextView) childview.findViewById(R.id.item_name)).setText(R.string.clear_search); ImageView icon = (ImageView) childview.findViewById(R.id.item_icon); icon.setImageResource(R.mipmap.disk_unknown); - int app_theme = mPrefs.getInt(Config.pref_app_theme, 0); - if (app_theme == 7) { - ImageViewCompat.setImageTintList(icon, ColorStateList.valueOf( - ContextCompat.getColor(getActivity(), R.color.colorDreamTint))); - } else if (app_theme == 1) { - ImageViewCompat.setImageTintList(icon, ColorStateList.valueOf( - ContextCompat.getColor(getActivity(), R.color.colorBlueTint))); - } else { - ImageViewCompat.setImageTintList(icon, ColorStateList.valueOf( - ContextCompat.getColor(getActivity(), R.color.colorDarkTint))); - } + configureTheme(childview, true); childview.setTag(null); @@ -433,6 +418,7 @@ public class FileBrowser extends Fragment { } } }); + configureTheme(childview, false); list.addView(childview); xmlParser.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, game.getName()); } @@ -499,18 +485,6 @@ public class FileBrowser extends Fragment { final View childview = browser.get().getActivity().getLayoutInflater().inflate( R.layout.browser_fragment_item, null, false); - int app_theme = browser.get().mPrefs.getInt(Config.pref_app_theme, 0); - if (app_theme == 7) { - childview.findViewById(R.id.childview) - .setBackgroundResource(R.drawable.game_selector_dream); - } else if (app_theme == 1) { - childview.findViewById(R.id.childview) - .setBackgroundResource(R.drawable.game_selector_blue); - } else { - childview.findViewById(R.id.childview) - .setBackgroundResource(R.drawable.game_selector_dark); - } - if (file == null) { ((TextView) childview.findViewById(R.id.item_name)).setText(R.string.folder_select); } else if (file == parent) @@ -522,17 +496,7 @@ public class FileBrowser extends Fragment { icon.setImageResource(file == null ? R.drawable.ic_settings: file.isDirectory() ? R.drawable.ic_folder_black_24dp : R.drawable.disk_unknown); - - if (app_theme == 7) { - ImageViewCompat.setImageTintList(icon, ColorStateList.valueOf( - ContextCompat.getColor(browser.get().getActivity(), R.color.colorDreamTint))); - } else if (app_theme == 1) { - ImageViewCompat.setImageTintList(icon, ColorStateList.valueOf( - ContextCompat.getColor(browser.get().getActivity(), R.color.colorBlueTint))); - } else { - ImageViewCompat.setImageTintList(icon, ColorStateList.valueOf( - ContextCompat.getColor(browser.get().getActivity(), R.color.colorDarkTint))); - } + browser.get().configureTheme(childview, true); childview.setTag(file); @@ -616,6 +580,30 @@ public class FileBrowser extends Fragment { } } + private void configureTheme(View childview, boolean useTint) { + int app_theme = mPrefs.getInt(Config.pref_app_theme, 0); + ImageView icon = (ImageView) childview.findViewById(R.id.item_icon); + if (app_theme == 7) { + childview.findViewById(R.id.childview) + .setBackgroundResource(R.drawable.game_selector_dream); + if (useTint) + ImageViewCompat.setImageTintList(icon, ColorStateList.valueOf( + ContextCompat.getColor(getActivity(), R.color.colorDreamTint))); + } else if (app_theme == 1) { + childview.findViewById(R.id.childview) + .setBackgroundResource(R.drawable.game_selector_blue); + if (useTint) + ImageViewCompat.setImageTintList(icon, ColorStateList.valueOf( + ContextCompat.getColor(getActivity(), R.color.colorBlueTint))); + } else { + childview.findViewById(R.id.childview) + .setBackgroundResource(R.drawable.game_selector_dark); + if (useTint) + ImageViewCompat.setImageTintList(icon, ColorStateList.valueOf( + ContextCompat.getColor(getActivity(), R.color.colorDarkTint))); + } + } + private void showToastMessage(String message, int duration) { ConstraintLayout layout = (ConstraintLayout) getActivity().findViewById(R.id.mainui_layout); Snackbar snackbar = Snackbar.make(layout, message, duration); diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/MainActivity.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/MainActivity.java index ce6a3bc17..615dec331 100644 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/MainActivity.java +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/MainActivity.java @@ -20,7 +20,6 @@ import android.support.design.widget.NavigationView; import android.support.design.widget.Snackbar; import android.support.v4.app.ActivityCompat; import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentTransaction; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.AppCompatActivity; @@ -41,6 +40,7 @@ import com.reicast.emulator.config.InputFragment; import com.reicast.emulator.config.OptionsFragment; import com.reicast.emulator.config.PGConfigFragment; import com.reicast.emulator.debug.GenerateLogs; +import com.reicast.emulator.cloud.CloudFragment; import com.reicast.emulator.emu.JNIdc; import java.lang.Thread.UncaughtExceptionHandler; diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/XMLParser.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/XMLParser.java index 222c55c59..eb7355632 100644 --- a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/XMLParser.java +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/XMLParser.java @@ -227,12 +227,15 @@ public class XMLParser extends AsyncTask { icon.setImageDrawable(game_icon); int app_theme = mPrefs.getInt(Config.pref_app_theme, 0); if (app_theme == 7) { + childview.get().setBackgroundResource(R.drawable.game_selector_dream); ImageViewCompat.setImageTintList(icon, ColorStateList.valueOf( ContextCompat.getColor(mContext.get(), R.color.colorDreamTint))); } else if (app_theme == 1) { + childview.get().setBackgroundResource(R.drawable.game_selector_blue); ImageViewCompat.setImageTintList(icon, ColorStateList.valueOf( ContextCompat.getColor(mContext.get(), R.color.colorBlueTint))); } else { + childview.get().setBackgroundResource(R.drawable.game_selector_dark); ImageViewCompat.setImageTintList(icon, ColorStateList.valueOf( ContextCompat.getColor(mContext.get(), R.color.colorDarkTint))); } diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/cloud/CloudFragment.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/cloud/CloudFragment.java new file mode 100644 index 000000000..5f680ccd7 --- /dev/null +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/cloud/CloudFragment.java @@ -0,0 +1,432 @@ +package com.reicast.emulator.cloud; + +/* + * Author: Luca D'Amico (Luca91) + * Rewrite: AbandonedCart + * Last Edit: 02 Oct 2018 + * Reference: http://forums.reicast.com/index.php?topic=160.msg422 + */ + +import android.app.AlertDialog; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Environment; +import android.preference.PreferenceManager; +import android.support.v4.app.Fragment; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.Toast; + +import com.dropbox.core.DbxException; +import com.dropbox.core.android.Auth; +import com.dropbox.core.v2.DbxClientV2; +import com.dropbox.core.v2.files.FileMetadata; +import com.dropbox.core.v2.files.ListFolderResult; +import com.dropbox.core.v2.files.Metadata; +import com.dropbox.core.v2.files.WriteMode; +import com.reicast.emulator.R; +import com.reicast.emulator.config.Config; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +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 { + + private final static String APP_KEY = "lowa9ps6h5k7zbo"; + private String mPath = ""; // Dropbox folder + + Button uploadBtn; + Button downloadBtn; + AlertDialog.Builder confirmDialog = null; + private String home_directory; + + String[] vmus = {"vmu_save_A1.bin", "vmu_save_A2.bin", + "vmu_save_B1.bin", "vmu_save_B2.bin", + "vmu_save_C1.bin", "vmu_save_C2.bin", + "vmu_save_D1.bin", "vmu_save_D2.bin"}; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.cloud_fragment, container, false); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); + home_directory = mPrefs.getString(Config.pref_home, + Environment.getExternalStorageDirectory().getAbsolutePath()); + buttonListener(); + confirmDialog = new AlertDialog.Builder(getActivity()); + } + + @Override + public void onResume() { + super.onResume(); + SharedPreferences mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); + String accessToken = mPrefs.getString("access-token", null); + if (accessToken == null) { + accessToken = Auth.getOAuth2Token(); + if (accessToken != null) { + mPrefs.edit().putString("access-token", accessToken).apply(); + DbxClientFactory.init(accessToken); + } + } else { + DbxClientFactory.init(accessToken); + } + } + + public void buttonListener() { + Auth.startOAuth2Authentication(getActivity(), APP_KEY); + uploadBtn = (Button) getView().findViewById(R.id.uploadBtn); + uploadBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View arg0) { + confirmDialog.setMessage(R.string.uploadWarning); + confirmDialog.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + getPathForFiles("Upload"); + } + }); + confirmDialog.setNegativeButton(R.string.cancel, null); + confirmDialog.show(); + + } + }); + + + downloadBtn = (Button) getView().findViewById(R.id.downloadBtn); + downloadBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View arg0) { + confirmDialog.setMessage(R.string.downloadWarning); + confirmDialog.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + getPathForFiles("Download"); + } + }); + confirmDialog.setNegativeButton(R.string.cancel, null); + confirmDialog.show(); + } + }); + } + + 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(), DbxClientFactory.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); + dialog.setCancelable(false); + dialog.setMessage("Loading"); + dialog.show(); + + try { + new ListFolderTask(DbxClientFactory.getClient(), new ListFolderTask.Callback() { + @Override + public void onDataLoaded(ListFolderResult result) { + dialog.dismiss(); + for (Metadata item : result.getEntries()) { + if (item.getName().equals(vmu)) { + if (item instanceof FileMetadata) { + downloadFile((FileMetadata) item); + } + } + } + } + + @Override + public void onError(Exception e) { + dialog.dismiss(); + + Log.e(getActivity().getLocalClassName(), "Failed to list folder.", e); + Toast.makeText(getActivity(), + "Failed to list folder", Toast.LENGTH_SHORT).show(); + } + }).execute(mPath); + } catch (IllegalStateException s) { + dialog.dismiss(); + Log.e(getActivity().getLocalClassName(), "Failed to list folder.", s); + Toast.makeText(getActivity(), + "Failed to list folder", Toast.LENGTH_SHORT).show(); + } + } + + private void downloadFile(FileMetadata file) { + final ProgressDialog dialog = new ProgressDialog(getActivity()); + dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); + dialog.setCancelable(false); + dialog.setMessage("Downloading"); + dialog.show(); + + try { + new DownloadFileTask(this, DbxClientFactory.getClient(), new DownloadFileTask.Callback() { + @Override + public void onDownloadComplete(File result) { + dialog.dismiss(); + } + + @Override + public void onError(Exception e) { + dialog.dismiss(); + + Log.e(getActivity().getLocalClassName(), "Failed to download file.", e); + Toast.makeText(getActivity(), + "Failed to download file\n" + e, Toast.LENGTH_SHORT).show(); + } + }).execute(file); + } catch (IllegalStateException s) { + dialog.dismiss(); + Log.e(getActivity().getLocalClassName(), "Failed to download file.", s); + Toast.makeText(getActivity(), + "Failed to download file", Toast.LENGTH_SHORT).show(); + } + } + + 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; + private final Callback mCallback; + private Exception mException; + + public interface Callback { + void onDataLoaded(ListFolderResult result); + void onError(Exception e); + } + + ListFolderTask(DbxClientV2 dbxClient, Callback callback) { + mDbxClient = dbxClient; + mCallback = callback; + } + + @Override + protected void onPostExecute(ListFolderResult result) { + super.onPostExecute(result); + + if (mException != null) { + mCallback.onError(mException); + } else { + mCallback.onDataLoaded(result); + } + } + + @Override + protected ListFolderResult doInBackground(String... params) { + try { + return mDbxClient.files().listFolder(params[0]); + } catch (DbxException e) { + mException = e; + } + + return null; + } + } + + static class DownloadFileTask extends AsyncTask { + + private WeakReference mCloud; + private final DbxClientV2 mDbxClient; + private final Callback mCallback; + private Exception mException; + + public interface Callback { + void onDownloadComplete(File result); + void onError(Exception e); + } + + DownloadFileTask(CloudFragment context, DbxClientV2 dbxClient, Callback callback) { + mCloud = new WeakReference<>(context); + mDbxClient = dbxClient; + mCallback = callback; + } + + @Override + protected void onPostExecute(File result) { + super.onPostExecute(result); + if (mException != null) { + mCallback.onError(mException); + } else { + mCallback.onDownloadComplete(result); + } + } + + @Override + protected File doInBackground(FileMetadata... params) { + FileMetadata metadata = params[0]; + try { + File path = new File(mCloud.get().home_directory); + File file = new File(path, metadata.getName()); + + // Make sure the Downloads directory exists. + if (!path.exists()) { + if (!path.mkdirs()) { + mException = new RuntimeException("Unable to create directory: " + path); + } + } else if (!path.isDirectory()) { + mException = new IllegalStateException("Download path is not a directory: " + path); + return null; + } + + // Download the file. + OutputStream outputStream = new FileOutputStream(file); + mDbxClient.files().download(metadata.getPathLower(), metadata.getRev()) + .download(outputStream); + + // Tell android about the file + Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); + intent.setData(Uri.fromFile(file)); + mCloud.get().getActivity().sendBroadcast(intent); + + return file; + } catch (DbxException | IOException e) { + mException = e; + } + + return null; + } + } + + 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()); + else + retrieveFiles(vmuFile.getName()); + } + if (task.equals("Upload")) { + if (vmuFile.exists()) + uploadFile(vmuFile.toString()); + } + } + } + + void createBackupOfVmu(String vmuName) { + File backupDir = new File(home_directory, "vmu_backup"); + if (!backupDir.exists()) { + backupDir.mkdirs(); + } + + File source = new File(home_directory, vmuName); + File destination = new File(home_directory, "vmu_backup/" + vmuName); + if (!destination.exists()) { + try { + destination.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + } + try { + InputStream in = new FileInputStream(source); + OutputStream out = new FileOutputStream(destination); + byte[] buffer = new byte[1024]; + int length; + while ((length = in.read(buffer)) > 0) { + out.write(buffer, 0, length); + } + in.close(); + out.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + retrieveFiles(vmuName); + } +} diff --git a/shell/android-studio/reicast/src/main/java/com/reicast/emulator/cloud/DbxClientFactory.java b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/cloud/DbxClientFactory.java new file mode 100644 index 000000000..1fceb1058 --- /dev/null +++ b/shell/android-studio/reicast/src/main/java/com/reicast/emulator/cloud/DbxClientFactory.java @@ -0,0 +1,32 @@ +package com.reicast.emulator.cloud; + +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 DbxClientFactory { + + 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; + } +}