gophast-android/app/src/main/java/space/rocketnine/gophast/GoPhast.java

513 lines
16 KiB
Java

package space.rocketnine.gophast;
import android.app.Activity;
import android.app.Application;
import android.app.DownloadManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.StrictMode;
import android.preference.PreferenceManager;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.RecyclerView;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Toast;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import space.rocketnine.gophast.manager.Manager;
import space.rocketnine.gophast.manager.Metadata;
import space.rocketnine.gophast.manager.NewDownloadInfo;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
public class GoPhast extends Application {
ArrayList<Download> downloads;
ExecutorService exec;
WakeLocker wakeLocker;
SharedPreferences prefs;
DownloadAdapter adapter;
DownloadManager DownloadManager;
RecyclerView downloadsRecyclerView;
@Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
}
exec = Executors.newCachedThreadPool();
DownloadManager = (DownloadManager) getSystemService(Activity.DOWNLOAD_SERVICE);
wakeLocker = new WakeLocker();
try {
Manager.initialize(2);
} catch (Exception e) {
e.printStackTrace();
return;
}
String appVersion;
try {
PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
appVersion = packageInfo.versionName;
} catch (PackageManager.NameNotFoundException e) {
appVersion = "?";
}
Manager.setUserAgentExtra("gophast-android/" + appVersion + " (https://gitlab.com/tslocum/gophast-android)");
Manager.setForce(true);
setDownloadDir("");
Long minSplitSize = Long.valueOf(getPrefs().getString("pref_minsplitsize", "10485760"));
if (minSplitSize >= 0) {
Manager.setMinSplitSize(minSplitSize);
}
Long maxConnections = Long.valueOf(getPrefs().getString("pref_maxconnections", "3"));
if (maxConnections >= 0) {
Manager.setMaxConnections(maxConnections);
}
downloads = new ArrayList<Download>();
}
void addDownload(String url, Activity activity) {
url = url.trim();
if (url.equals("") || !url.toLowerCase().startsWith("http")) {
longToast(getString(R.string.invalidURL));
return;
}
NewDownloadInfo info = new NewDownloadInfo();
info.setURL(url);
info.setPostData(new byte[]{});
Metadata md;
try {
md = Manager.fetchMetadata(info);
} catch (Exception e) {
longToast(e.getMessage());
return;
}
String downloadName = (new File(url)).getName();
if (md != null && !md.getName().isEmpty()) {
downloadName = md.getName();
}
Download download = new Download();
download.URL = url;
download.Name = downloadName;
File downloadPath = (new File(getDownloadDir(), download.Name));
File downloadControlPath = (new File(getDownloadDir(), download.Name + ".gophast"));
if (downloadPath.exists() && !downloadControlPath.exists()) {
// TODO: Update add dialog with "fetching metadata" and overwrite warning for smoother transition
// Allow passing known metadata later to reduce transfer time
promptExistingFile(download, activity);
} else {
finishAddingDownload(download);
}
}
void promptExistingFile(Download download, Activity activity) {
AlertDialog.Builder alert = new AlertDialog.Builder(activity, R.style.AppTheme_Dialog)
.setTitle(String.format(getString(R.string.overwriteFile), download.Name))
.setIcon(R.drawable.ic_warning_white)
.setMessage(R.string.promptFileExists)
.setPositiveButton(R.string.overwrite, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
finishAddingDownload(download);
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
}
});
alert.show();
}
void finishAddingDownload(Download download) {
downloads.add(download);
updateDownloadIndexes();
download.Path = Uri.fromFile(new File(getDownloadDir(), download.Name));
adapter.notifyItemInserted(download.Index);
DownloadRunnable runnable = new DownloadRunnable();
runnable.d = download;
runnable.gophast = GoPhast.this;
runnable.recyclerView = downloadsRecyclerView;
Thread thread = new Thread(runnable);
thread.setPriority(THREAD_PRIORITY_BACKGROUND);
exec.execute(thread);
}
void updateDownloadIndexes() {
int index = 0;
for (Iterator<Download> i = downloads.iterator(); i.hasNext(); ) {
Download d = i.next();
d.Index = index;
index++;
}
}
void startDownload(Download d, NewDownloadInfo info) throws Exception {
if (d.Paused) {
resumeDownload(d);
return;
} else if (d.Complete) {
return;
}
d.ID = Manager.addDownload(info);
acquireWakeLock(d.ID);
}
void pauseDownload(Download d) {
if (d.ID <= 0 || d.Paused || d.Complete) {
return;
}
d.Paused = true;
Log.d("gophast", "Pausing #" + d.ID);
try {
Manager.pauseDownload(d.ID);
} catch (Exception e) {
e.printStackTrace();
}
releaseWakeLock(d.ID);
}
void resumeDownload(Download d) {
if (d.ID <= 0 || !d.Paused || d.Complete) {
return;
}
d.Paused = false;
try {
Manager.resumeDownload(d.ID);
} catch (Exception e) {
e.printStackTrace();
return;
}
acquireWakeLock(d.ID);
}
void togglePauseDownload(Download d) {
if (!d.Paused) {
exec.execute(new Runnable() {
@Override
public void run() {
pauseDownload(d);
}
});
} else {
exec.execute(new Runnable() {
@Override
public void run() {
resumeDownload(d);
}
});
}
}
void openDownload(Download d, boolean showChooser, Context context) {
if (!d.Complete) {
return;
}
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(d.Path, d.getMIME());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
try {
if (showChooser) {
context.startActivity(Intent.createChooser(intent, context.getString(R.string.openWith)));
} else {
context.startActivity(intent);
}
} catch (Exception e) {
e.printStackTrace();
longToast("No application able to open this file is installed");
}
}
void toggleOpenDownloadWhenComplete(Download d) {
if (d.Complete) {
return;
}
d.OpenWhenComplete = !d.OpenWhenComplete;
if (d.OpenWhenComplete) {
shortToast(String.format(getString(R.string.openingWhenComplete), d.Name));
} else {
shortToast(String.format(getString(R.string.notOpeningWhenComplete), d.Name));
}
}
void promptRemoveDownload(final Download d, Context context) {
AlertDialog removeDialog = new AlertDialog.Builder(context, R.style.AppTheme_Dialog)
.setTitle(String.format(getString(R.string.deleteFile), d.Name))
.setIcon(R.drawable.ic_warning_white)
.setMessage(R.string.confirmDelete)
.setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
exec.execute(new Runnable() {
@Override
public void run() {
removeDownload(d);
}
});
}
})
.setNegativeButton(R.string.cancel, null)
.create();
removeDialog.show();
}
void removeDownload(Download delete) {
if (delete == null) {
return;
}
Log.d("gophast", "Removing #" + delete.ID);
long id = delete.ID;
if (delete.ID > 0) {
try {
Manager.removeDownload(delete.ID);
} catch (Exception e) {
// Already removed
}
}
for (Iterator<Download> i = downloads.iterator(); i.hasNext(); ) {
Download d = i.next();
if (d.equals(delete)) {
if (d.DownloadManagerID > 0) {
try {
DownloadManager.remove(d.DownloadManagerID);
} catch (Exception e) {
// Already removed
}
}
int index = d.Index;
String path = d.Path != null ? d.Path.getPath() : "";
String name = d.Name;
i.remove();
updateDownloadIndexes();
adapter.notifyItemRemoved(index);
if (path != null && !path.isEmpty()) {
boolean deleted = false;
try {
File file = new File(path);
if (file.exists()) {
if (file.isFile()) {
deleted = file.delete();
}
} else {
deleted = true;
}
} catch (Exception e) {
// Failed to delete download
}
try {
File controlFile = new File(path + ".gophast");
if (controlFile.exists() && controlFile.isFile()) {
controlFile.delete();
}
} catch (Exception e) {
// Failed to delete control file
}
if (!deleted) {
longToast(String.format(getString(R.string.failedToDelete), name));
}
}
break;
}
}
if (id > 0) {
releaseWakeLock(id);
}
}
void shortToast(final String message) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
Toast.makeText(GoPhast.this, message, Toast.LENGTH_SHORT).show();
}
});
}
void shortToast(final int resId) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
Toast.makeText(GoPhast.this, resId, Toast.LENGTH_SHORT).show();
}
});
}
void longToast(final String message) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
Toast.makeText(GoPhast.this, message, Toast.LENGTH_LONG).show();
}
});
}
void longToast(final int resId) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
Toast.makeText(GoPhast.this, resId, Toast.LENGTH_LONG).show();
}
});
}
String getDefaultDownloadDir() {
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
}
String getDownloadDir() {
String downloadDir = getPrefs().getString("pref_downloaddir", "");
if (downloadDir == null || downloadDir.isEmpty()) {
downloadDir = getDefaultDownloadDir();
}
return downloadDir;
}
void setDownloadDir(String newDownloadDir) {
String downloadDir;
if (newDownloadDir != null && !newDownloadDir.isEmpty()) {
downloadDir = newDownloadDir;
} else {
downloadDir = getDownloadDir();
}
Log.d("gophast", "Setting download directory to " + downloadDir);
Manager.setDownloadDir(downloadDir);
}
public void animateShowView(Context ctx, View v, int animation) {
Animation fadeInAnimation = AnimationUtils.loadAnimation(
ctx, animation);
v.startAnimation(fadeInAnimation);
fadeInAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
v.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
}
});
}
public void animateHideView(Context ctx, View v, int animation) {
Animation fadeInAnimation = AnimationUtils.loadAnimation(
ctx, animation);
v.startAnimation(fadeInAnimation);
fadeInAnimation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
v.setVisibility(View.INVISIBLE);
}
});
}
/**
* This method converts dp unit to equivalent pixels, depending on device density.
*
* @param dp A value in dp (density independent pixels) unit. Which we need to convert into pixels
* @return A float value to represent px equivalent to dp depending on device density
*/
public int convertDpToPixel(int dp) {
return (int) (dp * ((float) getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT));
}
/**
* This method converts device specific pixels to density independent pixels.
*
* @param px A value in px (pixels) unit. Which we need to convert into db
* @return A float value to represent dp equivalent to px value
*/
public int convertPixelsToDp(int px) {
return (int) (px / ((float) getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT));
}
public void acquireWakeLock(long id) {
wakeLocker.acquire(GoPhast.this, id, false);
}
public void releaseWakeLock(long id) {
wakeLocker.release(id);
}
public SharedPreferences getPrefs() {
if (prefs == null) {
prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
}
return prefs;
}
boolean isDownloadIntent(Intent intent) {
return intent != null && intent.getAction() != null && intent.getAction().equals("android.intent.action.VIEW") && intent.getData() != null;
}
}