From 209e4c87b0edcb597888ed304699941b15600865 Mon Sep 17 00:00:00 2001
From: daktak
Date: Tue, 12 Jul 2016 09:33:59 +1000
Subject: [PATCH 1/3] update to add header
---
app/build.gradle | 4 ++--
app/src/main/java/org/afhdownloader/Download.java | 8 ++++++--
app/src/main/java/org/afhdownloader/MainActivity.java | 3 ++-
3 files changed, 10 insertions(+), 5 deletions(-)
diff --git a/app/build.gradle b/app/build.gradle
index 64fd03c..93f2ff0 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -8,8 +8,8 @@ android {
applicationId "org.afhdownloader"
minSdkVersion 16
targetSdkVersion 24
- versionCode 4
- versionName "0.3"
+ versionCode 5
+ versionName "0.3.1"
}
buildTypes {
release {
diff --git a/app/src/main/java/org/afhdownloader/Download.java b/app/src/main/java/org/afhdownloader/Download.java
index 294b31f..2e4c439 100644
--- a/app/src/main/java/org/afhdownloader/Download.java
+++ b/app/src/main/java/org/afhdownloader/Download.java
@@ -94,6 +94,10 @@ public String parseUrl(String url) {
urls.add(link.ownText());
urls.add(link.attr("href"));
}
+ //set title
+ Elements h1s = doc.select("h1");
+ String h1 = h1s.get(0).ownText();
+ urls.add(h1);
} catch (Throwable t) {
Log.e(LOGTAG,t.getMessage());
@@ -253,9 +257,9 @@ protected void onPostExecute(String s) {
public void download(String url, String desc, String title, String filename) {
SharedPreferences mySharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
- String exten = "zip";
+ String exten = "/";
- if (url.endsWith(exten)) {
+ if (!url.endsWith(exten)) {
Log.d(LOGTAG, "Downloading: " + url);
boolean external = mySharedPreferences.getBoolean("prefExternal", false);
diff --git a/app/src/main/java/org/afhdownloader/MainActivity.java b/app/src/main/java/org/afhdownloader/MainActivity.java
index 19cee2e..55b574c 100644
--- a/app/src/main/java/org/afhdownloader/MainActivity.java
+++ b/app/src/main/java/org/afhdownloader/MainActivity.java
@@ -262,9 +262,10 @@ public void setList(List values) {
}
}
+ getSupportActionBar().setTitle(values.get(values.size()-1));
//for each returned value - filename and url
- for (int j = 0; j < values.size(); j+=2) {
+ for (int j = 0; j < values.size()-1; j+=2) {
String md5val = "";
String url = values.get(j+1).trim();
String name = values.get(j).trim();
From c59cc7d00508626edda2cb9b08c914d78981cd88 Mon Sep 17 00:00:00 2001
From: daktak
Date: Tue, 12 Jul 2016 12:51:08 +1000
Subject: [PATCH 2/3] Swipe to delete added
---
app/build.gradle | 2 +-
.../java/org/afhdownloader/MainActivity.java | 66 ++-
.../SwipeDismissListViewTouchListener.java | 414 ++++++++++++++++++
.../SwipeDismissTouchListener.java | 291 ++++++++++++
app/src/main/res/values/donottranslate.xml | 3 +-
app/src/main/res/values/strings.xml | 3 +
6 files changed, 769 insertions(+), 10 deletions(-)
create mode 100644 app/src/main/java/org/afhdownloader/SwipeDismissListViewTouchListener.java
create mode 100644 app/src/main/java/org/afhdownloader/SwipeDismissTouchListener.java
diff --git a/app/build.gradle b/app/build.gradle
index 93f2ff0..4a48efb 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -9,7 +9,7 @@ android {
minSdkVersion 16
targetSdkVersion 24
versionCode 5
- versionName "0.3.1"
+ versionName "0.4"
}
buildTypes {
release {
diff --git a/app/src/main/java/org/afhdownloader/MainActivity.java b/app/src/main/java/org/afhdownloader/MainActivity.java
index 55b574c..9251d9a 100644
--- a/app/src/main/java/org/afhdownloader/MainActivity.java
+++ b/app/src/main/java/org/afhdownloader/MainActivity.java
@@ -2,8 +2,10 @@
import android.Manifest;
import android.app.AlarmManager;
+import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
@@ -39,9 +41,12 @@ public class MainActivity extends AppCompatActivity
private static final int REQUEST_PREFS = 99;
private static final int RC_EXT_WRITE =1;
private static final int RC_EXT_READ=2;
+ private static final int YES_NO_CALL=80;
public static MainActivity instance = null;
-
+ public ArrayList md5check = new ArrayList();
+ public ArrayList names = new ArrayList();
public ArrayList urls = new ArrayList();
+ public String directory;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -238,12 +243,12 @@ public void writeFile(String name, String body){
}
public void setList(List values) {
- ArrayList names = new ArrayList();
SharedPreferences mySharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
- String directory = mySharedPreferences.getString("prefDirectory",Environment.DIRECTORY_DOWNLOADS).trim();
+ directory = mySharedPreferences.getString("prefDirectory",Environment.DIRECTORY_DOWNLOADS).trim();
boolean external = mySharedPreferences.getBoolean("prefExternal",false);
- ArrayList md5check = new ArrayList();
-
+ md5check = new ArrayList();
+ String md5_ext = getString(R.string.md5_ext);
+ final String md5_calc_ext = getString(R.string.md5calc_ext);
if (external){
directory = Environment.DIRECTORY_DOWNLOADS;
}
@@ -284,16 +289,16 @@ public void setList(List values) {
for (int k = 0; k < file.length; k++) {
if (name.equals(file[k].getName())) {
- String md5 = readFile(name + ".md5");
+ String md5 = readFile(name + md5_ext);
if (!md5.isEmpty()) {
- String md5calc = readFile(name+".calc.md5");
+ String md5calc = readFile(name+md5_calc_ext);
if (md5calc.isEmpty()) {
md5calc = MD5.calculateMD5(file[k]);
}
if (md5calc.equalsIgnoreCase(md5)) {
md5val = "Y";
//cache this result
- writeFile(name+".calc.md5", md5calc);
+ writeFile(name+md5_calc_ext, md5calc);
} else {
md5val = "N";
//don't cache, in the event the file is still downloading
@@ -322,6 +327,38 @@ public void setList(List values) {
// Set the ArrayAdapter as the ListView's adapter.
mainListView.setAdapter( listAdapter );
+ SwipeDismissListViewTouchListener touchListener =
+ new SwipeDismissListViewTouchListener(
+ mainListView,
+ new SwipeDismissListViewTouchListener.DismissCallbacks() {
+ @Override
+ public boolean canDismiss(int position) {
+ boolean dis = true;
+ if (md5check.get(position).isEmpty()) { dis = false; };
+ return dis;
+ }
+
+ @Override
+ public void onDismiss(ListView listView, int[] reverseSortedPositions) {
+ for (int position : reverseSortedPositions) {
+ final int pos = position;
+ DialogInterface.OnClickListener yesListener = new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ File direct = new File(Environment.getExternalStorageDirectory() + "/" + directory+"/"+names.get(pos));
+ Log.d(LOGTAG, "Delete " + direct.getName());
+ if (direct.exists()&&direct.isFile()) { direct.delete(); }
+ File md5file = new File(getFilesDir(), names.get(pos)+md5_calc_ext );
+ if (md5file.exists()&&md5file.isFile()) { md5file.delete(); }
+ if (MainActivity.instance != null) {
+ run(MainActivity.instance);
+ }
+ }
+ };
+ message_dialog_yes_no(getString(R.string.delete) + " " + names.get(pos)+"?" , yesListener);
+ }
+ }
+ });
+ mainListView.setOnTouchListener(touchListener);
mainListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
@@ -344,6 +381,19 @@ public void onItemClick(AdapterView> parent, final View view, int position, lo
});
}
+ public void message_dialog_yes_no (String msg, DialogInterface.OnClickListener yesListener) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
+
+ builder.setMessage(msg)
+ .setCancelable(false)
+ .setPositiveButton(getString(R.string.yes), yesListener)
+ .setNegativeButton(getString(R.string.no), new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.cancel();
+ }})
+ .show();
+ }
+
/**
* Executes commands as root user
* @author http://muzikant-android.blogspot.com/2011/02/how-to-get-root-access-and-execute.html
diff --git a/app/src/main/java/org/afhdownloader/SwipeDismissListViewTouchListener.java b/app/src/main/java/org/afhdownloader/SwipeDismissListViewTouchListener.java
new file mode 100644
index 0000000..1fad7b9
--- /dev/null
+++ b/app/src/main/java/org/afhdownloader/SwipeDismissListViewTouchListener.java
@@ -0,0 +1,414 @@
+package org.afhdownloader;
+
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.graphics.Rect;
+import android.os.Build;
+import android.os.SystemClock;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.ViewPropertyAnimator;
+import android.widget.AbsListView;
+import android.widget.ListView;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A {@link View.OnTouchListener} that makes the list items in a {@link ListView}
+ * dismissable. {@link ListView} is given special treatment because by default it handles touches
+ * for its list items... i.e. it's in charge of drawing the pressed state (the list selector),
+ * handling list item clicks, etc.
+ *
+ * After creating the listener, the caller should also call
+ * {@link ListView#setOnScrollListener(AbsListView.OnScrollListener)}, passing
+ * in the scroll listener returned by {@link #makeScrollListener()}. If a scroll listener is
+ * already assigned, the caller should still pass scroll changes through to this listener. This will
+ * ensure that this {@link SwipeDismissListViewTouchListener} is paused during list view
+ * scrolling.
+ *
+ * Example usage:
+ *
+ *
+ * SwipeDismissListViewTouchListener touchListener =
+ * new SwipeDismissListViewTouchListener(
+ * listView,
+ * new SwipeDismissListViewTouchListener.OnDismissCallback() {
+ * public void onDismiss(ListView listView, int[] reverseSortedPositions) {
+ * for (int position : reverseSortedPositions) {
+ * adapter.remove(adapter.getItem(position));
+ * }
+ * adapter.notifyDataSetChanged();
+ * }
+ * });
+ * listView.setOnTouchListener(touchListener);
+ * listView.setOnScrollListener(touchListener.makeScrollListener());
+ *
+ *
+ * This class Requires API level 12 or later due to use of {@link
+ * ViewPropertyAnimator}.
+ *
+ * For a generalized {@link View.OnTouchListener} that makes any view dismissable,
+ * see {@link SwipeDismissTouchListener}.
+ *
+ * @see SwipeDismissTouchListener
+ */
+public class SwipeDismissListViewTouchListener implements View.OnTouchListener {
+ // Cached ViewConfiguration and system-wide constant values
+ private int mSlop;
+ private int mMinFlingVelocity;
+ private int mMaxFlingVelocity;
+ private long mAnimationTime;
+
+ // Fixed properties
+ private ListView mListView;
+ private DismissCallbacks mCallbacks;
+ private int mViewWidth = 1; // 1 and not 0 to prevent dividing by zero
+
+ // Transient properties
+ private List mPendingDismisses = new ArrayList();
+ private int mDismissAnimationRefCount = 0;
+ private float mDownX;
+ private float mDownY;
+ private boolean mSwiping;
+ private int mSwipingSlop;
+ private VelocityTracker mVelocityTracker;
+ private int mDownPosition;
+ private View mDownView;
+ private boolean mPaused;
+
+ /**
+ * The callback interface used by {@link SwipeDismissListViewTouchListener} to inform its client
+ * about a successful dismissal of one or more list item positions.
+ */
+ public interface DismissCallbacks {
+ /**
+ * Called to determine whether the given position can be dismissed.
+ */
+ boolean canDismiss(int position);
+
+ /**
+ * Called when the user has indicated they she would like to dismiss one or more list item
+ * positions.
+ *
+ * @param listView The originating {@link ListView}.
+ * @param reverseSortedPositions An array of positions to dismiss, sorted in descending
+ * order for convenience.
+ */
+ void onDismiss(ListView listView, int[] reverseSortedPositions);
+ }
+
+ /**
+ * Constructs a new swipe-to-dismiss touch listener for the given list view.
+ *
+ * @param listView The list view whose items should be dismissable.
+ * @param callbacks The callback to trigger when the user has indicated that she would like to
+ * dismiss one or more list items.
+ */
+ public SwipeDismissListViewTouchListener(ListView listView, DismissCallbacks callbacks) {
+ ViewConfiguration vc = ViewConfiguration.get(listView.getContext());
+ mSlop = vc.getScaledTouchSlop();
+ mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * 16;
+ mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
+ mAnimationTime = listView.getContext().getResources().getInteger(
+ android.R.integer.config_shortAnimTime);
+ mListView = listView;
+ mCallbacks = callbacks;
+ }
+
+ /**
+ * Enables or disables (pauses or resumes) watching for swipe-to-dismiss gestures.
+ *
+ * @param enabled Whether or not to watch for gestures.
+ */
+ public void setEnabled(boolean enabled) {
+ mPaused = !enabled;
+ }
+
+ /**
+ * Returns an {@link AbsListView.OnScrollListener} to be added to the {@link
+ * ListView} using {@link ListView#setOnScrollListener(AbsListView.OnScrollListener)}.
+ * If a scroll listener is already assigned, the caller should still pass scroll changes through
+ * to this listener. This will ensure that this {@link SwipeDismissListViewTouchListener} is
+ * paused during list view scrolling.
+ *
+ * @see SwipeDismissListViewTouchListener
+ */
+ public AbsListView.OnScrollListener makeScrollListener() {
+ return new AbsListView.OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(AbsListView absListView, int scrollState) {
+ setEnabled(scrollState != AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
+ }
+
+ @Override
+ public void onScroll(AbsListView absListView, int i, int i1, int i2) {
+ }
+ };
+ }
+
+ @TargetApi(12)
+ @Override
+ public boolean onTouch(View view, MotionEvent motionEvent) {
+ if (mViewWidth < 2) {
+ mViewWidth = mListView.getWidth();
+ }
+
+ switch (motionEvent.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN: {
+ if (mPaused) {
+ return false;
+ }
+
+ // TODO: ensure this is a finger, and set a flag
+
+ // Find the child view that was touched (perform a hit test)
+ Rect rect = new Rect();
+ int childCount = mListView.getChildCount();
+ int[] listViewCoords = new int[2];
+ mListView.getLocationOnScreen(listViewCoords);
+ int x = (int) motionEvent.getRawX() - listViewCoords[0];
+ int y = (int) motionEvent.getRawY() - listViewCoords[1];
+ View child;
+ for (int i = 0; i < childCount; i++) {
+ child = mListView.getChildAt(i);
+ child.getHitRect(rect);
+ if (rect.contains(x, y)) {
+ mDownView = child;
+ break;
+ }
+ }
+
+ if (mDownView != null) {
+ mDownX = motionEvent.getRawX();
+ mDownY = motionEvent.getRawY();
+ mDownPosition = mListView.getPositionForView(mDownView);
+ if (mCallbacks.canDismiss(mDownPosition)) {
+ mVelocityTracker = VelocityTracker.obtain();
+ mVelocityTracker.addMovement(motionEvent);
+ } else {
+ mDownView = null;
+ }
+ }
+ return false;
+ }
+
+ case MotionEvent.ACTION_CANCEL: {
+ if (mVelocityTracker == null) {
+ break;
+ }
+
+ if (mDownView != null && mSwiping) {
+ // cancel
+ mDownView.animate()
+ .translationX(0)
+ .alpha(1)
+ .setDuration(mAnimationTime)
+ .setListener(null);
+ }
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ mDownX = 0;
+ mDownY = 0;
+ mDownView = null;
+ mDownPosition = ListView.INVALID_POSITION;
+ mSwiping = false;
+ break;
+ }
+
+ case MotionEvent.ACTION_UP: {
+ if (mVelocityTracker == null) {
+ break;
+ }
+
+ float deltaX = motionEvent.getRawX() - mDownX;
+ mVelocityTracker.addMovement(motionEvent);
+ mVelocityTracker.computeCurrentVelocity(1000);
+ float velocityX = mVelocityTracker.getXVelocity();
+ float absVelocityX = Math.abs(velocityX);
+ float absVelocityY = Math.abs(mVelocityTracker.getYVelocity());
+ boolean dismiss = false;
+ boolean dismissRight = false;
+ if (Math.abs(deltaX) > mViewWidth / 2 && mSwiping) {
+ dismiss = true;
+ dismissRight = deltaX > 0;
+ } else if (mMinFlingVelocity <= absVelocityX && absVelocityX <= mMaxFlingVelocity
+ && absVelocityY < absVelocityX && mSwiping) {
+ // dismiss only if flinging in the same direction as dragging
+ dismiss = (velocityX < 0) == (deltaX < 0);
+ dismissRight = mVelocityTracker.getXVelocity() > 0;
+ }
+ if (dismiss && mDownPosition != ListView.INVALID_POSITION) {
+ // dismiss
+ final View downView = mDownView; // mDownView gets null'd before animation ends
+ final int downPosition = mDownPosition;
+ ++mDismissAnimationRefCount;
+ if (Build.VERSION.SDK_INT >= 12) {
+ mDownView.animate()
+ .translationX(dismissRight ? mViewWidth : -mViewWidth)
+ .alpha(0)
+ .setDuration(mAnimationTime)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ performDismiss(downView, downPosition);
+ }
+ });
+ } else {
+ performDismiss(downView, downPosition);
+ }
+ } else {
+ // cancel
+ if (Build.VERSION.SDK_INT >= 12) {
+ mDownView.animate()
+ .translationX(0)
+ .alpha(1)
+ .setDuration(mAnimationTime)
+ .setListener(null);
+ }
+ }
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ mDownX = 0;
+ mDownY = 0;
+ mDownView = null;
+ mDownPosition = ListView.INVALID_POSITION;
+ mSwiping = false;
+ break;
+ }
+
+ case MotionEvent.ACTION_MOVE: {
+ if (mVelocityTracker == null || mPaused) {
+ break;
+ }
+
+ mVelocityTracker.addMovement(motionEvent);
+ float deltaX = motionEvent.getRawX() - mDownX;
+ float deltaY = motionEvent.getRawY() - mDownY;
+ if (Math.abs(deltaX) > mSlop && Math.abs(deltaY) < Math.abs(deltaX) / 2) {
+ mSwiping = true;
+ mSwipingSlop = (deltaX > 0 ? mSlop : -mSlop);
+ mListView.requestDisallowInterceptTouchEvent(true);
+
+ // Cancel ListView's touch (un-highlighting the item)
+ MotionEvent cancelEvent = MotionEvent.obtain(motionEvent);
+ cancelEvent.setAction(MotionEvent.ACTION_CANCEL |
+ (motionEvent.getActionIndex()
+ << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
+ mListView.onTouchEvent(cancelEvent);
+ cancelEvent.recycle();
+ }
+
+ if (mSwiping) {
+ mDownView.setTranslationX(deltaX - mSwipingSlop);
+ mDownView.setAlpha(Math.max(0f, Math.min(1f,
+ 1f - 2f * Math.abs(deltaX) / mViewWidth)));
+ view.performClick();
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+ }
+
+ class PendingDismissData implements Comparable {
+ public int position;
+ public View view;
+
+ public PendingDismissData(int position, View view) {
+ this.position = position;
+ this.view = view;
+ }
+
+ @Override
+ public int compareTo(PendingDismissData other) {
+ // Sort by descending position
+ return other.position - position;
+ }
+ }
+
+ private void performDismiss(final View dismissView, final int dismissPosition) {
+ // Animate the dismissed list item to zero-height and fire the dismiss callback when
+ // all dismissed list item animations have completed. This triggers layout on each animation
+ // frame; in the future we may want to do something smarter and more performant.
+
+ final ViewGroup.LayoutParams lp = dismissView.getLayoutParams();
+ final int originalHeight = dismissView.getHeight();
+
+ ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 1).setDuration(mAnimationTime);
+
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ --mDismissAnimationRefCount;
+ if (mDismissAnimationRefCount == 0) {
+ // No active animations, process all pending dismisses.
+ // Sort by descending position
+ Collections.sort(mPendingDismisses);
+
+ int[] dismissPositions = new int[mPendingDismisses.size()];
+ for (int i = mPendingDismisses.size() - 1; i >= 0; i--) {
+ dismissPositions[i] = mPendingDismisses.get(i).position;
+ }
+ mCallbacks.onDismiss(mListView, dismissPositions);
+
+ // Reset mDownPosition to avoid MotionEvent.ACTION_UP trying to start a dismiss
+ // animation with a stale position
+ mDownPosition = ListView.INVALID_POSITION;
+
+ ViewGroup.LayoutParams lp;
+ for (PendingDismissData pendingDismiss : mPendingDismisses) {
+ // Reset view presentation
+ pendingDismiss.view.setAlpha(1f);
+ pendingDismiss.view.setTranslationX(0);
+ lp = pendingDismiss.view.getLayoutParams();
+ lp.height = originalHeight;
+ pendingDismiss.view.setLayoutParams(lp);
+ }
+
+ // Send a cancel event
+ long time = SystemClock.uptimeMillis();
+ MotionEvent cancelEvent = MotionEvent.obtain(time, time,
+ MotionEvent.ACTION_CANCEL, 0, 0, 0);
+ mListView.dispatchTouchEvent(cancelEvent);
+
+ mPendingDismisses.clear();
+ }
+ }
+ });
+
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ lp.height = (Integer) valueAnimator.getAnimatedValue();
+ dismissView.setLayoutParams(lp);
+ }
+ });
+
+ mPendingDismisses.add(new PendingDismissData(dismissPosition, dismissView));
+ animator.start();
+ }
+}
diff --git a/app/src/main/java/org/afhdownloader/SwipeDismissTouchListener.java b/app/src/main/java/org/afhdownloader/SwipeDismissTouchListener.java
new file mode 100644
index 0000000..c1edfbc
--- /dev/null
+++ b/app/src/main/java/org/afhdownloader/SwipeDismissTouchListener.java
@@ -0,0 +1,291 @@
+package org.afhdownloader;
+
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.app.ListActivity;
+import android.app.ListFragment;
+import android.os.Build;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ListView;
+
+/**
+ * A {@link View.OnTouchListener} that makes any {@link View} dismissable when the
+ * user swipes (drags her finger) horizontally across the view.
+ *
+ * For {@link ListView} list items that don't manage their own touch events
+ * (i.e. you're using
+ * {@link ListView#setOnItemClickListener(AdapterView.OnItemClickListener)}
+ * or an equivalent listener on {@link ListActivity} or
+ * {@link ListFragment}, use {@link SwipeDismissListViewTouchListener} instead.
+ *
+ * Example usage:
+ *
+ *
+ * view.setOnTouchListener(new SwipeDismissTouchListener(
+ * view,
+ * null, // Optional token/cookie object
+ * new SwipeDismissTouchListener.OnDismissCallback() {
+ * public void onDismiss(View view, Object token) {
+ * parent.removeView(view);
+ * }
+ * }));
+ *
+ *
+ * This class Requires API level 12 or later due to use of {@link
+ * android.view.ViewPropertyAnimator}.
+ *
+ * @see SwipeDismissListViewTouchListener
+ */
+public class SwipeDismissTouchListener implements View.OnTouchListener {
+ // Cached ViewConfiguration and system-wide constant values
+ private int mSlop;
+ private int mMinFlingVelocity;
+ private int mMaxFlingVelocity;
+ private long mAnimationTime;
+
+ // Fixed properties
+ private View mView;
+ private DismissCallbacks mCallbacks;
+ private int mViewWidth = 1; // 1 and not 0 to prevent dividing by zero
+
+ // Transient properties
+ private float mDownX;
+ private float mDownY;
+ private boolean mSwiping;
+ private int mSwipingSlop;
+ private Object mToken;
+ private VelocityTracker mVelocityTracker;
+ private float mTranslationX;
+
+ /**
+ * The callback interface used by {@link SwipeDismissTouchListener} to inform its client
+ * about a successful dismissal of the view for which it was created.
+ */
+ public interface DismissCallbacks {
+ /**
+ * Called to determine whether the view can be dismissed.
+ */
+ boolean canDismiss(Object token);
+
+ /**
+ * Called when the user has indicated they she would like to dismiss the view.
+ *
+ * @param view The originating {@link View} to be dismissed.
+ * @param token The optional token passed to this object's constructor.
+ */
+ void onDismiss(View view, Object token);
+ }
+
+ /**
+ * Constructs a new swipe-to-dismiss touch listener for the given view.
+ *
+ * @param view The view to make dismissable.
+ * @param token An optional token/cookie object to be passed through to the callback.
+ * @param callbacks The callback to trigger when the user has indicated that she would like to
+ * dismiss this view.
+ */
+ public SwipeDismissTouchListener(View view, Object token, DismissCallbacks callbacks) {
+ ViewConfiguration vc = ViewConfiguration.get(view.getContext());
+ mSlop = vc.getScaledTouchSlop();
+ mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * 16;
+ mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
+ mAnimationTime = view.getContext().getResources().getInteger(
+ android.R.integer.config_shortAnimTime);
+ mView = view;
+ mToken = token;
+ mCallbacks = callbacks;
+ }
+
+ @TargetApi(12)
+ @Override
+ public boolean onTouch(View view, MotionEvent motionEvent) {
+ // offset because the view is translated during swipe
+ motionEvent.offsetLocation(mTranslationX, 0);
+
+ if (mViewWidth < 2) {
+ mViewWidth = mView.getWidth();
+ }
+
+ switch (motionEvent.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN: {
+ // TODO: ensure this is a finger, and set a flag
+ mDownX = motionEvent.getRawX();
+ mDownY = motionEvent.getRawY();
+ if (mCallbacks.canDismiss(mToken)) {
+ mVelocityTracker = VelocityTracker.obtain();
+ mVelocityTracker.addMovement(motionEvent);
+ }
+ return false;
+ }
+
+ case MotionEvent.ACTION_UP: {
+ if (mVelocityTracker == null) {
+ break;
+ }
+
+ float deltaX = motionEvent.getRawX() - mDownX;
+ mVelocityTracker.addMovement(motionEvent);
+ mVelocityTracker.computeCurrentVelocity(1000);
+ float velocityX = mVelocityTracker.getXVelocity();
+ float absVelocityX = Math.abs(velocityX);
+ float absVelocityY = Math.abs(mVelocityTracker.getYVelocity());
+ boolean dismiss = false;
+ boolean dismissRight = false;
+ if (Math.abs(deltaX) > mViewWidth / 2 && mSwiping) {
+ dismiss = true;
+ dismissRight = deltaX > 0;
+ } else if (mMinFlingVelocity <= absVelocityX && absVelocityX <= mMaxFlingVelocity
+ && absVelocityY < absVelocityX
+ && absVelocityY < absVelocityX && mSwiping) {
+ // dismiss only if flinging in the same direction as dragging
+ dismiss = (velocityX < 0) == (deltaX < 0);
+ dismissRight = mVelocityTracker.getXVelocity() > 0;
+ }
+ if (dismiss) {
+ // dismiss
+ if (Build.VERSION.SDK_INT >= 12) {
+ mView.animate()
+ .translationX(dismissRight ? mViewWidth : -mViewWidth)
+ .alpha(0)
+ .setDuration(mAnimationTime)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ performDismiss();
+ }
+ });
+ } else {
+ performDismiss();
+ }
+ } else if (mSwiping) {
+ // cancel
+ if (Build.VERSION.SDK_INT >= 12) {
+ mView.animate()
+ .translationX(0)
+ .alpha(1)
+ .setDuration(mAnimationTime)
+ .setListener(null);
+ }
+ }
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ mTranslationX = 0;
+ mDownX = 0;
+ mDownY = 0;
+ mSwiping = false;
+ break;
+ }
+
+ case MotionEvent.ACTION_CANCEL: {
+ if (mVelocityTracker == null) {
+ break;
+ }
+ if (Build.VERSION.SDK_INT >= 12) {
+ mView.animate()
+ .translationX(0)
+ .alpha(1)
+ .setDuration(mAnimationTime)
+ .setListener(null);
+ }
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ mTranslationX = 0;
+ mDownX = 0;
+ mDownY = 0;
+ mSwiping = false;
+ break;
+ }
+
+ case MotionEvent.ACTION_MOVE: {
+ if (mVelocityTracker == null) {
+ break;
+ }
+
+ mVelocityTracker.addMovement(motionEvent);
+ float deltaX = motionEvent.getRawX() - mDownX;
+ float deltaY = motionEvent.getRawY() - mDownY;
+ if (Math.abs(deltaX) > mSlop && Math.abs(deltaY) < Math.abs(deltaX) / 2) {
+ mSwiping = true;
+ mSwipingSlop = (deltaX > 0 ? mSlop : -mSlop);
+ mView.getParent().requestDisallowInterceptTouchEvent(true);
+
+ // Cancel listview's touch
+ MotionEvent cancelEvent = MotionEvent.obtain(motionEvent);
+ cancelEvent.setAction(MotionEvent.ACTION_CANCEL |
+ (motionEvent.getActionIndex() <<
+ MotionEvent.ACTION_POINTER_INDEX_SHIFT));
+ mView.onTouchEvent(cancelEvent);
+ cancelEvent.recycle();
+ }
+
+ if (mSwiping) {
+ mTranslationX = deltaX;
+ mView.setTranslationX(deltaX - mSwipingSlop);
+ // TODO: use an ease-out interpolator or such
+ mView.setAlpha(Math.max(0f, Math.min(1f,
+ 1f - 2f * Math.abs(deltaX) / mViewWidth)));
+ view.performClick();
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+ }
+
+ private void performDismiss() {
+ // Animate the dismissed view to zero-height and then fire the dismiss callback.
+ // This triggers layout on each animation frame; in the future we may want to do something
+ // smarter and more performant.
+
+ final ViewGroup.LayoutParams lp = mView.getLayoutParams();
+ final int originalHeight = mView.getHeight();
+
+ ValueAnimator animator = ValueAnimator.ofInt(originalHeight, 1).setDuration(mAnimationTime);
+
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mCallbacks.onDismiss(mView, mToken);
+ // Reset view presentation
+ mView.setAlpha(1f);
+ mView.setTranslationX(0);
+ lp.height = originalHeight;
+ mView.setLayoutParams(lp);
+ }
+ });
+
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ lp.height = (Integer) valueAnimator.getAnimatedValue();
+ mView.setLayoutParams(lp);
+ }
+ });
+
+ animator.start();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml
index bfee718..7dddc43 100644
--- a/app/src/main/res/values/donottranslate.xml
+++ b/app/src/main/res/values/donottranslate.xml
@@ -8,5 +8,6 @@
39788
3
0
-
+ .calc.md5
+ .md5
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e547049..ca216db 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -35,5 +35,8 @@
HTTP Mirror POST Data
MD5 indicator
+ Yes
+ No
+ Delete
From f8ed27c3289dc2bbbc226950a576eb235f1d99a2 Mon Sep 17 00:00:00 2001
From: daktak
Date: Tue, 12 Jul 2016 13:00:04 +1000
Subject: [PATCH 3/3] Heading exception clause
---
app/src/main/java/org/afhdownloader/Download.java | 12 +++++++++---
app/src/main/res/values/donottranslate.xml | 1 +
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/app/src/main/java/org/afhdownloader/Download.java b/app/src/main/java/org/afhdownloader/Download.java
index 2e4c439..f9aa2bb 100644
--- a/app/src/main/java/org/afhdownloader/Download.java
+++ b/app/src/main/java/org/afhdownloader/Download.java
@@ -26,6 +26,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.ExecutionException;
/**
@@ -95,9 +96,14 @@ public String parseUrl(String url) {
urls.add(link.attr("href"));
}
//set title
- Elements h1s = doc.select("h1");
- String h1 = h1s.get(0).ownText();
- urls.add(h1);
+ String head = getString(R.string.app_name);
+ try {
+ Elements h1s = doc.select(getString(R.string.head_selector));
+ head = h1s.get(0).ownText();
+ } catch (Exception e) {
+ Log.d(LOGTAG,"Unable to find heading");
+ }
+ urls.add(head);
} catch (Throwable t) {
Log.e(LOGTAG,t.getMessage());
diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml
index 7dddc43..3039600 100644
--- a/app/src/main/res/values/donottranslate.xml
+++ b/app/src/main/res/values/donottranslate.xml
@@ -10,4 +10,5 @@
0
.calc.md5
.md5
+ h1