Update dependencies
Settings activity refactor
This commit is contained in:
248 files changed
+8700
-8980
No files matched your search
@@ -30,6 +30,7 @@ import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.IdRes;
|
||||
import androidx.annotation.LayoutRes;
|
||||
@@ -48,7 +49,7 @@ import androidx.core.app.NavUtils;
|
||||
import androidx.core.app.TaskStackBuilder;
|
||||
|
||||
public abstract class AppCompatPreferenceActivity extends PreferenceActivity implements AppCompatCallback,
|
||||
TaskStackBuilder.SupportParentable, ActionBarDrawerToggle.DelegateProvider {
|
||||
TaskStackBuilder.SupportParentable, ActionBarDrawerToggle.DelegateProvider {
|
||||
|
||||
private AppCompatDelegate mDelegate;
|
||||
private int mThemeId = 0;
|
||||
@@ -187,7 +188,7 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity imp
|
||||
|
||||
final ActionBar ab = getSupportActionBar();
|
||||
if (item.getItemId() == android.R.id.home && ab != null &&
|
||||
(ab.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
|
||||
(ab.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0) {
|
||||
return onSupportNavigateUp();
|
||||
}
|
||||
return false;
|
||||
@@ -213,9 +214,8 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity imp
|
||||
* </p>
|
||||
*
|
||||
* @param featureId The desired feature as defined in
|
||||
* {@link android.view.Window} or {@link androidx.core.view.WindowCompat}.
|
||||
* {@link android.view.Window} or {@link androidx.core.view.WindowCompat}.
|
||||
* @return Returns true if the requested feature is supported and now enabled.
|
||||
*
|
||||
* @see android.app.Activity#requestWindowFeature
|
||||
* @see android.view.Window#requestFeature
|
||||
*/
|
||||
@@ -311,7 +311,7 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity imp
|
||||
/**
|
||||
* Support version of {@link #onCreateNavigateUpTaskStack(android.app.TaskStackBuilder)}.
|
||||
* This method will be called on all platform versions.
|
||||
*
|
||||
* <p>
|
||||
* Define the synthetic task stack that will be generated during Up navigation from
|
||||
* a different task.
|
||||
*
|
||||
@@ -338,7 +338,7 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity imp
|
||||
/**
|
||||
* Support version of {@link #onPrepareNavigateUpTaskStack(android.app.TaskStackBuilder)}.
|
||||
* This method will be called on all platform versions.
|
||||
*
|
||||
* <p>
|
||||
* Prepare the synthetic task stack that will be generated during Up navigation
|
||||
* from a different task.
|
||||
*
|
||||
@@ -374,7 +374,7 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity imp
|
||||
* {@link #supportNavigateUpTo(android.content.Intent)} for help implementing custom Up navigation.</p>
|
||||
*
|
||||
* @return true if Up navigation completed successfully and this Activity was finished,
|
||||
* false otherwise.
|
||||
* false otherwise.
|
||||
*/
|
||||
public boolean onSupportNavigateUp() {
|
||||
Intent upIntent = getSupportParentActivityIntent();
|
||||
@@ -429,7 +429,7 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity imp
|
||||
*
|
||||
* @param targetIntent An intent representing the target destination for up navigation
|
||||
* @return true if navigating up should recreate a new task stack, false if the same task
|
||||
* should be used for the destination
|
||||
* should be used for the destination
|
||||
*/
|
||||
public boolean supportShouldUpRecreateTask(@NonNull Intent targetIntent) {
|
||||
return NavUtils.shouldUpRecreateTask(this, targetIntent);
|
||||
@@ -504,7 +504,7 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity imp
|
||||
final int keyCode = event.getKeyCode();
|
||||
final ActionBar actionBar = getSupportActionBar();
|
||||
if (keyCode == KeyEvent.KEYCODE_MENU
|
||||
&& actionBar != null && actionBar.onMenuKeyEvent(event)) {
|
||||
&& actionBar != null && actionBar.onMenuKeyEvent(event)) {
|
||||
return true;
|
||||
}
|
||||
return super.dispatchKeyEvent(event);
|
||||
@@ -526,15 +526,13 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity imp
|
||||
*/
|
||||
private boolean performMenuItemShortcut(int keycode, KeyEvent event) {
|
||||
if (!(Build.VERSION.SDK_INT >= 26) && !event.isCtrlPressed()
|
||||
&& !KeyEvent.metaStateHasNoModifiers(event.getMetaState())
|
||||
&& event.getRepeatCount() == 0
|
||||
&& !KeyEvent.isModifierKey(event.getKeyCode())) {
|
||||
&& !KeyEvent.metaStateHasNoModifiers(event.getMetaState())
|
||||
&& event.getRepeatCount() == 0
|
||||
&& !KeyEvent.isModifierKey(event.getKeyCode())) {
|
||||
final Window currentWindow = getWindow();
|
||||
if (currentWindow != null && currentWindow.getDecorView() != null) {
|
||||
final View decorView = currentWindow.getDecorView();
|
||||
if (decorView.dispatchKeyShortcutEvent(event)) {
|
||||
return true;
|
||||
}
|
||||
return decorView.dispatchKeyShortcutEvent(event);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -553,7 +551,7 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity imp
|
||||
public void openOptionsMenu() {
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if (getWindow().hasFeature(Window.FEATURE_OPTIONS_PANEL)
|
||||
&& (actionBar == null || !actionBar.openOptionsMenu())) {
|
||||
&& (actionBar == null || !actionBar.openOptionsMenu())) {
|
||||
super.openOptionsMenu();
|
||||
}
|
||||
}
|
||||
@@ -563,7 +561,7 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity imp
|
||||
public void closeOptionsMenu() {
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if (getWindow().hasFeature(Window.FEATURE_OPTIONS_PANEL)
|
||||
&& (actionBar == null || !actionBar.closeOptionsMenu())) {
|
||||
&& (actionBar == null || !actionBar.closeOptionsMenu())) {
|
||||
super.closeOptionsMenu();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,9 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
|
||||
public class CheckBoxDialogBuilder extends AlertDialog.Builder {
|
||||
|
||||
@@ -25,7 +25,9 @@ import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
import com.hippo.ehviewer.R;
|
||||
|
||||
|
||||
@@ -24,7 +24,9 @@ import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ListView;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.yorozuya.ViewUtils;
|
||||
|
||||
@@ -36,7 +38,7 @@ public class ListCheckBoxDialogBuilder extends AlertDialog.Builder {
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
public ListCheckBoxDialogBuilder(Context context, CharSequence[] items,
|
||||
final OnItemClickListener listener, String checkText, boolean checked) {
|
||||
final OnItemClickListener listener, String checkText, boolean checked) {
|
||||
super(context);
|
||||
View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_list_checkbox_builder, null);
|
||||
setView(view);
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 Hippo Seven
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.hippo.app;
|
||||
|
||||
import android.content.Context;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import com.hippo.ehviewer.R;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class PrettyPreferenceActivity extends AppCompatPreferenceActivity {
|
||||
|
||||
@Override
|
||||
public void setListAdapter(ListAdapter adapter) {
|
||||
if (adapter == null) {
|
||||
super.setListAdapter(null);
|
||||
return;
|
||||
}
|
||||
|
||||
int count = adapter.getCount();
|
||||
List<PreferenceActivity.Header> headers = new ArrayList<>(count);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
headers.add((PreferenceActivity.Header) adapter.getItem(i));
|
||||
}
|
||||
|
||||
super.setListAdapter(new HeaderAdapter(this, headers, R.layout.item_preference_header, true));
|
||||
}
|
||||
|
||||
private static class HeaderAdapter extends ArrayAdapter<PreferenceActivity.Header> {
|
||||
private static class HeaderViewHolder {
|
||||
ImageView icon;
|
||||
TextView title;
|
||||
TextView summary;
|
||||
}
|
||||
|
||||
private LayoutInflater mInflater;
|
||||
private int mLayoutResId;
|
||||
private boolean mRemoveIconIfEmpty;
|
||||
|
||||
private HeaderAdapter(Context context, List<PreferenceActivity.Header> objects, int layoutResId,
|
||||
boolean removeIconBehavior) {
|
||||
super(context, 0, objects);
|
||||
mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
mLayoutResId = layoutResId;
|
||||
mRemoveIconIfEmpty = removeIconBehavior;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
|
||||
HeaderViewHolder holder;
|
||||
View view;
|
||||
|
||||
if (convertView == null) {
|
||||
view = mInflater.inflate(mLayoutResId, parent, false);
|
||||
holder = new HeaderViewHolder();
|
||||
holder.icon = view.findViewById(android.R.id.icon);
|
||||
holder.title = view.findViewById(android.R.id.title);
|
||||
holder.summary = view.findViewById(android.R.id.summary);
|
||||
view.setTag(holder);
|
||||
} else {
|
||||
view = convertView;
|
||||
holder = (HeaderViewHolder) view.getTag();
|
||||
}
|
||||
|
||||
// All view fields must be updated every time, because the view may be recycled
|
||||
PreferenceActivity.Header header = getItem(position);
|
||||
if (mRemoveIconIfEmpty) {
|
||||
if (header.iconRes == 0) {
|
||||
holder.icon.setVisibility(View.GONE);
|
||||
} else {
|
||||
holder.icon.setVisibility(View.VISIBLE);
|
||||
holder.icon.setImageResource(header.iconRes);
|
||||
}
|
||||
} else {
|
||||
holder.icon.setImageResource(header.iconRes);
|
||||
}
|
||||
holder.title.setText(header.getTitle(getContext().getResources()));
|
||||
CharSequence summary = header.getSummary(getContext().getResources());
|
||||
if (!TextUtils.isEmpty(summary)) {
|
||||
holder.summary.setVisibility(View.VISIBLE);
|
||||
holder.summary.setText(summary);
|
||||
} else {
|
||||
holder.summary.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,8 +28,11 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
|
||||
/**
|
||||
@@ -40,12 +43,14 @@ import java.text.NumberFormat;
|
||||
*/
|
||||
public class ProgressDialog extends AlertDialog {
|
||||
|
||||
/** Creates a ProgressDialog with a circular, spinning progress
|
||||
/**
|
||||
* Creates a ProgressDialog with a circular, spinning progress
|
||||
* bar. This is the default.
|
||||
*/
|
||||
public static final int STYLE_SPINNER = 0;
|
||||
|
||||
/** Creates a ProgressDialog with a horizontal progress bar.
|
||||
/**
|
||||
* Creates a ProgressDialog with a horizontal progress bar.
|
||||
*/
|
||||
public static final int STYLE_HORIZONTAL = 1;
|
||||
|
||||
@@ -81,30 +86,24 @@ public class ProgressDialog extends AlertDialog {
|
||||
initFormats();
|
||||
}
|
||||
|
||||
private void initFormats() {
|
||||
mProgressNumberFormat = "%1d/%2d";
|
||||
mProgressPercentFormat = NumberFormat.getPercentInstance();
|
||||
mProgressPercentFormat.setMaximumFractionDigits(0);
|
||||
}
|
||||
|
||||
public static ProgressDialog show(Context context, CharSequence title,
|
||||
CharSequence message) {
|
||||
CharSequence message) {
|
||||
return show(context, title, message, false);
|
||||
}
|
||||
|
||||
public static ProgressDialog show(Context context, CharSequence title,
|
||||
CharSequence message, boolean indeterminate) {
|
||||
CharSequence message, boolean indeterminate) {
|
||||
return show(context, title, message, indeterminate, false, null);
|
||||
}
|
||||
|
||||
public static ProgressDialog show(Context context, CharSequence title,
|
||||
CharSequence message, boolean indeterminate, boolean cancelable) {
|
||||
CharSequence message, boolean indeterminate, boolean cancelable) {
|
||||
return show(context, title, message, indeterminate, cancelable, null);
|
||||
}
|
||||
|
||||
public static ProgressDialog show(Context context, CharSequence title,
|
||||
CharSequence message, boolean indeterminate,
|
||||
boolean cancelable, OnCancelListener cancelListener) {
|
||||
CharSequence message, boolean indeterminate,
|
||||
boolean cancelable, OnCancelListener cancelListener) {
|
||||
ProgressDialog dialog = new ProgressDialog(context);
|
||||
dialog.setTitle(title);
|
||||
dialog.setMessage(message);
|
||||
@@ -115,6 +114,12 @@ public class ProgressDialog extends AlertDialog {
|
||||
return dialog;
|
||||
}
|
||||
|
||||
private void initFormats() {
|
||||
mProgressNumberFormat = "%1d/%2d";
|
||||
mProgressPercentFormat = NumberFormat.getPercentInstance();
|
||||
mProgressPercentFormat.setMaximumFractionDigits(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
LayoutInflater inflater = LayoutInflater.from(getContext());
|
||||
@@ -200,6 +205,13 @@ public class ProgressDialog extends AlertDialog {
|
||||
mHasStarted = false;
|
||||
}
|
||||
|
||||
public int getProgress() {
|
||||
if (mProgress != null) {
|
||||
return mProgress.getProgress();
|
||||
}
|
||||
return mProgressVal;
|
||||
}
|
||||
|
||||
public void setProgress(int value) {
|
||||
if (mHasStarted) {
|
||||
mProgress.setProgress(value);
|
||||
@@ -209,6 +221,13 @@ public class ProgressDialog extends AlertDialog {
|
||||
}
|
||||
}
|
||||
|
||||
public int getSecondaryProgress() {
|
||||
if (mProgress != null) {
|
||||
return mProgress.getSecondaryProgress();
|
||||
}
|
||||
return mSecondaryProgressVal;
|
||||
}
|
||||
|
||||
public void setSecondaryProgress(int secondaryProgress) {
|
||||
if (mProgress != null) {
|
||||
mProgress.setSecondaryProgress(secondaryProgress);
|
||||
@@ -218,20 +237,6 @@ public class ProgressDialog extends AlertDialog {
|
||||
}
|
||||
}
|
||||
|
||||
public int getProgress() {
|
||||
if (mProgress != null) {
|
||||
return mProgress.getProgress();
|
||||
}
|
||||
return mProgressVal;
|
||||
}
|
||||
|
||||
public int getSecondaryProgress() {
|
||||
if (mProgress != null) {
|
||||
return mProgress.getSecondaryProgress();
|
||||
}
|
||||
return mSecondaryProgressVal;
|
||||
}
|
||||
|
||||
public int getMax() {
|
||||
if (mProgress != null) {
|
||||
return mProgress.getMax();
|
||||
@@ -282,6 +287,13 @@ public class ProgressDialog extends AlertDialog {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isIndeterminate() {
|
||||
if (mProgress != null) {
|
||||
return mProgress.isIndeterminate();
|
||||
}
|
||||
return mIndeterminate;
|
||||
}
|
||||
|
||||
public void setIndeterminate(boolean indeterminate) {
|
||||
if (mProgress != null) {
|
||||
mProgress.setIndeterminate(indeterminate);
|
||||
@@ -290,13 +302,6 @@ public class ProgressDialog extends AlertDialog {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isIndeterminate() {
|
||||
if (mProgress != null) {
|
||||
return mProgress.isIndeterminate();
|
||||
}
|
||||
return mIndeterminate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessage(CharSequence message) {
|
||||
if (mProgress != null) {
|
||||
@@ -318,9 +323,10 @@ public class ProgressDialog extends AlertDialog {
|
||||
* Change the format of the small text showing current and maximum units
|
||||
* of progress. The default is "%1d/%2d".
|
||||
* Should not be called during the number is progressing.
|
||||
*
|
||||
* @param format A string passed to {@link String#format String.format()};
|
||||
* use "%1d" for the current number and "%2d" for the maximum. If null,
|
||||
* nothing will be shown.
|
||||
* use "%1d" for the current number and "%2d" for the maximum. If null,
|
||||
* nothing will be shown.
|
||||
*/
|
||||
public void setProgressNumberFormat(String format) {
|
||||
mProgressNumberFormat = format;
|
||||
@@ -332,8 +338,9 @@ public class ProgressDialog extends AlertDialog {
|
||||
* The default is
|
||||
* {@link NumberFormat#getPercentInstance() NumberFormat.getPercentageInstnace().}
|
||||
* Should not be called during the number is progressing.
|
||||
*
|
||||
* @param format An instance of a {@link NumberFormat} to generate the
|
||||
* percentage text. If null, nothing will be shown.
|
||||
* percentage text. If null, nothing will be shown.
|
||||
*/
|
||||
public void setProgressPercentFormat(NumberFormat format) {
|
||||
mProgressPercentFormat = format;
|
||||
|
||||
@@ -26,36 +26,37 @@ import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
import android.os.LocaleList;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class ContextLocalWrapper extends ContextWrapper {
|
||||
|
||||
public ContextLocalWrapper(Context base) {
|
||||
super(base);
|
||||
}
|
||||
|
||||
public static ContextLocalWrapper wrap(Context context, Locale newLocale) {
|
||||
Resources res = context.getResources();
|
||||
Configuration configuration = res.getConfiguration();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
configuration.setLocale(newLocale);
|
||||
|
||||
LocaleList localeList = new LocaleList(newLocale);
|
||||
LocaleList.setDefault(localeList);
|
||||
configuration.setLocales(localeList);
|
||||
|
||||
context = context.createConfigurationContext(configuration);
|
||||
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
configuration.setLocale(newLocale);
|
||||
context = context.createConfigurationContext(configuration);
|
||||
|
||||
} else {
|
||||
configuration.locale = newLocale;
|
||||
res.updateConfiguration(configuration, res.getDisplayMetrics());
|
||||
public ContextLocalWrapper(Context base) {
|
||||
super(base);
|
||||
}
|
||||
|
||||
return new ContextLocalWrapper(context);
|
||||
}
|
||||
public static ContextLocalWrapper wrap(Context context, Locale newLocale) {
|
||||
Resources res = context.getResources();
|
||||
Configuration configuration = res.getConfiguration();
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
configuration.setLocale(newLocale);
|
||||
|
||||
LocaleList localeList = new LocaleList(newLocale);
|
||||
LocaleList.setDefault(localeList);
|
||||
configuration.setLocales(localeList);
|
||||
|
||||
context = context.createConfigurationContext(configuration);
|
||||
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
configuration.setLocale(newLocale);
|
||||
context = context.createConfigurationContext(configuration);
|
||||
|
||||
} else {
|
||||
configuration.locale = newLocale;
|
||||
res.updateConfiguration(configuration, res.getDisplayMetrics());
|
||||
}
|
||||
|
||||
return new ContextLocalWrapper(context);
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,6 @@
|
||||
|
||||
package com.hippo.content;
|
||||
|
||||
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
|
||||
import static org.xmlpull.v1.XmlPullParser.START_TAG;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentValues;
|
||||
@@ -36,13 +33,19 @@ import android.os.ParcelFileDescriptor;
|
||||
import android.provider.MediaStore;
|
||||
import android.text.TextUtils;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
|
||||
import static org.xmlpull.v1.XmlPullParser.START_TAG;
|
||||
|
||||
/**
|
||||
* FileProvider is a special subclass of {@link ContentProvider} that facilitates secure sharing
|
||||
@@ -91,7 +94,7 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||
* >android:grantUriPermissions</a> attribute to <code>true</code>, to allow you
|
||||
* to grant temporary access to files. For example:
|
||||
* <pre class="prettyprint">
|
||||
*<manifest>
|
||||
* <manifest>
|
||||
* ...
|
||||
* <application>
|
||||
* ...
|
||||
@@ -104,7 +107,7 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||
* </provider>
|
||||
* ...
|
||||
* </application>
|
||||
*</manifest></pre>
|
||||
* </manifest></pre>
|
||||
* <p>
|
||||
* If you want to override any of the default behavior of FileProvider methods, extend
|
||||
* the FileProvider class and use the fully-qualified class name in the <code>android:name</code>
|
||||
@@ -116,19 +119,19 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||
* For example, the following <code>paths</code> element tells FileProvider that you intend to
|
||||
* request content URIs for the <code>images/</code> subdirectory of your private file area.
|
||||
* <pre class="prettyprint">
|
||||
*<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
* <paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
* <files-path name="my_images" path="images/"/>
|
||||
* ...
|
||||
*</paths>
|
||||
*</pre>
|
||||
* </paths>
|
||||
* </pre>
|
||||
* <p>
|
||||
* The <code><paths></code> element must contain one or more of the following child elements:
|
||||
* </p>
|
||||
* <dl>
|
||||
* <dt>
|
||||
* <pre class="prettyprint">
|
||||
*<files-path name="<i>name</i>" path="<i>path</i>" />
|
||||
*</pre>
|
||||
* <files-path name="<i>name</i>" path="<i>path</i>" />
|
||||
* </pre>
|
||||
* </dt>
|
||||
* <dd>
|
||||
* Represents files in the <code>files/</code> subdirectory of your app's internal storage
|
||||
@@ -137,8 +140,8 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||
* </dd>
|
||||
* <dt>
|
||||
* <pre>
|
||||
*<cache-path name="<i>name</i>" path="<i>path</i>" />
|
||||
*</pre>
|
||||
* <cache-path name="<i>name</i>" path="<i>path</i>" />
|
||||
* </pre>
|
||||
* <dt>
|
||||
* <dd>
|
||||
* Represents files in the cache subdirectory of your app's internal storage area. The root path
|
||||
@@ -147,8 +150,8 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||
* </dd>
|
||||
* <dt>
|
||||
* <pre class="prettyprint">
|
||||
*<external-path name="<i>name</i>" path="<i>path</i>" />
|
||||
*</pre>
|
||||
* <external-path name="<i>name</i>" path="<i>path</i>" />
|
||||
* </pre>
|
||||
* </dt>
|
||||
* <dd>
|
||||
* Represents files in the root of the external storage area. The root path of this subdirectory
|
||||
@@ -157,8 +160,8 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||
* </dd>
|
||||
* <dt>
|
||||
* <pre class="prettyprint">
|
||||
*<external-files-path name="<i>name</i>" path="<i>path</i>" />
|
||||
*</pre>
|
||||
* <external-files-path name="<i>name</i>" path="<i>path</i>" />
|
||||
* </pre>
|
||||
* </dt>
|
||||
* <dd>
|
||||
* Represents files in the root of your app's external storage area. The root path of this
|
||||
@@ -167,8 +170,8 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||
* </dd>
|
||||
* <dt>
|
||||
* <pre class="prettyprint">
|
||||
*<external-cache-path name="<i>name</i>" path="<i>path</i>" />
|
||||
*</pre>
|
||||
* <external-cache-path name="<i>name</i>" path="<i>path</i>" />
|
||||
* </pre>
|
||||
* </dt>
|
||||
* <dd>
|
||||
* Represents files in the root of your app's external cache area. The root path of this
|
||||
@@ -203,11 +206,11 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||
* You must specify a child element of <code><paths></code> for each directory that contains
|
||||
* files for which you want content URIs. For example, these XML elements specify two directories:
|
||||
* <pre class="prettyprint">
|
||||
*<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
* <paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
* <files-path name="my_images" path="images/"/>
|
||||
* <files-path name="my_docs" path="docs/"/>
|
||||
*</paths>
|
||||
*</pre>
|
||||
* </paths>
|
||||
* </pre>
|
||||
* <p>
|
||||
* Put the <code><paths></code> element and its children in an XML file in your project.
|
||||
* For example, you can add them to a new file called <code>res/xml/file_paths.xml</code>.
|
||||
@@ -219,7 +222,7 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||
* to <code>@xml/file_paths</code> (notice that you don't specify the <code>.xml</code>
|
||||
* extension). For example:
|
||||
* <pre class="prettyprint">
|
||||
*<provider
|
||||
* <provider
|
||||
* android:name="android.support.v4.content.FileProvider"
|
||||
* android:authorities="com.mydomain.fileprovider"
|
||||
* android:exported="false"
|
||||
@@ -227,8 +230,8 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||
* <meta-data
|
||||
* android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
* android:resource="@xml/file_paths" />
|
||||
*</provider>
|
||||
*</pre>
|
||||
* </provider>
|
||||
* </pre>
|
||||
* <h3 id="GetUri">Generating the Content URI for a File</h3>
|
||||
* <p>
|
||||
* To share a file with another app using a content URI, your app has to generate the content URI.
|
||||
@@ -245,10 +248,10 @@ import org.xmlpull.v1.XmlPullParserException;
|
||||
* <code>default_image.jpg</code> in the <code>images/</code> subdirectory of your internal storage
|
||||
* add the following code:
|
||||
* <pre class="prettyprint">
|
||||
*File imagePath = new File(Context.getFilesDir(), "images");
|
||||
*File newFile = new File(imagePath, "default_image.jpg");
|
||||
*Uri contentUri = getUriForFile(getContext(), "com.mydomain.fileprovider", newFile);
|
||||
*</pre>
|
||||
* File imagePath = new File(Context.getFilesDir(), "images");
|
||||
* File newFile = new File(imagePath, "default_image.jpg");
|
||||
* Uri contentUri = getUriForFile(getContext(), "com.mydomain.fileprovider", newFile);
|
||||
* </pre>
|
||||
* As a result of the previous snippet,
|
||||
* {@link #getUriForFile(Context, String, File) getUriForFile()} returns the content URI
|
||||
* <code>content://com.mydomain.fileprovider/my_images/default_image.jpg</code>.
|
||||
@@ -348,37 +351,6 @@ public class FileProvider extends ContentProvider {
|
||||
|
||||
private PathStrategy mStrategy;
|
||||
|
||||
/**
|
||||
* The default FileProvider implementation does not need to be initialized. If you want to
|
||||
* override this method, you must provide your own subclass of FileProvider.
|
||||
*/
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* After the FileProvider is instantiated, this method is called to provide the system with
|
||||
* information about the provider.
|
||||
*
|
||||
* @param context A {@link Context} for the current component.
|
||||
* @param info A {@link ProviderInfo} for the new provider.
|
||||
*/
|
||||
@Override
|
||||
public void attachInfo(Context context, ProviderInfo info) {
|
||||
super.attachInfo(context, info);
|
||||
|
||||
// Sanity check our security
|
||||
if (info.exported) {
|
||||
throw new SecurityException("Provider must not be exported");
|
||||
}
|
||||
if (!info.grantUriPermissions) {
|
||||
throw new SecurityException("Provider must grant uri permissions");
|
||||
}
|
||||
|
||||
mStrategy = getPathStrategy(context, info.authority);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a content URI for a given {@link File}. Specific temporary
|
||||
* permissions for the content URI can be set with
|
||||
@@ -390,168 +362,20 @@ public class FileProvider extends ContentProvider {
|
||||
* <code>content</code> {@link Uri} for file paths defined in their <code><paths></code>
|
||||
* meta-data element. See the Class Overview for more information.
|
||||
*
|
||||
* @param context A {@link Context} for the current component.
|
||||
* @param context A {@link Context} for the current component.
|
||||
* @param authority The authority of a {@link FileProvider} defined in a
|
||||
* {@code <provider>} element in your app's manifest.
|
||||
* @param file A {@link File} pointing to the filename for which you want a
|
||||
* <code>content</code> {@link Uri}.
|
||||
* {@code <provider>} element in your app's manifest.
|
||||
* @param file A {@link File} pointing to the filename for which you want a
|
||||
* <code>content</code> {@link Uri}.
|
||||
* @return A content URI for the file.
|
||||
* @throws IllegalArgumentException When the given {@link File} is outside
|
||||
* the paths supported by the provider.
|
||||
* the paths supported by the provider.
|
||||
*/
|
||||
public static Uri getUriForFile(Context context, String authority, File file) {
|
||||
final PathStrategy strategy = getPathStrategy(context, authority);
|
||||
return strategy.getUriForFile(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a content URI returned by
|
||||
* {@link #getUriForFile(Context, String, File) getUriForFile()} to get information about a file
|
||||
* managed by the FileProvider.
|
||||
* FileProvider reports the column names defined in {@link android.provider.MediaStore.MediaColumns}:
|
||||
* <ul>
|
||||
* <li>{@link android.provider.MediaStore.MediaColumns#DISPLAY_NAME}</li>
|
||||
* <li>{@link android.provider.MediaStore.MediaColumns#SIZE}</li>
|
||||
* <li>{@link android.provider.MediaStore.MediaColumns#DATA}</li>
|
||||
* </ul>
|
||||
* For more information, see
|
||||
* {@link ContentProvider#query(Uri, String[], String, String[], String)
|
||||
* ContentProvider.query()}.
|
||||
*
|
||||
* @param uri A content URI returned by {@link #getUriForFile}.
|
||||
* @param projection The list of columns to put into the {@link Cursor}. If null all columns are
|
||||
* included.
|
||||
* @param selection Selection criteria to apply. If null then all data that matches the content
|
||||
* URI is returned.
|
||||
* @param selectionArgs An array of {@link java.lang.String}, containing arguments to bind to
|
||||
* the <i>selection</i> parameter. The <i>query</i> method scans <i>selection</i> from left to
|
||||
* right and iterates through <i>selectionArgs</i>, replacing the current "?" character in
|
||||
* <i>selection</i> with the value at the current position in <i>selectionArgs</i>. The
|
||||
* values are bound to <i>selection</i> as {@link java.lang.String} values.
|
||||
* @param sortOrder A {@link java.lang.String} containing the column name(s) on which to sort
|
||||
* the resulting {@link Cursor}.
|
||||
* @return A {@link Cursor} containing the results of the query.
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
|
||||
String sortOrder) {
|
||||
// ContentProvider has already checked granted permissions
|
||||
final File file = mStrategy.getFileForUri(uri);
|
||||
|
||||
if (projection == null) {
|
||||
projection = COLUMNS;
|
||||
}
|
||||
|
||||
String[] cols = new String[projection.length];
|
||||
Object[] values = new Object[projection.length];
|
||||
int i = 0;
|
||||
for (String col : projection) {
|
||||
if (MediaStore.MediaColumns.DISPLAY_NAME.equals(col)) {
|
||||
cols[i] = MediaStore.MediaColumns.DISPLAY_NAME;
|
||||
values[i++] = file.getName();
|
||||
} else if (MediaStore.MediaColumns.SIZE.equals(col)) {
|
||||
cols[i] = MediaStore.MediaColumns.SIZE;
|
||||
values[i++] = file.length();
|
||||
} else if (MediaStore.MediaColumns.DATA.equals(col)) {
|
||||
cols[i] = MediaStore.MediaColumns.DATA;
|
||||
values[i++] = file.getPath();
|
||||
}
|
||||
}
|
||||
|
||||
cols = copyOf(cols, i);
|
||||
values = copyOf(values, i);
|
||||
|
||||
final MatrixCursor cursor = new MatrixCursor(cols, 1);
|
||||
cursor.addRow(values);
|
||||
return cursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the MIME type of a content URI returned by
|
||||
* {@link #getUriForFile(Context, String, File) getUriForFile()}.
|
||||
*
|
||||
* @param uri A content URI returned by
|
||||
* {@link #getUriForFile(Context, String, File) getUriForFile()}.
|
||||
* @return If the associated file has an extension, the MIME type associated with that
|
||||
* extension; otherwise <code>application/octet-stream</code>.
|
||||
*/
|
||||
@Override
|
||||
public String getType(Uri uri) {
|
||||
// ContentProvider has already checked granted permissions
|
||||
final File file = mStrategy.getFileForUri(uri);
|
||||
|
||||
final int lastDot = file.getName().lastIndexOf('.');
|
||||
if (lastDot >= 0) {
|
||||
final String extension = file.getName().substring(lastDot + 1);
|
||||
final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
||||
if (mime != null) {
|
||||
return mime;
|
||||
}
|
||||
}
|
||||
|
||||
return "application/octet-stream";
|
||||
}
|
||||
|
||||
/**
|
||||
* By default, this method throws an {@link java.lang.UnsupportedOperationException}. You must
|
||||
* subclass FileProvider if you want to provide different functionality.
|
||||
*/
|
||||
@Override
|
||||
public Uri insert(Uri uri, ContentValues values) {
|
||||
throw new UnsupportedOperationException("No external inserts");
|
||||
}
|
||||
|
||||
/**
|
||||
* By default, this method throws an {@link java.lang.UnsupportedOperationException}. You must
|
||||
* subclass FileProvider if you want to provide different functionality.
|
||||
*/
|
||||
@Override
|
||||
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||
throw new UnsupportedOperationException("No external updates");
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the file associated with the specified content URI, as
|
||||
* returned by {@link #getUriForFile(Context, String, File) getUriForFile()}. Notice that this
|
||||
* method does <b>not</b> throw an {@link java.io.IOException}; you must check its return value.
|
||||
*
|
||||
* @param uri A content URI for a file, as returned by
|
||||
* {@link #getUriForFile(Context, String, File) getUriForFile()}.
|
||||
* @param selection Ignored. Set to {@code null}.
|
||||
* @param selectionArgs Ignored. Set to {@code null}.
|
||||
* @return 1 if the delete succeeds; otherwise, 0.
|
||||
*/
|
||||
@Override
|
||||
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||
// ContentProvider has already checked granted permissions
|
||||
final File file = mStrategy.getFileForUri(uri);
|
||||
return file.delete() ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* By default, FileProvider automatically returns the
|
||||
* {@link ParcelFileDescriptor} for a file associated with a <code>content://</code>
|
||||
* {@link Uri}. To get the {@link ParcelFileDescriptor}, call
|
||||
* {@link android.content.ContentResolver#openFileDescriptor(Uri, String)
|
||||
* ContentResolver.openFileDescriptor}.
|
||||
*
|
||||
* To override this method, you must provide your own subclass of FileProvider.
|
||||
*
|
||||
* @param uri A content URI associated with a file, as returned by
|
||||
* {@link #getUriForFile(Context, String, File) getUriForFile()}.
|
||||
* @param mode Access mode for the file. May be "r" for read-only access, "rw" for read and
|
||||
* write access, or "rwt" for read and write access that truncates any existing file.
|
||||
* @return A new {@link ParcelFileDescriptor} with which you can access the file.
|
||||
*/
|
||||
@Override
|
||||
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
|
||||
// ContentProvider has already checked granted permissions
|
||||
final File file = mStrategy.getFileForUri(uri);
|
||||
final int fileMode = modeToMode(mode);
|
||||
return ParcelFileDescriptor.open(file, fileMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@link PathStrategy} for given authority, either by parsing or
|
||||
* returning from cache.
|
||||
@@ -633,6 +457,234 @@ public class FileProvider extends ContentProvider {
|
||||
return strat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copied from ContentResolver.java
|
||||
*/
|
||||
private static int modeToMode(String mode) {
|
||||
int modeBits;
|
||||
if ("r".equals(mode)) {
|
||||
modeBits = ParcelFileDescriptor.MODE_READ_ONLY;
|
||||
} else if ("w".equals(mode) || "wt".equals(mode)) {
|
||||
modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
|
||||
| ParcelFileDescriptor.MODE_CREATE
|
||||
| ParcelFileDescriptor.MODE_TRUNCATE;
|
||||
} else if ("wa".equals(mode)) {
|
||||
modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
|
||||
| ParcelFileDescriptor.MODE_CREATE
|
||||
| ParcelFileDescriptor.MODE_APPEND;
|
||||
} else if ("rw".equals(mode)) {
|
||||
modeBits = ParcelFileDescriptor.MODE_READ_WRITE
|
||||
| ParcelFileDescriptor.MODE_CREATE;
|
||||
} else if ("rwt".equals(mode)) {
|
||||
modeBits = ParcelFileDescriptor.MODE_READ_WRITE
|
||||
| ParcelFileDescriptor.MODE_CREATE
|
||||
| ParcelFileDescriptor.MODE_TRUNCATE;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid mode: " + mode);
|
||||
}
|
||||
return modeBits;
|
||||
}
|
||||
|
||||
private static File buildPath(File base, String... segments) {
|
||||
File cur = base;
|
||||
for (String segment : segments) {
|
||||
if (segment != null) {
|
||||
cur = new File(cur, segment);
|
||||
}
|
||||
}
|
||||
return cur;
|
||||
}
|
||||
|
||||
private static String[] copyOf(String[] original, int newLength) {
|
||||
final String[] result = new String[newLength];
|
||||
System.arraycopy(original, 0, result, 0, newLength);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Object[] copyOf(Object[] original, int newLength) {
|
||||
final Object[] result = new Object[newLength];
|
||||
System.arraycopy(original, 0, result, 0, newLength);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* The default FileProvider implementation does not need to be initialized. If you want to
|
||||
* override this method, you must provide your own subclass of FileProvider.
|
||||
*/
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* After the FileProvider is instantiated, this method is called to provide the system with
|
||||
* information about the provider.
|
||||
*
|
||||
* @param context A {@link Context} for the current component.
|
||||
* @param info A {@link ProviderInfo} for the new provider.
|
||||
*/
|
||||
@Override
|
||||
public void attachInfo(Context context, ProviderInfo info) {
|
||||
super.attachInfo(context, info);
|
||||
|
||||
// Sanity check our security
|
||||
if (info.exported) {
|
||||
throw new SecurityException("Provider must not be exported");
|
||||
}
|
||||
if (!info.grantUriPermissions) {
|
||||
throw new SecurityException("Provider must grant uri permissions");
|
||||
}
|
||||
|
||||
mStrategy = getPathStrategy(context, info.authority);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a content URI returned by
|
||||
* {@link #getUriForFile(Context, String, File) getUriForFile()} to get information about a file
|
||||
* managed by the FileProvider.
|
||||
* FileProvider reports the column names defined in {@link android.provider.MediaStore.MediaColumns}:
|
||||
* <ul>
|
||||
* <li>{@link android.provider.MediaStore.MediaColumns#DISPLAY_NAME}</li>
|
||||
* <li>{@link android.provider.MediaStore.MediaColumns#SIZE}</li>
|
||||
* <li>{@link android.provider.MediaStore.MediaColumns#DATA}</li>
|
||||
* </ul>
|
||||
* For more information, see
|
||||
* {@link ContentProvider#query(Uri, String[], String, String[], String)
|
||||
* ContentProvider.query()}.
|
||||
*
|
||||
* @param uri A content URI returned by {@link #getUriForFile}.
|
||||
* @param projection The list of columns to put into the {@link Cursor}. If null all columns are
|
||||
* included.
|
||||
* @param selection Selection criteria to apply. If null then all data that matches the content
|
||||
* URI is returned.
|
||||
* @param selectionArgs An array of {@link java.lang.String}, containing arguments to bind to
|
||||
* the <i>selection</i> parameter. The <i>query</i> method scans <i>selection</i> from left to
|
||||
* right and iterates through <i>selectionArgs</i>, replacing the current "?" character in
|
||||
* <i>selection</i> with the value at the current position in <i>selectionArgs</i>. The
|
||||
* values are bound to <i>selection</i> as {@link java.lang.String} values.
|
||||
* @param sortOrder A {@link java.lang.String} containing the column name(s) on which to sort
|
||||
* the resulting {@link Cursor}.
|
||||
* @return A {@link Cursor} containing the results of the query.
|
||||
*/
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
|
||||
String sortOrder) {
|
||||
// ContentProvider has already checked granted permissions
|
||||
final File file = mStrategy.getFileForUri(uri);
|
||||
|
||||
if (projection == null) {
|
||||
projection = COLUMNS;
|
||||
}
|
||||
|
||||
String[] cols = new String[projection.length];
|
||||
Object[] values = new Object[projection.length];
|
||||
int i = 0;
|
||||
for (String col : projection) {
|
||||
if (MediaStore.MediaColumns.DISPLAY_NAME.equals(col)) {
|
||||
cols[i] = MediaStore.MediaColumns.DISPLAY_NAME;
|
||||
values[i++] = file.getName();
|
||||
} else if (MediaStore.MediaColumns.SIZE.equals(col)) {
|
||||
cols[i] = MediaStore.MediaColumns.SIZE;
|
||||
values[i++] = file.length();
|
||||
} else if (MediaStore.MediaColumns.DATA.equals(col)) {
|
||||
cols[i] = MediaStore.MediaColumns.DATA;
|
||||
values[i++] = file.getPath();
|
||||
}
|
||||
}
|
||||
|
||||
cols = copyOf(cols, i);
|
||||
values = copyOf(values, i);
|
||||
|
||||
final MatrixCursor cursor = new MatrixCursor(cols, 1);
|
||||
cursor.addRow(values);
|
||||
return cursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the MIME type of a content URI returned by
|
||||
* {@link #getUriForFile(Context, String, File) getUriForFile()}.
|
||||
*
|
||||
* @param uri A content URI returned by
|
||||
* {@link #getUriForFile(Context, String, File) getUriForFile()}.
|
||||
* @return If the associated file has an extension, the MIME type associated with that
|
||||
* extension; otherwise <code>application/octet-stream</code>.
|
||||
*/
|
||||
@Override
|
||||
public String getType(Uri uri) {
|
||||
// ContentProvider has already checked granted permissions
|
||||
final File file = mStrategy.getFileForUri(uri);
|
||||
|
||||
final int lastDot = file.getName().lastIndexOf('.');
|
||||
if (lastDot >= 0) {
|
||||
final String extension = file.getName().substring(lastDot + 1);
|
||||
final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
||||
if (mime != null) {
|
||||
return mime;
|
||||
}
|
||||
}
|
||||
|
||||
return "application/octet-stream";
|
||||
}
|
||||
|
||||
/**
|
||||
* By default, this method throws an {@link java.lang.UnsupportedOperationException}. You must
|
||||
* subclass FileProvider if you want to provide different functionality.
|
||||
*/
|
||||
@Override
|
||||
public Uri insert(Uri uri, ContentValues values) {
|
||||
throw new UnsupportedOperationException("No external inserts");
|
||||
}
|
||||
|
||||
/**
|
||||
* By default, this method throws an {@link java.lang.UnsupportedOperationException}. You must
|
||||
* subclass FileProvider if you want to provide different functionality.
|
||||
*/
|
||||
@Override
|
||||
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||
throw new UnsupportedOperationException("No external updates");
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the file associated with the specified content URI, as
|
||||
* returned by {@link #getUriForFile(Context, String, File) getUriForFile()}. Notice that this
|
||||
* method does <b>not</b> throw an {@link java.io.IOException}; you must check its return value.
|
||||
*
|
||||
* @param uri A content URI for a file, as returned by
|
||||
* {@link #getUriForFile(Context, String, File) getUriForFile()}.
|
||||
* @param selection Ignored. Set to {@code null}.
|
||||
* @param selectionArgs Ignored. Set to {@code null}.
|
||||
* @return 1 if the delete succeeds; otherwise, 0.
|
||||
*/
|
||||
@Override
|
||||
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||
// ContentProvider has already checked granted permissions
|
||||
final File file = mStrategy.getFileForUri(uri);
|
||||
return file.delete() ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* By default, FileProvider automatically returns the
|
||||
* {@link ParcelFileDescriptor} for a file associated with a <code>content://</code>
|
||||
* {@link Uri}. To get the {@link ParcelFileDescriptor}, call
|
||||
* {@link android.content.ContentResolver#openFileDescriptor(Uri, String)
|
||||
* ContentResolver.openFileDescriptor}.
|
||||
* <p>
|
||||
* To override this method, you must provide your own subclass of FileProvider.
|
||||
*
|
||||
* @param uri A content URI associated with a file, as returned by
|
||||
* {@link #getUriForFile(Context, String, File) getUriForFile()}.
|
||||
* @param mode Access mode for the file. May be "r" for read-only access, "rw" for read and
|
||||
* write access, or "rwt" for read and write access that truncates any existing file.
|
||||
* @return A new {@link ParcelFileDescriptor} with which you can access the file.
|
||||
*/
|
||||
@Override
|
||||
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
|
||||
// ContentProvider has already checked granted permissions
|
||||
final File file = mStrategy.getFileForUri(uri);
|
||||
final int fileMode = modeToMode(mode);
|
||||
return ParcelFileDescriptor.open(file, fileMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy for mapping between {@link File} and {@link Uri}.
|
||||
* <p>
|
||||
@@ -650,12 +702,12 @@ public class FileProvider extends ContentProvider {
|
||||
/**
|
||||
* Return a {@link Uri} that represents the given {@link File}.
|
||||
*/
|
||||
public Uri getUriForFile(File file);
|
||||
Uri getUriForFile(File file);
|
||||
|
||||
/**
|
||||
* Return a {@link File} that represents the given {@link Uri}.
|
||||
*/
|
||||
public File getFileForUri(Uri uri);
|
||||
File getFileForUri(Uri uri);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -761,54 +813,4 @@ public class FileProvider extends ContentProvider {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copied from ContentResolver.java
|
||||
*/
|
||||
private static int modeToMode(String mode) {
|
||||
int modeBits;
|
||||
if ("r".equals(mode)) {
|
||||
modeBits = ParcelFileDescriptor.MODE_READ_ONLY;
|
||||
} else if ("w".equals(mode) || "wt".equals(mode)) {
|
||||
modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
|
||||
| ParcelFileDescriptor.MODE_CREATE
|
||||
| ParcelFileDescriptor.MODE_TRUNCATE;
|
||||
} else if ("wa".equals(mode)) {
|
||||
modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY
|
||||
| ParcelFileDescriptor.MODE_CREATE
|
||||
| ParcelFileDescriptor.MODE_APPEND;
|
||||
} else if ("rw".equals(mode)) {
|
||||
modeBits = ParcelFileDescriptor.MODE_READ_WRITE
|
||||
| ParcelFileDescriptor.MODE_CREATE;
|
||||
} else if ("rwt".equals(mode)) {
|
||||
modeBits = ParcelFileDescriptor.MODE_READ_WRITE
|
||||
| ParcelFileDescriptor.MODE_CREATE
|
||||
| ParcelFileDescriptor.MODE_TRUNCATE;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid mode: " + mode);
|
||||
}
|
||||
return modeBits;
|
||||
}
|
||||
|
||||
private static File buildPath(File base, String... segments) {
|
||||
File cur = base;
|
||||
for (String segment : segments) {
|
||||
if (segment != null) {
|
||||
cur = new File(cur, segment);
|
||||
}
|
||||
}
|
||||
return cur;
|
||||
}
|
||||
|
||||
private static String[] copyOf(String[] original, int newLength) {
|
||||
final String[] result = new String[newLength];
|
||||
System.arraycopy(original, 0, result, 0, newLength);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Object[] copyOf(Object[] original, int newLength) {
|
||||
final Object[] result = new Object[newLength];
|
||||
System.arraycopy(original, 0, result, 0, newLength);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,9 @@ package com.hippo.content;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.hippo.scene.SceneApplication;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
@@ -31,60 +33,65 @@ import java.util.List;
|
||||
|
||||
public abstract class RecordingApplication extends SceneApplication {
|
||||
|
||||
private List<WeakReference<Activity>> list = new LinkedList<>();
|
||||
private List<WeakReference<Activity>> list = new LinkedList<>();
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
|
||||
@Override
|
||||
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
|
||||
list.add(new java.lang.ref.WeakReference<>(activity));
|
||||
}
|
||||
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
|
||||
@Override
|
||||
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
|
||||
list.add(new java.lang.ref.WeakReference<>(activity));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed(Activity activity) {
|
||||
Iterator<WeakReference<Activity>> iterator = list.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
WeakReference<Activity> reference = iterator.next();
|
||||
Activity a = reference.get();
|
||||
// Remove current activity and null
|
||||
if (a == null || a == activity) {
|
||||
iterator.remove();
|
||||
}
|
||||
@Override
|
||||
public void onActivityDestroyed(Activity activity) {
|
||||
Iterator<WeakReference<Activity>> iterator = list.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
WeakReference<Activity> reference = iterator.next();
|
||||
Activity a = reference.get();
|
||||
// Remove current activity and null
|
||||
if (a == null || a == activity) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStarted(Activity activity) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResumed(Activity activity) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityPaused(Activity activity) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStopped(Activity activity) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void recreate() {
|
||||
// Copy list
|
||||
ArrayList<Activity> listCopy = new ArrayList<>(list.size());
|
||||
for (WeakReference<Activity> reference : list) {
|
||||
Activity activity = reference.get();
|
||||
if (activity == null) continue;
|
||||
listCopy.add(activity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStarted(Activity activity) {}
|
||||
|
||||
@Override
|
||||
public void onActivityResumed(Activity activity) {}
|
||||
|
||||
@Override
|
||||
public void onActivityPaused(Activity activity) {}
|
||||
|
||||
@Override
|
||||
public void onActivityStopped(Activity activity) {}
|
||||
|
||||
@Override
|
||||
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
|
||||
});
|
||||
}
|
||||
|
||||
public void recreate() {
|
||||
// Copy list
|
||||
ArrayList<Activity> listCopy = new ArrayList<>(list.size());
|
||||
for (WeakReference<Activity> reference: list) {
|
||||
Activity activity = reference.get();
|
||||
if (activity == null) continue;
|
||||
listCopy.add(activity);
|
||||
// Finish all activities
|
||||
for (int i = listCopy.size() - 1; i >= 0; --i) {
|
||||
listCopy.get(i).recreate();
|
||||
}
|
||||
}
|
||||
|
||||
// Finish all activities
|
||||
for (int i = listCopy.size() - 1; i >= 0; --i) {
|
||||
listCopy.get(i).recreate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ package com.hippo.database;
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -30,101 +31,101 @@ import java.util.Map;
|
||||
|
||||
public class MSQLiteBuilder {
|
||||
|
||||
public static final String COLUMN_ID = "_id";
|
||||
public static final String COLUMN_ID = "_id";
|
||||
|
||||
private static final Map<Class, String> JAVA_TYPE_TO_SQLITE_TYPE = new HashMap<>();
|
||||
private static final Map<Class, String> JAVA_TYPE_TO_SQLITE_TYPE = new HashMap<>();
|
||||
|
||||
static {
|
||||
JAVA_TYPE_TO_SQLITE_TYPE.put(boolean.class, "INTEGER NOT NULL DEFAULT 0");
|
||||
JAVA_TYPE_TO_SQLITE_TYPE.put(byte.class, "INTEGER NOT NULL DEFAULT 0");
|
||||
JAVA_TYPE_TO_SQLITE_TYPE.put(short.class, "INTEGER NOT NULL DEFAULT 0");
|
||||
JAVA_TYPE_TO_SQLITE_TYPE.put(int.class, "INTEGER NOT NULL DEFAULT 0");
|
||||
JAVA_TYPE_TO_SQLITE_TYPE.put(long.class, "INTEGER NOT NULL DEFAULT 0");
|
||||
JAVA_TYPE_TO_SQLITE_TYPE.put(float.class, "REAL NOT NULL DEFAULT 0");
|
||||
JAVA_TYPE_TO_SQLITE_TYPE.put(double.class, "REAL NOT NULL DEFAULT 0");
|
||||
JAVA_TYPE_TO_SQLITE_TYPE.put(String.class, "TEXT");
|
||||
}
|
||||
|
||||
private static String javaTypeToSQLiteType(Class clazz) {
|
||||
String type = JAVA_TYPE_TO_SQLITE_TYPE.get(clazz);
|
||||
if (type == null) {
|
||||
throw new IllegalStateException("Unknown type: " + clazz);
|
||||
static {
|
||||
JAVA_TYPE_TO_SQLITE_TYPE.put(boolean.class, "INTEGER NOT NULL DEFAULT 0");
|
||||
JAVA_TYPE_TO_SQLITE_TYPE.put(byte.class, "INTEGER NOT NULL DEFAULT 0");
|
||||
JAVA_TYPE_TO_SQLITE_TYPE.put(short.class, "INTEGER NOT NULL DEFAULT 0");
|
||||
JAVA_TYPE_TO_SQLITE_TYPE.put(int.class, "INTEGER NOT NULL DEFAULT 0");
|
||||
JAVA_TYPE_TO_SQLITE_TYPE.put(long.class, "INTEGER NOT NULL DEFAULT 0");
|
||||
JAVA_TYPE_TO_SQLITE_TYPE.put(float.class, "REAL NOT NULL DEFAULT 0");
|
||||
JAVA_TYPE_TO_SQLITE_TYPE.put(double.class, "REAL NOT NULL DEFAULT 0");
|
||||
JAVA_TYPE_TO_SQLITE_TYPE.put(String.class, "TEXT");
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
private int version = 0;
|
||||
private List<String> statements;
|
||||
private SparseArray<List<String>> statementsMap = new SparseArray<>();
|
||||
private int version = 0;
|
||||
private List<String> statements;
|
||||
private SparseArray<List<String>> statementsMap = new SparseArray<>();
|
||||
|
||||
/**
|
||||
* Bump database version.
|
||||
*/
|
||||
public MSQLiteBuilder version(int version) {
|
||||
if (version <= this.version) {
|
||||
throw new IllegalStateException("New version must be bigger than current version. "
|
||||
+ "current version: " + this.version + ", new version: " + version + ".");
|
||||
private static String javaTypeToSQLiteType(Class clazz) {
|
||||
String type = JAVA_TYPE_TO_SQLITE_TYPE.get(clazz);
|
||||
if (type == null) {
|
||||
throw new IllegalStateException("Unknown type: " + clazz);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
this.version = version;
|
||||
this.statements = new ArrayList<>();
|
||||
this.statementsMap.put(version, this.statements);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a table with int {@link #COLUMN_ID} primary key.
|
||||
*/
|
||||
public MSQLiteBuilder createTable(String table) {
|
||||
return createTable(table, COLUMN_ID, int.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a table.
|
||||
*/
|
||||
public MSQLiteBuilder createTable(String table, String column, Class clazz) {
|
||||
return statement("CREATE TABLE " + table + " (" + column + " " + javaTypeToSQLiteType(clazz) + " PRIMARY KEY);");
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops a table.
|
||||
*/
|
||||
public MSQLiteBuilder dropTable(String table) {
|
||||
return statement("DROP TABLE " + table + ";");
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a column to the table.
|
||||
*/
|
||||
public MSQLiteBuilder insertColumn(String table, String column, Class clazz) {
|
||||
return statement("ALTER TABLE " + table + " ADD COLUMN " + column + " " + javaTypeToSQLiteType(clazz) + ";");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a statement.
|
||||
*/
|
||||
public MSQLiteBuilder statement(String statement) {
|
||||
if (version == 0 || statements == null) {
|
||||
throw new IllegalStateException("Call version() first!");
|
||||
/**
|
||||
* Bump database version.
|
||||
*/
|
||||
public MSQLiteBuilder version(int version) {
|
||||
if (version <= this.version) {
|
||||
throw new IllegalStateException("New version must be bigger than current version. "
|
||||
+ "current version: " + this.version + ", new version: " + version + ".");
|
||||
}
|
||||
this.version = version;
|
||||
this.statements = new ArrayList<>();
|
||||
this.statementsMap.put(version, this.statements);
|
||||
return this;
|
||||
}
|
||||
statements.add(statement);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a SQLiteOpenHelper from it.
|
||||
*/
|
||||
public SQLiteOpenHelper build(Context context, String name, int version) {
|
||||
return new MSQLiteOpenHelper(context, name, version, this);
|
||||
}
|
||||
|
||||
List<String> getStatements(int oldVersion, int newVersion) {
|
||||
List<String> result = new ArrayList<>();
|
||||
for (int i = oldVersion + 1; i <= newVersion; ++i) {
|
||||
List<String> list = statementsMap.get(i);
|
||||
if (list != null) {
|
||||
result.addAll(list);
|
||||
}
|
||||
/**
|
||||
* Creates a table with int {@link #COLUMN_ID} primary key.
|
||||
*/
|
||||
public MSQLiteBuilder createTable(String table) {
|
||||
return createTable(table, COLUMN_ID, int.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a table.
|
||||
*/
|
||||
public MSQLiteBuilder createTable(String table, String column, Class clazz) {
|
||||
return statement("CREATE TABLE " + table + " (" + column + " " + javaTypeToSQLiteType(clazz) + " PRIMARY KEY);");
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops a table.
|
||||
*/
|
||||
public MSQLiteBuilder dropTable(String table) {
|
||||
return statement("DROP TABLE " + table + ";");
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a column to the table.
|
||||
*/
|
||||
public MSQLiteBuilder insertColumn(String table, String column, Class clazz) {
|
||||
return statement("ALTER TABLE " + table + " ADD COLUMN " + column + " " + javaTypeToSQLiteType(clazz) + ";");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a statement.
|
||||
*/
|
||||
public MSQLiteBuilder statement(String statement) {
|
||||
if (version == 0 || statements == null) {
|
||||
throw new IllegalStateException("Call version() first!");
|
||||
}
|
||||
statements.add(statement);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a SQLiteOpenHelper from it.
|
||||
*/
|
||||
public SQLiteOpenHelper build(Context context, String name, int version) {
|
||||
return new MSQLiteOpenHelper(context, name, version, this);
|
||||
}
|
||||
|
||||
List<String> getStatements(int oldVersion, int newVersion) {
|
||||
List<String> result = new ArrayList<>();
|
||||
for (int i = oldVersion + 1; i <= newVersion; ++i) {
|
||||
List<String> list = statementsMap.get(i);
|
||||
if (list != null) {
|
||||
result.addAll(list);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -23,34 +23,35 @@ package com.hippo.database;
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
|
||||
import com.hippo.util.SqlUtils;
|
||||
|
||||
class MSQLiteOpenHelper extends SQLiteOpenHelper {
|
||||
|
||||
private final int version;
|
||||
private final MSQLiteBuilder builder;
|
||||
private final int version;
|
||||
private final MSQLiteBuilder builder;
|
||||
|
||||
public MSQLiteOpenHelper(Context context, String name, int version, MSQLiteBuilder builder) {
|
||||
super(context, name, null, version);
|
||||
this.version = version;
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
onUpgrade(db, 0, version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
for (String command : builder.getStatements(oldVersion, newVersion)) {
|
||||
db.execSQL(command);
|
||||
public MSQLiteOpenHelper(Context context, String name, int version, MSQLiteBuilder builder) {
|
||||
super(context, name, null, version);
|
||||
this.version = version;
|
||||
this.builder = builder;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
SqlUtils.dropAllTable(db);
|
||||
onCreate(db);
|
||||
}
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
onUpgrade(db, 0, version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
for (String command : builder.getStatements(oldVersion, newVersion)) {
|
||||
db.execSQL(command);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
SqlUtils.dropAllTable(db);
|
||||
onCreate(db);
|
||||
}
|
||||
}
|
||||
@@ -28,21 +28,18 @@ import com.hippo.yorozuya.MathUtils;
|
||||
|
||||
public class BatteryDrawable extends Drawable {
|
||||
|
||||
public static final int WARN_LIMIT = 15;
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = BatteryDrawable.class.getSimpleName();
|
||||
|
||||
public static final int WARN_LIMIT = 15;
|
||||
|
||||
private int mColor = Color.WHITE;
|
||||
private int mWarningColor = Color.RED;
|
||||
private int mElect = -1;
|
||||
private final Paint mPaint;
|
||||
|
||||
private final Rect mTopRect;
|
||||
private final Rect mBottomRect;
|
||||
private final Rect mRightRect;
|
||||
private final Rect mHeadRect;
|
||||
private final Rect mElectRect;
|
||||
private int mColor = Color.WHITE;
|
||||
private int mWarningColor = Color.RED;
|
||||
private int mElect = -1;
|
||||
private int mStart;
|
||||
private int mStop;
|
||||
|
||||
|
||||
@@ -18,7 +18,9 @@ package com.hippo.drawable;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
|
||||
@@ -23,158 +23,159 @@ import android.graphics.PorterDuff;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Region;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.graphics.drawable.DrawableCompat;
|
||||
|
||||
public class DrawableWrapper extends Drawable implements Drawable.Callback {
|
||||
private Drawable mDrawable;
|
||||
private Drawable mDrawable;
|
||||
|
||||
public DrawableWrapper(Drawable drawable) {
|
||||
this.setWrappedDrawable(drawable);
|
||||
}
|
||||
|
||||
public void draw(@NonNull Canvas canvas) {
|
||||
this.mDrawable.draw(canvas);
|
||||
}
|
||||
|
||||
protected void onBoundsChange(Rect bounds) {
|
||||
this.mDrawable.setBounds(bounds);
|
||||
}
|
||||
|
||||
public void setChangingConfigurations(int configs) {
|
||||
this.mDrawable.setChangingConfigurations(configs);
|
||||
}
|
||||
|
||||
public int getChangingConfigurations() {
|
||||
return this.mDrawable.getChangingConfigurations();
|
||||
}
|
||||
|
||||
public void setDither(boolean dither) {
|
||||
this.mDrawable.setDither(dither);
|
||||
}
|
||||
|
||||
public void setFilterBitmap(boolean filter) {
|
||||
this.mDrawable.setFilterBitmap(filter);
|
||||
}
|
||||
|
||||
public void setAlpha(int alpha) {
|
||||
this.mDrawable.setAlpha(alpha);
|
||||
}
|
||||
|
||||
public void setColorFilter(ColorFilter cf) {
|
||||
this.mDrawable.setColorFilter(cf);
|
||||
}
|
||||
|
||||
public boolean isStateful() {
|
||||
return this.mDrawable.isStateful();
|
||||
}
|
||||
|
||||
public boolean setState(@NonNull int[] stateSet) {
|
||||
return this.mDrawable.setState(stateSet);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public int[] getState() {
|
||||
return this.mDrawable.getState();
|
||||
}
|
||||
|
||||
public void jumpToCurrentState() {
|
||||
this.mDrawable.jumpToCurrentState();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Drawable getCurrent() {
|
||||
return this.mDrawable.getCurrent();
|
||||
}
|
||||
|
||||
public boolean setVisible(boolean visible, boolean restart) {
|
||||
return super.setVisible(visible, restart) || this.mDrawable.setVisible(visible, restart);
|
||||
}
|
||||
|
||||
public int getOpacity() {
|
||||
return this.mDrawable.getOpacity();
|
||||
}
|
||||
|
||||
public Region getTransparentRegion() {
|
||||
return this.mDrawable.getTransparentRegion();
|
||||
}
|
||||
|
||||
public int getIntrinsicWidth() {
|
||||
return this.mDrawable.getIntrinsicWidth();
|
||||
}
|
||||
|
||||
public int getIntrinsicHeight() {
|
||||
return this.mDrawable.getIntrinsicHeight();
|
||||
}
|
||||
|
||||
public int getMinimumWidth() {
|
||||
return this.mDrawable.getMinimumWidth();
|
||||
}
|
||||
|
||||
public int getMinimumHeight() {
|
||||
return this.mDrawable.getMinimumHeight();
|
||||
}
|
||||
|
||||
public boolean getPadding(@NonNull Rect padding) {
|
||||
return this.mDrawable.getPadding(padding);
|
||||
}
|
||||
|
||||
public void invalidateDrawable(@NonNull Drawable who) {
|
||||
this.invalidateSelf();
|
||||
}
|
||||
|
||||
public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
|
||||
this.scheduleSelf(what, when);
|
||||
}
|
||||
|
||||
public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
|
||||
this.unscheduleSelf(what);
|
||||
}
|
||||
|
||||
protected boolean onLevelChange(int level) {
|
||||
return this.mDrawable.setLevel(level);
|
||||
}
|
||||
|
||||
public void setAutoMirrored(boolean mirrored) {
|
||||
DrawableCompat.setAutoMirrored(this.mDrawable, mirrored);
|
||||
}
|
||||
|
||||
public boolean isAutoMirrored() {
|
||||
return DrawableCompat.isAutoMirrored(this.mDrawable);
|
||||
}
|
||||
|
||||
public void setTint(int tint) {
|
||||
DrawableCompat.setTint(this.mDrawable, tint);
|
||||
}
|
||||
|
||||
public void setTintList(ColorStateList tint) {
|
||||
DrawableCompat.setTintList(this.mDrawable, tint);
|
||||
}
|
||||
|
||||
public void setTintMode(@NonNull PorterDuff.Mode tintMode) {
|
||||
DrawableCompat.setTintMode(this.mDrawable, tintMode);
|
||||
}
|
||||
|
||||
public void setHotspot(float x, float y) {
|
||||
DrawableCompat.setHotspot(this.mDrawable, x, y);
|
||||
}
|
||||
|
||||
public void setHotspotBounds(int left, int top, int right, int bottom) {
|
||||
DrawableCompat.setHotspotBounds(this.mDrawable, left, top, right, bottom);
|
||||
}
|
||||
|
||||
public Drawable getWrappedDrawable() {
|
||||
return this.mDrawable;
|
||||
}
|
||||
|
||||
public void setWrappedDrawable(Drawable drawable) {
|
||||
if (this.mDrawable != null) {
|
||||
this.mDrawable.setCallback(null);
|
||||
public DrawableWrapper(Drawable drawable) {
|
||||
this.setWrappedDrawable(drawable);
|
||||
}
|
||||
|
||||
this.mDrawable = drawable;
|
||||
if (drawable != null) {
|
||||
drawable.setCallback(this);
|
||||
public void draw(@NonNull Canvas canvas) {
|
||||
this.mDrawable.draw(canvas);
|
||||
}
|
||||
|
||||
protected void onBoundsChange(Rect bounds) {
|
||||
this.mDrawable.setBounds(bounds);
|
||||
}
|
||||
|
||||
public int getChangingConfigurations() {
|
||||
return this.mDrawable.getChangingConfigurations();
|
||||
}
|
||||
|
||||
public void setChangingConfigurations(int configs) {
|
||||
this.mDrawable.setChangingConfigurations(configs);
|
||||
}
|
||||
|
||||
public void setDither(boolean dither) {
|
||||
this.mDrawable.setDither(dither);
|
||||
}
|
||||
|
||||
public void setFilterBitmap(boolean filter) {
|
||||
this.mDrawable.setFilterBitmap(filter);
|
||||
}
|
||||
|
||||
public void setAlpha(int alpha) {
|
||||
this.mDrawable.setAlpha(alpha);
|
||||
}
|
||||
|
||||
public void setColorFilter(ColorFilter cf) {
|
||||
this.mDrawable.setColorFilter(cf);
|
||||
}
|
||||
|
||||
public boolean isStateful() {
|
||||
return this.mDrawable.isStateful();
|
||||
}
|
||||
|
||||
public boolean setState(@NonNull int[] stateSet) {
|
||||
return this.mDrawable.setState(stateSet);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public int[] getState() {
|
||||
return this.mDrawable.getState();
|
||||
}
|
||||
|
||||
public void jumpToCurrentState() {
|
||||
this.mDrawable.jumpToCurrentState();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Drawable getCurrent() {
|
||||
return this.mDrawable.getCurrent();
|
||||
}
|
||||
|
||||
public boolean setVisible(boolean visible, boolean restart) {
|
||||
return super.setVisible(visible, restart) || this.mDrawable.setVisible(visible, restart);
|
||||
}
|
||||
|
||||
public int getOpacity() {
|
||||
return this.mDrawable.getOpacity();
|
||||
}
|
||||
|
||||
public Region getTransparentRegion() {
|
||||
return this.mDrawable.getTransparentRegion();
|
||||
}
|
||||
|
||||
public int getIntrinsicWidth() {
|
||||
return this.mDrawable.getIntrinsicWidth();
|
||||
}
|
||||
|
||||
public int getIntrinsicHeight() {
|
||||
return this.mDrawable.getIntrinsicHeight();
|
||||
}
|
||||
|
||||
public int getMinimumWidth() {
|
||||
return this.mDrawable.getMinimumWidth();
|
||||
}
|
||||
|
||||
public int getMinimumHeight() {
|
||||
return this.mDrawable.getMinimumHeight();
|
||||
}
|
||||
|
||||
public boolean getPadding(@NonNull Rect padding) {
|
||||
return this.mDrawable.getPadding(padding);
|
||||
}
|
||||
|
||||
public void invalidateDrawable(@NonNull Drawable who) {
|
||||
this.invalidateSelf();
|
||||
}
|
||||
|
||||
public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
|
||||
this.scheduleSelf(what, when);
|
||||
}
|
||||
|
||||
public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
|
||||
this.unscheduleSelf(what);
|
||||
}
|
||||
|
||||
protected boolean onLevelChange(int level) {
|
||||
return this.mDrawable.setLevel(level);
|
||||
}
|
||||
|
||||
public boolean isAutoMirrored() {
|
||||
return DrawableCompat.isAutoMirrored(this.mDrawable);
|
||||
}
|
||||
|
||||
public void setAutoMirrored(boolean mirrored) {
|
||||
DrawableCompat.setAutoMirrored(this.mDrawable, mirrored);
|
||||
}
|
||||
|
||||
public void setTint(int tint) {
|
||||
DrawableCompat.setTint(this.mDrawable, tint);
|
||||
}
|
||||
|
||||
public void setTintList(ColorStateList tint) {
|
||||
DrawableCompat.setTintList(this.mDrawable, tint);
|
||||
}
|
||||
|
||||
public void setTintMode(@NonNull PorterDuff.Mode tintMode) {
|
||||
DrawableCompat.setTintMode(this.mDrawable, tintMode);
|
||||
}
|
||||
|
||||
public void setHotspot(float x, float y) {
|
||||
DrawableCompat.setHotspot(this.mDrawable, x, y);
|
||||
}
|
||||
|
||||
public void setHotspotBounds(int left, int top, int right, int bottom) {
|
||||
DrawableCompat.setHotspotBounds(this.mDrawable, left, top, right, bottom);
|
||||
}
|
||||
|
||||
public Drawable getWrappedDrawable() {
|
||||
return this.mDrawable;
|
||||
}
|
||||
|
||||
public void setWrappedDrawable(Drawable drawable) {
|
||||
if (this.mDrawable != null) {
|
||||
this.mDrawable.setCallback(null);
|
||||
}
|
||||
|
||||
this.mDrawable = drawable;
|
||||
if (drawable != null) {
|
||||
drawable.setCallback(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,9 @@ import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.yorozuya.MathUtils;
|
||||
|
||||
@@ -36,10 +38,9 @@ import com.hippo.yorozuya.MathUtils;
|
||||
*/
|
||||
public class DrawerArrowDrawable extends Drawable {
|
||||
|
||||
private final Paint mPaint = new Paint();
|
||||
|
||||
// The angle in degrees that the arrow head is inclined at.
|
||||
private static final float ARROW_HEAD_ANGLE = (float) Math.toRadians(45);
|
||||
private final Paint mPaint = new Paint();
|
||||
private final float mBarThickness;
|
||||
// The length of top and bottom bars when they merge into an arrow
|
||||
private final float mTopBottomArrowSize;
|
||||
@@ -56,12 +57,12 @@ public class DrawerArrowDrawable extends Drawable {
|
||||
private final Path mPath = new Path();
|
||||
// The reported intrinsic size of the drawable.
|
||||
private final int mSize;
|
||||
// the amount that overlaps w/ bar size when rotation is max
|
||||
private final float mMaxCutForBarSize;
|
||||
// Whether we should mirror animation when animation is reversed.
|
||||
private boolean mVerticalMirror = false;
|
||||
// The interpolated version of the original progress
|
||||
private float mProgress;
|
||||
// the amount that overlaps w/ bar size when rotation is max
|
||||
private final float mMaxCutForBarSize;
|
||||
|
||||
/**
|
||||
* @param context used to get the configuration for the drawable from
|
||||
|
||||
@@ -18,7 +18,9 @@ package com.hippo.drawable;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.hippo.conaco.Conaco;
|
||||
import com.hippo.conaco.ConacoTask;
|
||||
import com.hippo.conaco.Unikery;
|
||||
@@ -31,11 +33,9 @@ public class UnikeryDrawable extends WrapDrawable implements Unikery<ImageBitmap
|
||||
ObservedTextView.OnWindowAttachListener {
|
||||
|
||||
private static final String TAG = UnikeryDrawable.class.getSimpleName();
|
||||
|
||||
private int mTaskId = Unikery.INVALID_ID;
|
||||
|
||||
private final ObservedTextView mTextView;
|
||||
private final Conaco<ImageBitmap> mConaco;
|
||||
private int mTaskId = Unikery.INVALID_ID;
|
||||
private String mUrl;
|
||||
|
||||
public UnikeryDrawable(ObservedTextView textView, Conaco<ImageBitmap> conaco) {
|
||||
@@ -91,13 +91,13 @@ public class UnikeryDrawable extends WrapDrawable implements Unikery<ImageBitmap
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTaskId(int id) {
|
||||
mTaskId = id;
|
||||
public int getTaskId() {
|
||||
return mTaskId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTaskId() {
|
||||
return mTaskId;
|
||||
public void setTaskId(int id) {
|
||||
mTaskId = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -107,16 +107,20 @@ public class UnikeryDrawable extends WrapDrawable implements Unikery<ImageBitmap
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMiss(int source) {}
|
||||
public void onMiss(int source) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequest() {}
|
||||
public void onRequest() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgress(long singleReceivedSize, long receivedSize, long totalSize) {}
|
||||
public void onProgress(long singleReceivedSize, long receivedSize, long totalSize) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWait() {}
|
||||
public void onWait() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onGetValue(@NonNull ImageBitmap value, int source) {
|
||||
|
||||
@@ -26,14 +26,14 @@ public class WrapDrawable extends Drawable {
|
||||
|
||||
private Drawable mDrawable;
|
||||
|
||||
public void setDrawable(Drawable drawable) {
|
||||
mDrawable = drawable;
|
||||
}
|
||||
|
||||
public Drawable getDrawable() {
|
||||
return mDrawable;
|
||||
}
|
||||
|
||||
public void setDrawable(Drawable drawable) {
|
||||
mDrawable = drawable;
|
||||
}
|
||||
|
||||
public void updateBounds() {
|
||||
setBounds(0, 0, getIntrinsicWidth(), getIntrinsicHeight());
|
||||
}
|
||||
@@ -63,15 +63,6 @@ public class WrapDrawable extends Drawable {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChangingConfigurations(int configs) {
|
||||
super.setChangingConfigurations(configs);
|
||||
|
||||
if (mDrawable != null) {
|
||||
mDrawable.setChangingConfigurations(configs);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChangingConfigurations() {
|
||||
if (mDrawable != null) {
|
||||
@@ -81,6 +72,15 @@ public class WrapDrawable extends Drawable {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChangingConfigurations(int configs) {
|
||||
super.setChangingConfigurations(configs);
|
||||
|
||||
if (mDrawable != null) {
|
||||
mDrawable.setChangingConfigurations(configs);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDither(boolean dither) {
|
||||
super.setDither(dither);
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import com.google.firebase.analytics.FirebaseAnalytics;
|
||||
import com.hippo.scene.SceneFragment;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class Analytics {
|
||||
|
||||
private static final String DEVICE_LANGUAGE = "device_language";
|
||||
|
||||
private static FirebaseAnalytics analytics;
|
||||
|
||||
private Analytics() {}
|
||||
|
||||
public static void start(Context context) {
|
||||
analytics = FirebaseAnalytics.getInstance(context);
|
||||
analytics.setUserId(Settings.getUserID());
|
||||
|
||||
Locale locale = Locale.getDefault();
|
||||
String language = locale.getLanguage();
|
||||
if (TextUtils.isEmpty(language)) {
|
||||
language = "none";
|
||||
}
|
||||
String country = locale.getCountry();
|
||||
if (!TextUtils.isEmpty(country)) {
|
||||
language = language + "-" + country;
|
||||
}
|
||||
language = language.toLowerCase();
|
||||
analytics.setUserProperty(DEVICE_LANGUAGE, language);
|
||||
}
|
||||
|
||||
public static boolean isEnabled() {
|
||||
return analytics != null && Settings.getEnableAnalytics();
|
||||
}
|
||||
|
||||
public static void onSceneView(SceneFragment scene) {
|
||||
if (isEnabled()) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("scene_simple_class", scene.getClass().getSimpleName());
|
||||
bundle.putString("scene_class", scene.getClass().getName());
|
||||
analytics.logEvent("scene_view", bundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,15 +18,19 @@ package com.hippo.ehviewer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.ehviewer.client.exception.ParseException;
|
||||
import com.hippo.util.ReadableTime;
|
||||
import com.hippo.yorozuya.FileUtils;
|
||||
import com.hippo.yorozuya.IOUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class AppConfig {
|
||||
|
||||
@@ -147,11 +151,11 @@ public class AppConfig {
|
||||
String message = e.getMessage();
|
||||
String body = e.getBody();
|
||||
if (null != message) {
|
||||
os.write(message.getBytes("utf-8"));
|
||||
os.write(message.getBytes(StandardCharsets.UTF_8));
|
||||
os.write('\n');
|
||||
}
|
||||
if (null != body) {
|
||||
os.write(body.getBytes("utf-8"));
|
||||
os.write(body.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
os.flush();
|
||||
} catch (IOException e1) {
|
||||
|
||||
@@ -22,135 +22,205 @@ import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Debug;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.hippo.scene.StageActivity;
|
||||
import com.hippo.util.PackageUtils;
|
||||
import com.hippo.util.ReadableTime;
|
||||
import com.hippo.yorozuya.FileUtils;
|
||||
import com.hippo.yorozuya.IOUtils;
|
||||
import com.hippo.yorozuya.OSUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
public final class Crash {
|
||||
private Crash() {}
|
||||
|
||||
@NonNull
|
||||
private static String avoidNull(String str) {
|
||||
return null != str ? str : "null";
|
||||
}
|
||||
|
||||
private static void collectInfo(Context context, FileWriter fw) throws IOException {
|
||||
try {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
PackageInfo pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);
|
||||
if (pi != null) {
|
||||
String versionName = pi.versionName == null ? "null" : pi.versionName;
|
||||
String versionCode = String.valueOf(pi.versionCode);
|
||||
fw.write("======== PackageInfo ========\r\n");
|
||||
fw.write("PackageName=");fw.write(pi.packageName);fw.write("\r\n");
|
||||
fw.write("VersionName=");fw.write(versionName);fw.write("\r\n");
|
||||
fw.write("VersionCode=");fw.write(versionCode);fw.write("\r\n");
|
||||
String signature = PackageUtils.getSignature(context, pi.packageName);
|
||||
fw.write("Signature=");fw.write(null != signature ? signature : "null");fw.write("\r\n");
|
||||
fw.write("\r\n");
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
fw.write("======== PackageInfo ========\r\n");
|
||||
fw.write("Can't get package information\r\n");
|
||||
fw.write("\r\n");
|
||||
private Crash() {
|
||||
}
|
||||
|
||||
// Runtime
|
||||
String topActivityClazzName = "null";
|
||||
String topSceneClazzName = "null";
|
||||
try {
|
||||
Activity topActivity = ((EhApplication) context.getApplicationContext()).getTopActivity();
|
||||
if (null != topActivity) {
|
||||
topActivityClazzName = topActivity.getClass().getName();
|
||||
if (topActivity instanceof StageActivity) {
|
||||
Class<?> clazz = ((StageActivity) topActivity).getTopSceneClass();
|
||||
if (clazz != null) {
|
||||
topSceneClazzName = clazz.getName();
|
||||
}
|
||||
@NonNull
|
||||
private static String avoidNull(String str) {
|
||||
return null != str ? str : "null";
|
||||
}
|
||||
|
||||
private static void collectInfo(Context context, FileWriter fw) throws IOException {
|
||||
try {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
PackageInfo pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);
|
||||
if (pi != null) {
|
||||
String versionName = pi.versionName == null ? "null" : pi.versionName;
|
||||
String versionCode = String.valueOf(pi.versionCode);
|
||||
fw.write("======== PackageInfo ========\r\n");
|
||||
fw.write("PackageName=");
|
||||
fw.write(pi.packageName);
|
||||
fw.write("\r\n");
|
||||
fw.write("VersionName=");
|
||||
fw.write(versionName);
|
||||
fw.write("\r\n");
|
||||
fw.write("VersionCode=");
|
||||
fw.write(versionCode);
|
||||
fw.write("\r\n");
|
||||
String signature = PackageUtils.getSignature(context, pi.packageName);
|
||||
fw.write("Signature=");
|
||||
fw.write(null != signature ? signature : "null");
|
||||
fw.write("\r\n");
|
||||
fw.write("\r\n");
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
fw.write("======== PackageInfo ========\r\n");
|
||||
fw.write("Can't get package information\r\n");
|
||||
fw.write("\r\n");
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
// Ignore
|
||||
}
|
||||
fw.write("======== Runtime ========\r\n");
|
||||
fw.write("TopActivity=");fw.write(avoidNull(topActivityClazzName));fw.write("\r\n");
|
||||
fw.write("TopScene=");fw.write(avoidNull(topSceneClazzName));fw.write("\r\n");
|
||||
fw.write("\r\n");
|
||||
|
||||
// Device info
|
||||
fw.write("======== DeviceInfo ========\r\n");
|
||||
fw.write("BOARD=");fw.write(Build.BOARD);fw.write("\r\n");
|
||||
fw.write("BOOTLOADER=");fw.write(Build.BOOTLOADER);fw.write("\r\n");
|
||||
fw.write("CPU_ABI=");fw.write(Build.CPU_ABI);fw.write("\r\n");
|
||||
fw.write("CPU_ABI2=");fw.write(Build.CPU_ABI2);fw.write("\r\n");
|
||||
fw.write("DEVICE=");fw.write(Build.DEVICE);fw.write("\r\n");
|
||||
fw.write("DISPLAY=");fw.write(Build.DISPLAY);fw.write("\r\n");
|
||||
fw.write("FINGERPRINT=");fw.write(Build.FINGERPRINT);fw.write("\r\n");
|
||||
fw.write("HARDWARE=");fw.write(Build.HARDWARE);fw.write("\r\n");
|
||||
fw.write("HOST=");fw.write(Build.HOST);fw.write("\r\n");
|
||||
fw.write("ID=");fw.write(Build.ID);fw.write("\r\n");
|
||||
fw.write("MANUFACTURER=");fw.write(Build.MANUFACTURER);fw.write("\r\n");
|
||||
fw.write("MODEL=");fw.write(Build.MODEL);fw.write("\r\n");
|
||||
fw.write("PRODUCT=");fw.write(Build.PRODUCT);fw.write("\r\n");
|
||||
fw.write("RADIO=");fw.write(Build.getRadioVersion());fw.write("\r\n");
|
||||
fw.write("SERIAL=");fw.write(Build.SERIAL);fw.write("\r\n");
|
||||
fw.write("TAGS=");fw.write(Build.TAGS);fw.write("\r\n");
|
||||
fw.write("TYPE=");fw.write(Build.TYPE);fw.write("\r\n");
|
||||
fw.write("USER=");fw.write(Build.USER);fw.write("\r\n");
|
||||
fw.write("CODENAME=");fw.write(Build.VERSION.CODENAME);fw.write("\r\n");
|
||||
fw.write("INCREMENTAL=");fw.write(Build.VERSION.INCREMENTAL);fw.write("\r\n");
|
||||
fw.write("RELEASE=");fw.write(Build.VERSION.RELEASE);fw.write("\r\n");
|
||||
fw.write("SDK=");fw.write(Integer.toString(Build.VERSION.SDK_INT));fw.write("\r\n");
|
||||
fw.write("MEMORY=");fw.write(
|
||||
FileUtils.humanReadableByteCount(OSUtils.getAppAllocatedMemory(), false));fw.write("\r\n");
|
||||
fw.write("MEMORY_NATIVE=");fw.write(FileUtils.humanReadableByteCount(Debug.getNativeHeapAllocatedSize(), false));fw.write("\r\n");
|
||||
fw.write("MEMORY_MAX=");fw.write(FileUtils.humanReadableByteCount(OSUtils.getAppMaxMemory(), false));fw.write("\r\n");
|
||||
fw.write("MEMORY_TOTAL=");fw.write(FileUtils.humanReadableByteCount(OSUtils.getTotalMemory(), false));fw.write("\r\n");
|
||||
fw.write("\r\n");
|
||||
}
|
||||
// Runtime
|
||||
String topActivityClazzName = "null";
|
||||
String topSceneClazzName = "null";
|
||||
try {
|
||||
Activity topActivity = ((EhApplication) context.getApplicationContext()).getTopActivity();
|
||||
if (null != topActivity) {
|
||||
topActivityClazzName = topActivity.getClass().getName();
|
||||
if (topActivity instanceof StageActivity) {
|
||||
Class<?> clazz = ((StageActivity) topActivity).getTopSceneClass();
|
||||
if (clazz != null) {
|
||||
topSceneClazzName = clazz.getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
// Ignore
|
||||
}
|
||||
fw.write("======== Runtime ========\r\n");
|
||||
fw.write("TopActivity=");
|
||||
fw.write(avoidNull(topActivityClazzName));
|
||||
fw.write("\r\n");
|
||||
fw.write("TopScene=");
|
||||
fw.write(avoidNull(topSceneClazzName));
|
||||
fw.write("\r\n");
|
||||
fw.write("\r\n");
|
||||
|
||||
private static void getThrowableInfo(Throwable t, FileWriter fw) {
|
||||
PrintWriter printWriter = new PrintWriter(fw);
|
||||
t.printStackTrace(printWriter);
|
||||
Throwable cause = t.getCause();
|
||||
while (cause != null) {
|
||||
cause.printStackTrace(printWriter);
|
||||
cause = cause.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
public static void saveCrashLog(Context context, Throwable t) {
|
||||
File dir = AppConfig.getExternalCrashDir();
|
||||
if (dir == null) {
|
||||
return;
|
||||
// Device info
|
||||
fw.write("======== DeviceInfo ========\r\n");
|
||||
fw.write("BOARD=");
|
||||
fw.write(Build.BOARD);
|
||||
fw.write("\r\n");
|
||||
fw.write("BOOTLOADER=");
|
||||
fw.write(Build.BOOTLOADER);
|
||||
fw.write("\r\n");
|
||||
fw.write("CPU_ABI=");
|
||||
fw.write(Build.CPU_ABI);
|
||||
fw.write("\r\n");
|
||||
fw.write("CPU_ABI2=");
|
||||
fw.write(Build.CPU_ABI2);
|
||||
fw.write("\r\n");
|
||||
fw.write("DEVICE=");
|
||||
fw.write(Build.DEVICE);
|
||||
fw.write("\r\n");
|
||||
fw.write("DISPLAY=");
|
||||
fw.write(Build.DISPLAY);
|
||||
fw.write("\r\n");
|
||||
fw.write("FINGERPRINT=");
|
||||
fw.write(Build.FINGERPRINT);
|
||||
fw.write("\r\n");
|
||||
fw.write("HARDWARE=");
|
||||
fw.write(Build.HARDWARE);
|
||||
fw.write("\r\n");
|
||||
fw.write("HOST=");
|
||||
fw.write(Build.HOST);
|
||||
fw.write("\r\n");
|
||||
fw.write("ID=");
|
||||
fw.write(Build.ID);
|
||||
fw.write("\r\n");
|
||||
fw.write("MANUFACTURER=");
|
||||
fw.write(Build.MANUFACTURER);
|
||||
fw.write("\r\n");
|
||||
fw.write("MODEL=");
|
||||
fw.write(Build.MODEL);
|
||||
fw.write("\r\n");
|
||||
fw.write("PRODUCT=");
|
||||
fw.write(Build.PRODUCT);
|
||||
fw.write("\r\n");
|
||||
fw.write("RADIO=");
|
||||
fw.write(Build.getRadioVersion());
|
||||
fw.write("\r\n");
|
||||
fw.write("SERIAL=");
|
||||
fw.write(Build.SERIAL);
|
||||
fw.write("\r\n");
|
||||
fw.write("TAGS=");
|
||||
fw.write(Build.TAGS);
|
||||
fw.write("\r\n");
|
||||
fw.write("TYPE=");
|
||||
fw.write(Build.TYPE);
|
||||
fw.write("\r\n");
|
||||
fw.write("USER=");
|
||||
fw.write(Build.USER);
|
||||
fw.write("\r\n");
|
||||
fw.write("CODENAME=");
|
||||
fw.write(Build.VERSION.CODENAME);
|
||||
fw.write("\r\n");
|
||||
fw.write("INCREMENTAL=");
|
||||
fw.write(Build.VERSION.INCREMENTAL);
|
||||
fw.write("\r\n");
|
||||
fw.write("RELEASE=");
|
||||
fw.write(Build.VERSION.RELEASE);
|
||||
fw.write("\r\n");
|
||||
fw.write("SDK=");
|
||||
fw.write(Integer.toString(Build.VERSION.SDK_INT));
|
||||
fw.write("\r\n");
|
||||
fw.write("MEMORY=");
|
||||
fw.write(
|
||||
FileUtils.humanReadableByteCount(OSUtils.getAppAllocatedMemory(), false));
|
||||
fw.write("\r\n");
|
||||
fw.write("MEMORY_NATIVE=");
|
||||
fw.write(FileUtils.humanReadableByteCount(Debug.getNativeHeapAllocatedSize(), false));
|
||||
fw.write("\r\n");
|
||||
fw.write("MEMORY_MAX=");
|
||||
fw.write(FileUtils.humanReadableByteCount(OSUtils.getAppMaxMemory(), false));
|
||||
fw.write("\r\n");
|
||||
fw.write("MEMORY_TOTAL=");
|
||||
fw.write(FileUtils.humanReadableByteCount(OSUtils.getTotalMemory(), false));
|
||||
fw.write("\r\n");
|
||||
fw.write("\r\n");
|
||||
}
|
||||
|
||||
String nowString = ReadableTime.getFilenamableTime(System.currentTimeMillis());
|
||||
String fileName = "crash-" + nowString + ".log";
|
||||
File file = new File(dir, fileName);
|
||||
private static void getThrowableInfo(Throwable t, FileWriter fw) {
|
||||
PrintWriter printWriter = new PrintWriter(fw);
|
||||
t.printStackTrace(printWriter);
|
||||
Throwable cause = t.getCause();
|
||||
while (cause != null) {
|
||||
cause.printStackTrace(printWriter);
|
||||
cause = cause.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
FileWriter fw = null;
|
||||
try {
|
||||
fw = new FileWriter(file);
|
||||
fw.write("TIME=");fw.write(nowString);fw.write("\r\n");
|
||||
fw.write("\r\n");
|
||||
collectInfo(context, fw);
|
||||
fw.write("======== CrashInfo ========\r\n");
|
||||
getThrowableInfo(t, fw);
|
||||
fw.write("\r\n");
|
||||
fw.flush();
|
||||
} catch (Exception e) {
|
||||
file.delete();
|
||||
} finally {
|
||||
IOUtils.closeQuietly(fw);
|
||||
public static void saveCrashLog(Context context, Throwable t) {
|
||||
File dir = AppConfig.getExternalCrashDir();
|
||||
if (dir == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String nowString = ReadableTime.getFilenamableTime(System.currentTimeMillis());
|
||||
String fileName = "crash-" + nowString + ".log";
|
||||
File file = new File(dir, fileName);
|
||||
|
||||
FileWriter fw = null;
|
||||
try {
|
||||
fw = new FileWriter(file);
|
||||
fw.write("TIME=");
|
||||
fw.write(nowString);
|
||||
fw.write("\r\n");
|
||||
fw.write("\r\n");
|
||||
collectInfo(context, fw);
|
||||
fw.write("======== CrashInfo ========\r\n");
|
||||
getThrowableInfo(t, fw);
|
||||
fw.write("\r\n");
|
||||
fw.flush();
|
||||
} catch (Exception e) {
|
||||
file.delete();
|
||||
} finally {
|
||||
IOUtils.closeQuietly(fw);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,9 +28,11 @@ import android.content.pm.PackageManager;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Debug;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.collection.LruCache;
|
||||
|
||||
import com.getkeepsafe.relinker.ReLinker;
|
||||
import com.hippo.a7zip.A7Zip;
|
||||
import com.hippo.a7zip.A7ZipExtractLite;
|
||||
@@ -58,21 +60,21 @@ import com.hippo.yorozuya.FileUtils;
|
||||
import com.hippo.yorozuya.IntIdGenerator;
|
||||
import com.hippo.yorozuya.OSUtils;
|
||||
import com.hippo.yorozuya.SimpleHandler;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
public class EhApplication extends RecordingApplication {
|
||||
|
||||
public static final boolean BETA = false;
|
||||
private static final String TAG = EhApplication.class.getSimpleName();
|
||||
private static final String KEY_GLOBAL_STUFF_NEXT_ID = "global_stuff_next_id";
|
||||
|
||||
public static final boolean BETA = false;
|
||||
|
||||
private static final boolean DEBUG_CONACO = false;
|
||||
private static final boolean DEBUG_PRINT_NATIVE_MEMORY = false;
|
||||
private static final boolean DEBUG_PRINT_IMAGE_COUNT = false;
|
||||
@@ -82,6 +84,7 @@ public class EhApplication extends RecordingApplication {
|
||||
|
||||
private final IntIdGenerator mIdGenerator = new IntIdGenerator();
|
||||
private final HashMap<Integer, Object> mGlobalStuffMap = new HashMap<>();
|
||||
private final List<Activity> mActivityList = new ArrayList<>();
|
||||
private EhCookieStore mEhCookieStore;
|
||||
private EhClient mEhClient;
|
||||
private EhProxySelector mEhProxySelector;
|
||||
@@ -93,182 +96,12 @@ public class EhApplication extends RecordingApplication {
|
||||
private DownloadManager mDownloadManager;
|
||||
private Hosts mHosts;
|
||||
private FavouriteStatusRouter mFavouriteStatusRouter;
|
||||
|
||||
private final List<Activity> mActivityList = new ArrayList<>();
|
||||
|
||||
private boolean initialized = false;
|
||||
|
||||
public static EhApplication getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
@Override
|
||||
public void onCreate() {
|
||||
instance = this;
|
||||
|
||||
Thread.UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler();
|
||||
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
|
||||
try {
|
||||
// Always save crash file if onCreate() is not done
|
||||
if (!initialized || Settings.getSaveCrashLog()) {
|
||||
Crash.saveCrashLog(instance, e);
|
||||
}
|
||||
} catch (Throwable ignored) { }
|
||||
|
||||
if (handler != null) {
|
||||
handler.uncaughtException(t, e);
|
||||
}
|
||||
});
|
||||
|
||||
super.onCreate();
|
||||
|
||||
GetText.initialize(this);
|
||||
StatusCodeException.initialize(this);
|
||||
Settings.initialize(this);
|
||||
ReadableTime.initialize(this);
|
||||
Html.initialize(this);
|
||||
AppConfig.initialize(this);
|
||||
SpiderDen.initialize(this);
|
||||
EhDB.initialize(this);
|
||||
EhEngine.initialize();
|
||||
BitmapUtils.initialize(this);
|
||||
Image.initialize(this);
|
||||
A7Zip.loadLibrary(A7ZipExtractLite.LIBRARY, libname -> ReLinker.loadLibrary(EhApplication.this, libname));
|
||||
|
||||
if (EhDB.needMerge()) {
|
||||
EhDB.mergeOldDB(this);
|
||||
}
|
||||
|
||||
if (Settings.getEnableAnalytics()) {
|
||||
Analytics.start(this);
|
||||
}
|
||||
|
||||
// Do io tasks in new thread
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
// Check no media file
|
||||
try {
|
||||
UniFile downloadLocation = Settings.getDownloadLocation();
|
||||
if (Settings.getMediaScan()) {
|
||||
CommonOperations.removeNoMediaFile(downloadLocation);
|
||||
} else {
|
||||
CommonOperations.ensureNoMediaFile(downloadLocation);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
ExceptionUtils.throwIfFatal(t);
|
||||
}
|
||||
|
||||
// Clear temp files
|
||||
try {
|
||||
clearTempDir();
|
||||
} catch (Throwable t) {
|
||||
ExceptionUtils.throwIfFatal(t);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}.executeOnExecutor(IoThreadPoolExecutor.getInstance());
|
||||
|
||||
// Check app update
|
||||
update();
|
||||
|
||||
// Update version code
|
||||
try {
|
||||
PackageInfo pi= getPackageManager().getPackageInfo(getPackageName(), 0);
|
||||
Settings.putVersionCode(pi.versionCode);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
mIdGenerator.setNextId(Settings.getInt(KEY_GLOBAL_STUFF_NEXT_ID, 0));
|
||||
|
||||
if (DEBUG_PRINT_NATIVE_MEMORY || DEBUG_PRINT_IMAGE_COUNT) {
|
||||
debugPrint();
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
private void clearTempDir() {
|
||||
File dir = AppConfig.getTempDir();
|
||||
if (null != dir) {
|
||||
FileUtils.deleteContent(dir);
|
||||
}
|
||||
dir = AppConfig.getExternalTempDir();
|
||||
if (null != dir) {
|
||||
FileUtils.deleteContent(dir);
|
||||
}
|
||||
|
||||
// Add .nomedia to external temp dir
|
||||
CommonOperations.ensureNoMediaFile(UniFile.fromFile(AppConfig.getExternalTempDir()));
|
||||
}
|
||||
|
||||
private void update() {
|
||||
int version = Settings.getVersionCode();
|
||||
if (version < 52) {
|
||||
Settings.putGuideGallery(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearMemoryCache() {
|
||||
if (null != mConaco) {
|
||||
mConaco.getBeerBelly().clearMemory();
|
||||
}
|
||||
if (null != mGalleryDetailCache) {
|
||||
mGalleryDetailCache.evictAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrimMemory(int level) {
|
||||
super.onTrimMemory(level);
|
||||
|
||||
if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW) {
|
||||
clearMemoryCache();
|
||||
}
|
||||
}
|
||||
|
||||
private void debugPrint() {
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (DEBUG_PRINT_NATIVE_MEMORY) {
|
||||
Log.i(TAG, "Native memory: " + FileUtils.humanReadableByteCount(
|
||||
Debug.getNativeHeapAllocatedSize(), false));
|
||||
}
|
||||
if (DEBUG_PRINT_IMAGE_COUNT) {
|
||||
Log.i(TAG, "Image count: " + Image.getImageCount());
|
||||
}
|
||||
SimpleHandler.getInstance().postDelayed(this, DEBUG_PRINT_INTERVAL);
|
||||
}
|
||||
}.run();
|
||||
}
|
||||
|
||||
public int putGlobalStuff(@NonNull Object o) {
|
||||
int id = mIdGenerator.nextId();
|
||||
mGlobalStuffMap.put(id, o);
|
||||
Settings.putInt(KEY_GLOBAL_STUFF_NEXT_ID, mIdGenerator.nextId());
|
||||
return id;
|
||||
}
|
||||
|
||||
public boolean containGlobalStuff(int id) {
|
||||
return mGlobalStuffMap.containsKey(id);
|
||||
}
|
||||
|
||||
public Object getGlobalStuff(int id) {
|
||||
return mGlobalStuffMap.get(id);
|
||||
}
|
||||
|
||||
public Object removeGlobalStuff(int id) {
|
||||
return mGlobalStuffMap.remove(id);
|
||||
}
|
||||
|
||||
public boolean removeGlobalStuff(Object o) {
|
||||
return mGlobalStuffMap.values().removeAll(Collections.singleton(o));
|
||||
}
|
||||
|
||||
public static EhCookieStore getEhCookieStore(@NonNull Context context) {
|
||||
EhApplication application = ((EhApplication) context.getApplicationContext());
|
||||
if (application.mEhCookieStore == null) {
|
||||
@@ -410,6 +243,170 @@ public class EhApplication extends RecordingApplication {
|
||||
return "ehviewersu$gmail.com".replace('$', '@');
|
||||
}
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
@Override
|
||||
public void onCreate() {
|
||||
instance = this;
|
||||
|
||||
Thread.UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler();
|
||||
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
|
||||
try {
|
||||
// Always save crash file if onCreate() is not done
|
||||
if (!initialized || Settings.getSaveCrashLog()) {
|
||||
Crash.saveCrashLog(instance, e);
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
if (handler != null) {
|
||||
handler.uncaughtException(t, e);
|
||||
}
|
||||
});
|
||||
|
||||
super.onCreate();
|
||||
|
||||
GetText.initialize(this);
|
||||
StatusCodeException.initialize(this);
|
||||
Settings.initialize(this);
|
||||
ReadableTime.initialize(this);
|
||||
Html.initialize(this);
|
||||
AppConfig.initialize(this);
|
||||
SpiderDen.initialize(this);
|
||||
EhDB.initialize(this);
|
||||
EhEngine.initialize();
|
||||
BitmapUtils.initialize(this);
|
||||
Image.initialize(this);
|
||||
A7Zip.loadLibrary(A7ZipExtractLite.LIBRARY, libname -> ReLinker.loadLibrary(EhApplication.this, libname));
|
||||
|
||||
if (EhDB.needMerge()) {
|
||||
EhDB.mergeOldDB(this);
|
||||
}
|
||||
|
||||
// Do io tasks in new thread
|
||||
new AsyncTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
// Check no media file
|
||||
try {
|
||||
UniFile downloadLocation = Settings.getDownloadLocation();
|
||||
if (Settings.getMediaScan()) {
|
||||
CommonOperations.removeNoMediaFile(downloadLocation);
|
||||
} else {
|
||||
CommonOperations.ensureNoMediaFile(downloadLocation);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
ExceptionUtils.throwIfFatal(t);
|
||||
}
|
||||
|
||||
// Clear temp files
|
||||
try {
|
||||
clearTempDir();
|
||||
} catch (Throwable t) {
|
||||
ExceptionUtils.throwIfFatal(t);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}.executeOnExecutor(IoThreadPoolExecutor.getInstance());
|
||||
|
||||
// Check app update
|
||||
update();
|
||||
|
||||
// Update version code
|
||||
try {
|
||||
PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(), 0);
|
||||
Settings.putVersionCode(pi.versionCode);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
mIdGenerator.setNextId(Settings.getInt(KEY_GLOBAL_STUFF_NEXT_ID, 0));
|
||||
|
||||
if (DEBUG_PRINT_NATIVE_MEMORY || DEBUG_PRINT_IMAGE_COUNT) {
|
||||
debugPrint();
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
private void clearTempDir() {
|
||||
File dir = AppConfig.getTempDir();
|
||||
if (null != dir) {
|
||||
FileUtils.deleteContent(dir);
|
||||
}
|
||||
dir = AppConfig.getExternalTempDir();
|
||||
if (null != dir) {
|
||||
FileUtils.deleteContent(dir);
|
||||
}
|
||||
|
||||
// Add .nomedia to external temp dir
|
||||
CommonOperations.ensureNoMediaFile(UniFile.fromFile(AppConfig.getExternalTempDir()));
|
||||
}
|
||||
|
||||
private void update() {
|
||||
int version = Settings.getVersionCode();
|
||||
if (version < 52) {
|
||||
Settings.putGuideGallery(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearMemoryCache() {
|
||||
if (null != mConaco) {
|
||||
mConaco.getBeerBelly().clearMemory();
|
||||
}
|
||||
if (null != mGalleryDetailCache) {
|
||||
mGalleryDetailCache.evictAll();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrimMemory(int level) {
|
||||
super.onTrimMemory(level);
|
||||
|
||||
if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW) {
|
||||
clearMemoryCache();
|
||||
}
|
||||
}
|
||||
|
||||
private void debugPrint() {
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (DEBUG_PRINT_NATIVE_MEMORY) {
|
||||
Log.i(TAG, "Native memory: " + FileUtils.humanReadableByteCount(
|
||||
Debug.getNativeHeapAllocatedSize(), false));
|
||||
}
|
||||
if (DEBUG_PRINT_IMAGE_COUNT) {
|
||||
Log.i(TAG, "Image count: " + Image.getImageCount());
|
||||
}
|
||||
SimpleHandler.getInstance().postDelayed(this, DEBUG_PRINT_INTERVAL);
|
||||
}
|
||||
}.run();
|
||||
}
|
||||
|
||||
public int putGlobalStuff(@NonNull Object o) {
|
||||
int id = mIdGenerator.nextId();
|
||||
mGlobalStuffMap.put(id, o);
|
||||
Settings.putInt(KEY_GLOBAL_STUFF_NEXT_ID, mIdGenerator.nextId());
|
||||
return id;
|
||||
}
|
||||
|
||||
public boolean containGlobalStuff(int id) {
|
||||
return mGlobalStuffMap.containsKey(id);
|
||||
}
|
||||
|
||||
public Object getGlobalStuff(int id) {
|
||||
return mGlobalStuffMap.get(id);
|
||||
}
|
||||
|
||||
public Object removeGlobalStuff(int id) {
|
||||
return mGlobalStuffMap.remove(id);
|
||||
}
|
||||
|
||||
public boolean removeGlobalStuff(Object o) {
|
||||
return mGlobalStuffMap.values().removeAll(Collections.singleton(o));
|
||||
}
|
||||
|
||||
public void registerActivity(Activity activity) {
|
||||
mActivityList.add(activity);
|
||||
}
|
||||
|
||||
@@ -21,8 +21,10 @@ import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo;
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder;
|
||||
import com.hippo.ehviewer.dao.DaoMaster;
|
||||
@@ -47,9 +49,7 @@ import com.hippo.util.SqlUtils;
|
||||
import com.hippo.yorozuya.IOUtils;
|
||||
import com.hippo.yorozuya.ObjectUtils;
|
||||
import com.hippo.yorozuya.collect.SparseJLArray;
|
||||
import de.greenrobot.dao.AbstractDao;
|
||||
import de.greenrobot.dao.query.CloseableListIterator;
|
||||
import de.greenrobot.dao.query.LazyList;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
@@ -59,6 +59,10 @@ import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import de.greenrobot.dao.AbstractDao;
|
||||
import de.greenrobot.dao.query.CloseableListIterator;
|
||||
import de.greenrobot.dao.query.LazyList;
|
||||
|
||||
public class EhDB {
|
||||
|
||||
private static final String TAG = EhDB.class.getSimpleName();
|
||||
@@ -70,34 +74,16 @@ public class EhDB {
|
||||
private static boolean sHasOldDB;
|
||||
private static boolean sNewDB;
|
||||
|
||||
private static class DBOpenHelper extends DaoMaster.OpenHelper {
|
||||
|
||||
public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
|
||||
super(context, name, factory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
super.onCreate(db);
|
||||
sNewDB = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
upgradeDB(db, oldVersion);
|
||||
}
|
||||
}
|
||||
|
||||
private static void upgradeDB(SQLiteDatabase db, int oldVersion) {
|
||||
switch (oldVersion) {
|
||||
case 1: // 1 to 2, add FILTER
|
||||
FilterDao.createTable(db, true);
|
||||
case 2: // 2 to 3, add ENABLE column to table FILTER
|
||||
db.execSQL("CREATE TABLE " + "\"FILTER2\" (" +
|
||||
"\"_id\" INTEGER PRIMARY KEY ," +
|
||||
"\"MODE\" INTEGER NOT NULL ," +
|
||||
"\"TEXT\" TEXT," +
|
||||
"\"ENABLE\" INTEGER);");
|
||||
"\"_id\" INTEGER PRIMARY KEY ," +
|
||||
"\"MODE\" INTEGER NOT NULL ," +
|
||||
"\"TEXT\" TEXT," +
|
||||
"\"ENABLE\" INTEGER);");
|
||||
db.execSQL("INSERT INTO \"FILTER2\" (" +
|
||||
"_id, MODE, TEXT, ENABLE)" +
|
||||
"SELECT _id, MODE, TEXT, 1 FROM FILTER;");
|
||||
@@ -105,48 +91,24 @@ public class EhDB {
|
||||
db.execSQL("ALTER TABLE FILTER2 RENAME TO FILTER");
|
||||
case 3: // 3 to 4, add PAGE_FROM and PAGE_TO column to QUICK_SEARCH
|
||||
db.execSQL("CREATE TABLE " + "\"QUICK_SEARCH2\" (" +
|
||||
"\"_id\" INTEGER PRIMARY KEY ," +
|
||||
"\"NAME\" TEXT," +
|
||||
"\"MODE\" INTEGER NOT NULL ," +
|
||||
"\"CATEGORY\" INTEGER NOT NULL ," +
|
||||
"\"KEYWORD\" TEXT," +
|
||||
"\"ADVANCE_SEARCH\" INTEGER NOT NULL ," +
|
||||
"\"MIN_RATING\" INTEGER NOT NULL ," +
|
||||
"\"PAGE_FROM\" INTEGER NOT NULL ," +
|
||||
"\"PAGE_TO\" INTEGER NOT NULL ," +
|
||||
"\"TIME\" INTEGER NOT NULL );");
|
||||
"\"_id\" INTEGER PRIMARY KEY ," +
|
||||
"\"NAME\" TEXT," +
|
||||
"\"MODE\" INTEGER NOT NULL ," +
|
||||
"\"CATEGORY\" INTEGER NOT NULL ," +
|
||||
"\"KEYWORD\" TEXT," +
|
||||
"\"ADVANCE_SEARCH\" INTEGER NOT NULL ," +
|
||||
"\"MIN_RATING\" INTEGER NOT NULL ," +
|
||||
"\"PAGE_FROM\" INTEGER NOT NULL ," +
|
||||
"\"PAGE_TO\" INTEGER NOT NULL ," +
|
||||
"\"TIME\" INTEGER NOT NULL );");
|
||||
db.execSQL("INSERT INTO \"QUICK_SEARCH2\" (" +
|
||||
"_id, NAME, MODE, CATEGORY, KEYWORD, ADVANCE_SEARCH, MIN_RATING, PAGE_FROM, PAGE_TO, TIME)" +
|
||||
"SELECT _id, NAME, MODE, CATEGORY, KEYWORD, ADVANCE_SEARCH, MIN_RATING, -1, -1, TIME FROM QUICK_SEARCH;");
|
||||
"_id, NAME, MODE, CATEGORY, KEYWORD, ADVANCE_SEARCH, MIN_RATING, PAGE_FROM, PAGE_TO, TIME)" +
|
||||
"SELECT _id, NAME, MODE, CATEGORY, KEYWORD, ADVANCE_SEARCH, MIN_RATING, -1, -1, TIME FROM QUICK_SEARCH;");
|
||||
db.execSQL("DROP TABLE QUICK_SEARCH");
|
||||
db.execSQL("ALTER TABLE QUICK_SEARCH2 RENAME TO QUICK_SEARCH");
|
||||
}
|
||||
}
|
||||
|
||||
private static class OldDBHelper extends SQLiteOpenHelper {
|
||||
|
||||
private static final String DB_NAME = "data";
|
||||
private static final int VERSION = 4;
|
||||
|
||||
private static final String TABLE_GALLERY = "gallery";
|
||||
private static final String TABLE_LOCAL_FAVOURITE = "local_favourite";
|
||||
private static final String TABLE_TAG = "tag";
|
||||
private static final String TABLE_DOWNLOAD = "download";
|
||||
private static final String TABLE_HISTORY = "history";
|
||||
|
||||
public OldDBHelper(Context context) {
|
||||
super(context, DB_NAME, null, VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
}
|
||||
}
|
||||
|
||||
public static void initialize(Context context) {
|
||||
sHasOldDB = context.getDatabasePath("data").exists();
|
||||
|
||||
@@ -362,7 +324,7 @@ public class EhDB {
|
||||
DownloadsDao dao = sDaoSession.getDownloadsDao();
|
||||
List<DownloadInfo> list = dao.queryBuilder().orderDesc(DownloadsDao.Properties.Time).list();
|
||||
// Fix state
|
||||
for (DownloadInfo info: list) {
|
||||
for (DownloadInfo info : list) {
|
||||
if (info.state == DownloadInfo.STATE_WAIT || info.state == DownloadInfo.STATE_DOWNLOAD) {
|
||||
info.state = DownloadInfo.STATE_NONE;
|
||||
}
|
||||
@@ -491,7 +453,7 @@ public class EhDB {
|
||||
}
|
||||
|
||||
public static synchronized List<GalleryInfo> searchLocalFavorites(String query) {
|
||||
query = SqlUtils.sqlEscapeString("%" + query+ "%");
|
||||
query = SqlUtils.sqlEscapeString("%" + query + "%");
|
||||
LocalFavoritesDao dao = sDaoSession.getLocalFavoritesDao();
|
||||
List<LocalFavoriteInfo> list = dao.queryBuilder().orderDesc(LocalFavoritesDao.Properties.Time)
|
||||
.where(LocalFavoritesDao.Properties.Title.like(query)).list();
|
||||
@@ -506,7 +468,7 @@ public class EhDB {
|
||||
|
||||
public static synchronized void removeLocalFavorites(long[] gidArray) {
|
||||
LocalFavoritesDao dao = sDaoSession.getLocalFavoritesDao();
|
||||
for (long gid: gidArray) {
|
||||
for (long gid : gidArray) {
|
||||
dao.deleteByKey(gid);
|
||||
}
|
||||
}
|
||||
@@ -531,7 +493,7 @@ public class EhDB {
|
||||
}
|
||||
|
||||
public static synchronized void putLocalFavorites(List<GalleryInfo> galleryInfoList) {
|
||||
for (GalleryInfo gi: galleryInfoList) {
|
||||
for (GalleryInfo gi : galleryInfoList) {
|
||||
putLocalFavorites(gi);
|
||||
}
|
||||
}
|
||||
@@ -607,7 +569,7 @@ public class EhDB {
|
||||
|
||||
public static synchronized void putHistoryInfo(List<HistoryInfo> historyInfoList) {
|
||||
HistoryDao dao = sDaoSession.getHistoryDao();
|
||||
for (HistoryInfo info: historyInfoList) {
|
||||
for (HistoryInfo info : historyInfoList) {
|
||||
if (null == dao.load(info.gid)) {
|
||||
dao.insert(info);
|
||||
}
|
||||
@@ -670,14 +632,22 @@ public class EhDB {
|
||||
try (SQLiteDatabase db = helper.getWritableDatabase()) {
|
||||
DaoMaster daoMaster = new DaoMaster(db);
|
||||
DaoSession exportSession = daoMaster.newSession();
|
||||
if (!copyDao(sDaoSession.getDownloadsDao(), exportSession.getDownloadsDao())) return false;
|
||||
if (!copyDao(sDaoSession.getDownloadLabelDao(), exportSession.getDownloadLabelDao())) return false;
|
||||
if (!copyDao(sDaoSession.getDownloadDirnameDao(), exportSession.getDownloadDirnameDao())) return false;
|
||||
if (!copyDao(sDaoSession.getHistoryDao(), exportSession.getHistoryDao())) return false;
|
||||
if (!copyDao(sDaoSession.getQuickSearchDao(), exportSession.getQuickSearchDao())) return false;
|
||||
if (!copyDao(sDaoSession.getLocalFavoritesDao(), exportSession.getLocalFavoritesDao())) return false;
|
||||
if (!copyDao(sDaoSession.getBookmarksBao(), exportSession.getBookmarksBao())) return false;
|
||||
if (!copyDao(sDaoSession.getFilterDao(), exportSession.getFilterDao())) return false;
|
||||
if (!copyDao(sDaoSession.getDownloadsDao(), exportSession.getDownloadsDao()))
|
||||
return false;
|
||||
if (!copyDao(sDaoSession.getDownloadLabelDao(), exportSession.getDownloadLabelDao()))
|
||||
return false;
|
||||
if (!copyDao(sDaoSession.getDownloadDirnameDao(), exportSession.getDownloadDirnameDao()))
|
||||
return false;
|
||||
if (!copyDao(sDaoSession.getHistoryDao(), exportSession.getHistoryDao()))
|
||||
return false;
|
||||
if (!copyDao(sDaoSession.getQuickSearchDao(), exportSession.getQuickSearchDao()))
|
||||
return false;
|
||||
if (!copyDao(sDaoSession.getLocalFavoritesDao(), exportSession.getLocalFavoritesDao()))
|
||||
return false;
|
||||
if (!copyDao(sDaoSession.getBookmarksBao(), exportSession.getBookmarksBao()))
|
||||
return false;
|
||||
if (!copyDao(sDaoSession.getFilterDao(), exportSession.getFilterDao()))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy export db to data dir
|
||||
@@ -737,7 +707,7 @@ public class EhDB {
|
||||
|
||||
// Download dirname
|
||||
List<DownloadDirname> downloadDirnameList = session.getDownloadDirnameDao().queryBuilder().list();
|
||||
for (DownloadDirname dirname: downloadDirnameList) {
|
||||
for (DownloadDirname dirname : downloadDirnameList) {
|
||||
putDownloadDirname(dirname.getGid(), dirname.getDirname());
|
||||
}
|
||||
|
||||
@@ -748,9 +718,9 @@ public class EhDB {
|
||||
// QuickSearch
|
||||
List<QuickSearch> quickSearchList = session.getQuickSearchDao().queryBuilder().list();
|
||||
List<QuickSearch> currentQuickSearchList = sDaoSession.getQuickSearchDao().queryBuilder().list();
|
||||
for (QuickSearch quickSearch: quickSearchList) {
|
||||
for (QuickSearch quickSearch : quickSearchList) {
|
||||
String name = quickSearch.name;
|
||||
for (QuickSearch q: currentQuickSearchList) {
|
||||
for (QuickSearch q : currentQuickSearchList) {
|
||||
if (ObjectUtils.equal(q.name, name)) {
|
||||
// The same name
|
||||
name = null;
|
||||
@@ -765,7 +735,7 @@ public class EhDB {
|
||||
|
||||
// LocalFavorites
|
||||
List<LocalFavoriteInfo> localFavoriteInfoList = session.getLocalFavoritesDao().queryBuilder().list();
|
||||
for (LocalFavoriteInfo info: localFavoriteInfoList) {
|
||||
for (LocalFavoriteInfo info : localFavoriteInfoList) {
|
||||
putLocalFavorites(info);
|
||||
}
|
||||
|
||||
@@ -775,7 +745,7 @@ public class EhDB {
|
||||
// Filter
|
||||
List<Filter> filterList = session.getFilterDao().queryBuilder().list();
|
||||
List<Filter> currentFilterList = sDaoSession.getFilterDao().queryBuilder().list();
|
||||
for (Filter filter: filterList) {
|
||||
for (Filter filter : filterList) {
|
||||
if (!currentFilterList.contains(filter)) {
|
||||
addFilter(filter);
|
||||
}
|
||||
@@ -788,4 +758,46 @@ public class EhDB {
|
||||
return context.getString(R.string.cant_read_the_file);
|
||||
}
|
||||
}
|
||||
|
||||
private static class DBOpenHelper extends DaoMaster.OpenHelper {
|
||||
|
||||
public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
|
||||
super(context, name, factory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
super.onCreate(db);
|
||||
sNewDB = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
upgradeDB(db, oldVersion);
|
||||
}
|
||||
}
|
||||
|
||||
private static class OldDBHelper extends SQLiteOpenHelper {
|
||||
|
||||
private static final String DB_NAME = "data";
|
||||
private static final int VERSION = 4;
|
||||
|
||||
private static final String TABLE_GALLERY = "gallery";
|
||||
private static final String TABLE_LOCAL_FAVOURITE = "local_favourite";
|
||||
private static final String TABLE_TAG = "tag";
|
||||
private static final String TABLE_DOWNLOAD = "download";
|
||||
private static final String TABLE_HISTORY = "history";
|
||||
|
||||
public OldDBHelper(Context context) {
|
||||
super(context, DB_NAME, null, VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,10 @@
|
||||
package com.hippo.ehviewer;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.hippo.network.InetValidator;
|
||||
import com.hippo.util.ExceptionUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
@@ -31,76 +33,78 @@ import java.util.List;
|
||||
|
||||
public class EhProxySelector extends ProxySelector {
|
||||
|
||||
public static final int TYPE_DIRECT = 0;
|
||||
public static final int TYPE_SYSTEM = 1;
|
||||
public static final int TYPE_HTTP = 2;
|
||||
public static final int TYPE_SOCKS = 3;
|
||||
public static final int TYPE_DIRECT = 0;
|
||||
public static final int TYPE_SYSTEM = 1;
|
||||
public static final int TYPE_HTTP = 2;
|
||||
public static final int TYPE_SOCKS = 3;
|
||||
|
||||
private ProxySelector delegation;
|
||||
private ProxySelector alternative;
|
||||
private ProxySelector delegation;
|
||||
private ProxySelector alternative;
|
||||
|
||||
EhProxySelector() {
|
||||
alternative = ProxySelector.getDefault();
|
||||
if (alternative == null) {
|
||||
alternative = new NullProxySelector();
|
||||
}
|
||||
|
||||
updateProxy();
|
||||
}
|
||||
|
||||
public void updateProxy() {
|
||||
switch (Settings.getProxyType()) {
|
||||
case TYPE_DIRECT:
|
||||
delegation = new NullProxySelector();
|
||||
break;
|
||||
default:
|
||||
case TYPE_SYSTEM:
|
||||
delegation = alternative;
|
||||
break;
|
||||
case TYPE_HTTP:
|
||||
case TYPE_SOCKS:
|
||||
delegation = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Proxy> select(URI uri) {
|
||||
int type = Settings.getProxyType();
|
||||
if (type == TYPE_HTTP || type == TYPE_SOCKS) {
|
||||
try {
|
||||
String ip = Settings.getProxyIp();
|
||||
int port = Settings.getProxyPort();
|
||||
if (!TextUtils.isEmpty(ip) && InetValidator.isValidInetPort(port)) {
|
||||
InetAddress inetAddress = InetAddress.getByName(ip);
|
||||
SocketAddress socketAddress = new InetSocketAddress(inetAddress, port);
|
||||
return Collections.singletonList(new Proxy(type == TYPE_HTTP ? Proxy.Type.HTTP : Proxy.Type.SOCKS, socketAddress));
|
||||
EhProxySelector() {
|
||||
alternative = ProxySelector.getDefault();
|
||||
if (alternative == null) {
|
||||
alternative = new NullProxySelector();
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
ExceptionUtils.throwIfFatal(t);
|
||||
}
|
||||
|
||||
updateProxy();
|
||||
}
|
||||
|
||||
if (delegation != null) {
|
||||
return delegation.select(uri);
|
||||
public void updateProxy() {
|
||||
switch (Settings.getProxyType()) {
|
||||
case TYPE_DIRECT:
|
||||
delegation = new NullProxySelector();
|
||||
break;
|
||||
default:
|
||||
case TYPE_SYSTEM:
|
||||
delegation = alternative;
|
||||
break;
|
||||
case TYPE_HTTP:
|
||||
case TYPE_SOCKS:
|
||||
delegation = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return alternative.select(uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
|
||||
if (delegation != null) {
|
||||
delegation.select(uri);
|
||||
}
|
||||
}
|
||||
|
||||
private static class NullProxySelector extends ProxySelector {
|
||||
@Override
|
||||
public List<Proxy> select(URI uri) {
|
||||
return Collections.singletonList(Proxy.NO_PROXY);
|
||||
int type = Settings.getProxyType();
|
||||
if (type == TYPE_HTTP || type == TYPE_SOCKS) {
|
||||
try {
|
||||
String ip = Settings.getProxyIp();
|
||||
int port = Settings.getProxyPort();
|
||||
if (!TextUtils.isEmpty(ip) && InetValidator.isValidInetPort(port)) {
|
||||
InetAddress inetAddress = InetAddress.getByName(ip);
|
||||
SocketAddress socketAddress = new InetSocketAddress(inetAddress, port);
|
||||
return Collections.singletonList(new Proxy(type == TYPE_HTTP ? Proxy.Type.HTTP : Proxy.Type.SOCKS, socketAddress));
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
ExceptionUtils.throwIfFatal(t);
|
||||
}
|
||||
}
|
||||
|
||||
if (delegation != null) {
|
||||
return delegation.select(uri);
|
||||
}
|
||||
|
||||
return alternative.select(uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { }
|
||||
}
|
||||
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
|
||||
if (delegation != null) {
|
||||
delegation.select(uri);
|
||||
}
|
||||
}
|
||||
|
||||
private static class NullProxySelector extends ProxySelector {
|
||||
@Override
|
||||
public List<Proxy> select(URI uri) {
|
||||
return Collections.singletonList(Proxy.NO_PROXY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,10 @@
|
||||
package com.hippo.ehviewer;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo;
|
||||
import com.hippo.yorozuya.IntIdGenerator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -26,47 +28,47 @@ import java.util.Map;
|
||||
|
||||
public class FavouriteStatusRouter {
|
||||
|
||||
private static final String KEY_DATA_MAP_NEXT_ID = "data_map_next_id";
|
||||
private static final String KEY_DATA_MAP_NEXT_ID = "data_map_next_id";
|
||||
|
||||
private final IntIdGenerator idGenerator = new IntIdGenerator(Settings.getInt(KEY_DATA_MAP_NEXT_ID, 0));
|
||||
@SuppressLint("UseSparseArrays")
|
||||
private final HashMap<Integer, Map<Long, GalleryInfo>> maps = new HashMap<>();
|
||||
private final IntIdGenerator idGenerator = new IntIdGenerator(Settings.getInt(KEY_DATA_MAP_NEXT_ID, 0));
|
||||
@SuppressLint("UseSparseArrays")
|
||||
private final HashMap<Integer, Map<Long, GalleryInfo>> maps = new HashMap<>();
|
||||
|
||||
private List<Listener> listeners = new ArrayList<>();
|
||||
private List<Listener> listeners = new ArrayList<>();
|
||||
|
||||
public int saveDataMap(Map<Long, GalleryInfo> map) {
|
||||
int id = idGenerator.nextId();
|
||||
maps.put(id, map);
|
||||
Settings.putInt(KEY_DATA_MAP_NEXT_ID, idGenerator.nextId());
|
||||
return id;
|
||||
}
|
||||
|
||||
public Map<Long, GalleryInfo> restoreDataMap(int id) {
|
||||
return maps.remove(id);
|
||||
}
|
||||
|
||||
public void modifyFavourites(long gid, int slot) {
|
||||
for (Map<Long, GalleryInfo> map : maps.values()) {
|
||||
GalleryInfo info = map.get(gid);
|
||||
if (info != null) {
|
||||
info.favoriteSlot = slot;
|
||||
}
|
||||
public int saveDataMap(Map<Long, GalleryInfo> map) {
|
||||
int id = idGenerator.nextId();
|
||||
maps.put(id, map);
|
||||
Settings.putInt(KEY_DATA_MAP_NEXT_ID, idGenerator.nextId());
|
||||
return id;
|
||||
}
|
||||
|
||||
for (Listener listener : listeners) {
|
||||
listener.onModifyFavourites(gid, slot);
|
||||
public Map<Long, GalleryInfo> restoreDataMap(int id) {
|
||||
return maps.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
public void addListener(Listener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
public void modifyFavourites(long gid, int slot) {
|
||||
for (Map<Long, GalleryInfo> map : maps.values()) {
|
||||
GalleryInfo info = map.get(gid);
|
||||
if (info != null) {
|
||||
info.favoriteSlot = slot;
|
||||
}
|
||||
}
|
||||
|
||||
public void removeListener(Listener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
for (Listener listener : listeners) {
|
||||
listener.onModifyFavourites(gid, slot);
|
||||
}
|
||||
}
|
||||
|
||||
public interface Listener {
|
||||
void onModifyFavourites(long gid, int slot);
|
||||
}
|
||||
public void addListener(Listener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeListener(Listener listener) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
|
||||
public interface Listener {
|
||||
void onModifyFavourites(long gid, int slot);
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package com.hippo.ehviewer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
|
||||
@@ -26,9 +26,12 @@ import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.database.MSQLiteBuilder;
|
||||
import com.hippo.util.SqlUtils;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
@@ -36,315 +39,310 @@ import java.util.List;
|
||||
|
||||
public class Hosts {
|
||||
|
||||
private static final int VERSION_1 = 1;
|
||||
private static final String TABLE_HOSTS = "HOSTS";
|
||||
private static final String COLUMN_HOST = "HOST";
|
||||
private static final String COLUMN_IP = "IP";
|
||||
private static final int VERSION_1 = 1;
|
||||
private static final String TABLE_HOSTS = "HOSTS";
|
||||
private static final String COLUMN_HOST = "HOST";
|
||||
private static final String COLUMN_IP = "IP";
|
||||
|
||||
private static final int DB_VERSION = VERSION_1;
|
||||
private static final int DB_VERSION = VERSION_1;
|
||||
|
||||
private final SQLiteOpenHelper helper;
|
||||
private final SQLiteDatabase db;
|
||||
private final SQLiteOpenHelper helper;
|
||||
private final SQLiteDatabase db;
|
||||
|
||||
public Hosts(Context context, String name) {
|
||||
helper = new MSQLiteBuilder()
|
||||
.version(VERSION_1)
|
||||
.createTable(TABLE_HOSTS)
|
||||
.insertColumn(TABLE_HOSTS, COLUMN_HOST, String.class)
|
||||
.insertColumn(TABLE_HOSTS, COLUMN_IP, String.class)
|
||||
.build(context, name, DB_VERSION);
|
||||
db = helper.getWritableDatabase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a InetAddress with the host.
|
||||
*/
|
||||
@Nullable
|
||||
public InetAddress get(String host) {
|
||||
if (!isValidHost(host)) {
|
||||
return null;
|
||||
public Hosts(Context context, String name) {
|
||||
helper = new MSQLiteBuilder()
|
||||
.version(VERSION_1)
|
||||
.createTable(TABLE_HOSTS)
|
||||
.insertColumn(TABLE_HOSTS, COLUMN_HOST, String.class)
|
||||
.insertColumn(TABLE_HOSTS, COLUMN_IP, String.class)
|
||||
.build(context, name, DB_VERSION);
|
||||
db = helper.getWritableDatabase();
|
||||
}
|
||||
|
||||
Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_HOSTS + " WHERE " + COLUMN_HOST + " = ?;", new String[] {host});
|
||||
try {
|
||||
if (cursor.moveToNext()) {
|
||||
String ip = SqlUtils.getString(cursor, COLUMN_IP, null);
|
||||
return toInetAddress(host, ip);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
@Nullable
|
||||
public static InetAddress toInetAddress(String host, String ip) {
|
||||
if (!isValidHost(host)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean contains(String host) {
|
||||
Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_HOSTS + " WHERE " + COLUMN_HOST + " = ?;", new String[] {host});
|
||||
try {
|
||||
return cursor.moveToNext();
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the host-ip pair into this hosts.
|
||||
*/
|
||||
public boolean put(String host, String ip) {
|
||||
if (!isValidHost(host) || !isValidIp(ip)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(COLUMN_HOST, host);
|
||||
values.put(COLUMN_IP, ip);
|
||||
|
||||
if (contains(host)) {
|
||||
db.update(TABLE_HOSTS, values, COLUMN_HOST + " = ?", new String[] { host });
|
||||
} else {
|
||||
db.insert(TABLE_HOSTS, null, values);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts delete the entry with the host.
|
||||
*/
|
||||
public void delete(String host) {
|
||||
db.delete(TABLE_HOSTS, COLUMN_HOST + " = ?", new String[] { host });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all data from this host.
|
||||
*/
|
||||
public List<Pair<String, String>> getAll() {
|
||||
List<Pair<String, String>> result = new ArrayList<>();
|
||||
|
||||
Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_HOSTS + ";", null);
|
||||
try {
|
||||
while (cursor.moveToNext()) {
|
||||
String host = SqlUtils.getString(cursor, COLUMN_HOST, null);
|
||||
String ip = SqlUtils.getString(cursor, COLUMN_IP, null);
|
||||
if (ip == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
InetAddress inetAddress = toInetAddress(host, ip);
|
||||
if (inetAddress == null) {
|
||||
continue;
|
||||
byte[] bytes = parseV4(ip);
|
||||
if (bytes == null) {
|
||||
bytes = parseV6(ip);
|
||||
}
|
||||
|
||||
result.add(new Pair<>(host, ip));
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static InetAddress toInetAddress(String host, String ip) {
|
||||
if (!isValidHost(host)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ip == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] bytes = parseV4(ip);
|
||||
if (bytes == null) {
|
||||
bytes = parseV6(ip);
|
||||
}
|
||||
if (bytes == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return InetAddress.getByAddress(host, bytes);
|
||||
} catch (UnknownHostException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the host is valid.
|
||||
*/
|
||||
public static boolean isValidHost(String host) {
|
||||
if (host == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (host.length() > 253) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int labelLength = 0;
|
||||
for (int i = 0, n = host.length(); i < n; i++) {
|
||||
char ch = host.charAt(i);
|
||||
|
||||
if (ch == '.') {
|
||||
if (labelLength < 1 || labelLength > 63) {
|
||||
return false;
|
||||
if (bytes == null) {
|
||||
return null;
|
||||
}
|
||||
labelLength = 0;
|
||||
} else {
|
||||
labelLength++;
|
||||
}
|
||||
|
||||
if ((ch < 'a' || ch > 'z') && (ch < '0' || ch > '9') && ch != '-' && ch != '.') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (labelLength < 1 || labelLength > 63) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the ip is valid.
|
||||
*/
|
||||
public static boolean isValidIp(String ip) {
|
||||
return ip != null && (parseV4(ip) != null || parseV6(ip) != null);
|
||||
}
|
||||
|
||||
// org.xbill.DNS.Address.parseV4
|
||||
@Nullable
|
||||
private static byte[] parseV4(String s) {
|
||||
int numDigits;
|
||||
int currentOctet;
|
||||
byte [] values = new byte[4];
|
||||
int currentValue;
|
||||
int length = s.length();
|
||||
|
||||
currentOctet = 0;
|
||||
currentValue = 0;
|
||||
numDigits = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
char c = s.charAt(i);
|
||||
if (c >= '0' && c <= '9') {
|
||||
/* Can't have more than 3 digits per octet. */
|
||||
if (numDigits == 3)
|
||||
return null;
|
||||
/* Octets shouldn't start with 0, unless they are 0. */
|
||||
if (numDigits > 0 && currentValue == 0)
|
||||
return null;
|
||||
numDigits++;
|
||||
currentValue *= 10;
|
||||
currentValue += (c - '0');
|
||||
/* 255 is the maximum value for an octet. */
|
||||
if (currentValue > 255)
|
||||
return null;
|
||||
} else if (c == '.') {
|
||||
/* Can't have more than 3 dots. */
|
||||
if (currentOctet == 3)
|
||||
return null;
|
||||
/* Two consecutive dots are bad. */
|
||||
if (numDigits == 0)
|
||||
return null;
|
||||
values[currentOctet++] = (byte) currentValue;
|
||||
currentValue = 0;
|
||||
numDigits = 0;
|
||||
} else
|
||||
return null;
|
||||
}
|
||||
/* Must have 4 octets. */
|
||||
if (currentOctet != 3)
|
||||
return null;
|
||||
/* The fourth octet can't be empty. */
|
||||
if (numDigits == 0)
|
||||
return null;
|
||||
values[currentOctet] = (byte) currentValue;
|
||||
return values;
|
||||
}
|
||||
|
||||
// org.xbill.DNS.Address.parseV6
|
||||
@Nullable
|
||||
private static byte[] parseV6(String s) {
|
||||
int range = -1;
|
||||
byte [] data = new byte[16];
|
||||
|
||||
String [] tokens = s.split(":", -1);
|
||||
|
||||
int first = 0;
|
||||
int last = tokens.length - 1;
|
||||
|
||||
if (tokens[0].length() == 0) {
|
||||
// If the first two tokens are empty, it means the string
|
||||
// started with ::, which is fine. If only the first is
|
||||
// empty, the string started with :, which is bad.
|
||||
if (last - first > 0 && tokens[1].length() == 0)
|
||||
first++;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
if (tokens[last].length() == 0) {
|
||||
// If the last two tokens are empty, it means the string
|
||||
// ended with ::, which is fine. If only the last is
|
||||
// empty, the string ended with :, which is bad.
|
||||
if (last - first > 0 && tokens[last - 1].length() == 0)
|
||||
last--;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
if (last - first + 1 > 8)
|
||||
return null;
|
||||
|
||||
int i, j;
|
||||
for (i = first, j = 0; i <= last; i++) {
|
||||
if (tokens[i].length() == 0) {
|
||||
if (range >= 0)
|
||||
return null;
|
||||
range = j;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tokens[i].indexOf('.') >= 0) {
|
||||
// An IPv4 address must be the last component
|
||||
if (i < last)
|
||||
return null;
|
||||
// There can't have been more than 6 components.
|
||||
if (i > 6)
|
||||
return null;
|
||||
byte [] v4addr = parseV4(tokens[i]);
|
||||
if (v4addr == null)
|
||||
return null;
|
||||
for (int k = 0; k < 4; k++)
|
||||
data[j++] = v4addr[k];
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
for (int k = 0; k < tokens[i].length(); k++) {
|
||||
char c = tokens[i].charAt(k);
|
||||
if (Character.digit(c, 16) < 0)
|
||||
try {
|
||||
return InetAddress.getByAddress(host, bytes);
|
||||
} catch (UnknownHostException e) {
|
||||
return null;
|
||||
}
|
||||
int x = Integer.parseInt(tokens[i], 16);
|
||||
if (x > 0xFFFF || x < 0)
|
||||
return null;
|
||||
data[j++] = (byte)(x >>> 8);
|
||||
data[j++] = (byte)(x & 0xFF);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (j < 16 && range < 0)
|
||||
return null;
|
||||
/**
|
||||
* Returns true if the host is valid.
|
||||
*/
|
||||
public static boolean isValidHost(String host) {
|
||||
if (host == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (range >= 0) {
|
||||
int empty = 16 - j;
|
||||
System.arraycopy(data, range, data, range + empty, j - range);
|
||||
for (i = range; i < range + empty; i++)
|
||||
data[i] = 0;
|
||||
if (host.length() > 253) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int labelLength = 0;
|
||||
for (int i = 0, n = host.length(); i < n; i++) {
|
||||
char ch = host.charAt(i);
|
||||
|
||||
if (ch == '.') {
|
||||
if (labelLength < 1 || labelLength > 63) {
|
||||
return false;
|
||||
}
|
||||
labelLength = 0;
|
||||
} else {
|
||||
labelLength++;
|
||||
}
|
||||
|
||||
if ((ch < 'a' || ch > 'z') && (ch < '0' || ch > '9') && ch != '-' && ch != '.') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return labelLength >= 1 && labelLength <= 63;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
/**
|
||||
* Returns true if the ip is valid.
|
||||
*/
|
||||
public static boolean isValidIp(String ip) {
|
||||
return ip != null && (parseV4(ip) != null || parseV6(ip) != null);
|
||||
}
|
||||
|
||||
// org.xbill.DNS.Address.parseV4
|
||||
@Nullable
|
||||
private static byte[] parseV4(String s) {
|
||||
int numDigits;
|
||||
int currentOctet;
|
||||
byte[] values = new byte[4];
|
||||
int currentValue;
|
||||
int length = s.length();
|
||||
|
||||
currentOctet = 0;
|
||||
currentValue = 0;
|
||||
numDigits = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
char c = s.charAt(i);
|
||||
if (c >= '0' && c <= '9') {
|
||||
/* Can't have more than 3 digits per octet. */
|
||||
if (numDigits == 3)
|
||||
return null;
|
||||
/* Octets shouldn't start with 0, unless they are 0. */
|
||||
if (numDigits > 0 && currentValue == 0)
|
||||
return null;
|
||||
numDigits++;
|
||||
currentValue *= 10;
|
||||
currentValue += (c - '0');
|
||||
/* 255 is the maximum value for an octet. */
|
||||
if (currentValue > 255)
|
||||
return null;
|
||||
} else if (c == '.') {
|
||||
/* Can't have more than 3 dots. */
|
||||
if (currentOctet == 3)
|
||||
return null;
|
||||
/* Two consecutive dots are bad. */
|
||||
if (numDigits == 0)
|
||||
return null;
|
||||
values[currentOctet++] = (byte) currentValue;
|
||||
currentValue = 0;
|
||||
numDigits = 0;
|
||||
} else
|
||||
return null;
|
||||
}
|
||||
/* Must have 4 octets. */
|
||||
if (currentOctet != 3)
|
||||
return null;
|
||||
/* The fourth octet can't be empty. */
|
||||
if (numDigits == 0)
|
||||
return null;
|
||||
values[currentOctet] = (byte) currentValue;
|
||||
return values;
|
||||
}
|
||||
|
||||
// org.xbill.DNS.Address.parseV6
|
||||
@Nullable
|
||||
private static byte[] parseV6(String s) {
|
||||
int range = -1;
|
||||
byte[] data = new byte[16];
|
||||
|
||||
String[] tokens = s.split(":", -1);
|
||||
|
||||
int first = 0;
|
||||
int last = tokens.length - 1;
|
||||
|
||||
if (tokens[0].length() == 0) {
|
||||
// If the first two tokens are empty, it means the string
|
||||
// started with ::, which is fine. If only the first is
|
||||
// empty, the string started with :, which is bad.
|
||||
if (last - first > 0 && tokens[1].length() == 0)
|
||||
first++;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
if (tokens[last].length() == 0) {
|
||||
// If the last two tokens are empty, it means the string
|
||||
// ended with ::, which is fine. If only the last is
|
||||
// empty, the string ended with :, which is bad.
|
||||
if (last - first > 0 && tokens[last - 1].length() == 0)
|
||||
last--;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
if (last - first + 1 > 8)
|
||||
return null;
|
||||
|
||||
int i, j;
|
||||
for (i = first, j = 0; i <= last; i++) {
|
||||
if (tokens[i].length() == 0) {
|
||||
if (range >= 0)
|
||||
return null;
|
||||
range = j;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tokens[i].indexOf('.') >= 0) {
|
||||
// An IPv4 address must be the last component
|
||||
if (i < last)
|
||||
return null;
|
||||
// There can't have been more than 6 components.
|
||||
if (i > 6)
|
||||
return null;
|
||||
byte[] v4addr = parseV4(tokens[i]);
|
||||
if (v4addr == null)
|
||||
return null;
|
||||
for (int k = 0; k < 4; k++)
|
||||
data[j++] = v4addr[k];
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
for (int k = 0; k < tokens[i].length(); k++) {
|
||||
char c = tokens[i].charAt(k);
|
||||
if (Character.digit(c, 16) < 0)
|
||||
return null;
|
||||
}
|
||||
int x = Integer.parseInt(tokens[i], 16);
|
||||
if (x > 0xFFFF || x < 0)
|
||||
return null;
|
||||
data[j++] = (byte) (x >>> 8);
|
||||
data[j++] = (byte) (x & 0xFF);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (j < 16 && range < 0)
|
||||
return null;
|
||||
|
||||
if (range >= 0) {
|
||||
int empty = 16 - j;
|
||||
System.arraycopy(data, range, data, range + empty, j - range);
|
||||
for (i = range; i < range + empty; i++)
|
||||
data[i] = 0;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a InetAddress with the host.
|
||||
*/
|
||||
@Nullable
|
||||
public InetAddress get(String host) {
|
||||
if (!isValidHost(host)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_HOSTS + " WHERE " + COLUMN_HOST + " = ?;", new String[]{host});
|
||||
try {
|
||||
if (cursor.moveToNext()) {
|
||||
String ip = SqlUtils.getString(cursor, COLUMN_IP, null);
|
||||
return toInetAddress(host, ip);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean contains(String host) {
|
||||
Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_HOSTS + " WHERE " + COLUMN_HOST + " = ?;", new String[]{host});
|
||||
try {
|
||||
return cursor.moveToNext();
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the host-ip pair into this hosts.
|
||||
*/
|
||||
public boolean put(String host, String ip) {
|
||||
if (!isValidHost(host) || !isValidIp(ip)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(COLUMN_HOST, host);
|
||||
values.put(COLUMN_IP, ip);
|
||||
|
||||
if (contains(host)) {
|
||||
db.update(TABLE_HOSTS, values, COLUMN_HOST + " = ?", new String[]{host});
|
||||
} else {
|
||||
db.insert(TABLE_HOSTS, null, values);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts delete the entry with the host.
|
||||
*/
|
||||
public void delete(String host) {
|
||||
db.delete(TABLE_HOSTS, COLUMN_HOST + " = ?", new String[]{host});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all data from this host.
|
||||
*/
|
||||
public List<Pair<String, String>> getAll() {
|
||||
List<Pair<String, String>> result = new ArrayList<>();
|
||||
|
||||
Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_HOSTS + ";", null);
|
||||
try {
|
||||
while (cursor.moveToNext()) {
|
||||
String host = SqlUtils.getString(cursor, COLUMN_HOST, null);
|
||||
String ip = SqlUtils.getString(cursor, COLUMN_IP, null);
|
||||
|
||||
InetAddress inetAddress = toInetAddress(host, ip);
|
||||
if (inetAddress == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result.add(new Pair<>(host, ip));
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -18,9 +18,11 @@ package com.hippo.ehviewer;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.conaco.ValueHelper;
|
||||
import com.hippo.image.ImageBitmap;
|
||||
import com.hippo.streampipe.InputStreamPipe;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ImageBitmapHelper implements ValueHelper<ImageBitmap> {
|
||||
|
||||
@@ -21,9 +21,11 @@ import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.DimenRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.ehviewer.client.EhConfig;
|
||||
import com.hippo.ehviewer.client.EhUtils;
|
||||
import com.hippo.ehviewer.client.data.FavListUrlBuilder;
|
||||
@@ -36,13 +38,222 @@ import com.hippo.yorozuya.AssertUtils;
|
||||
import com.hippo.yorozuya.FileUtils;
|
||||
import com.hippo.yorozuya.MathUtils;
|
||||
import com.hippo.yorozuya.NumberUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Locale;
|
||||
|
||||
public class Settings {
|
||||
|
||||
private static final String TAG = Settings.class.getSimpleName();
|
||||
/********************
|
||||
****** Eh
|
||||
********************/
|
||||
|
||||
public static final String KEY_THEME = "theme";
|
||||
public static final int THEME_LIGHT = 0;
|
||||
public static final int THEME_DARK = 1;
|
||||
public static final int THEME_BLACK = 2;
|
||||
public static final String KEY_APPLY_NAV_BAR_THEME_COLOR = "apply_nav_bar_theme_color";
|
||||
public static final String KEY_GALLERY_SITE = "gallery_site";
|
||||
public static final String KEY_LIST_MODE = "list_mode";
|
||||
public static final String KEY_DETAIL_SIZE = "detail_size";
|
||||
public static final String KEY_THUMB_SIZE = "thumb_size";
|
||||
public static final String KEY_THUMB_RESOLUTION = "thumb_resolution";
|
||||
public static final String KEY_SHOW_TAG_TRANSLATIONS = "show_tag_translations";
|
||||
public static final String KEY_DEFAULT_CATEGORIES = "default_categories";
|
||||
public static final int DEFAULT_DEFAULT_CATEGORIES = EhUtils.ALL_CATEGORY;
|
||||
public static final String KEY_EXCLUDED_TAG_NAMESPACES = "excluded_tag_namespaces";
|
||||
public static final String KEY_EXCLUDED_LANGUAGES = "excluded_languages";
|
||||
/********************
|
||||
****** Privacy and Security
|
||||
********************/
|
||||
public static final String KEY_SEC_SECURITY = "enable_secure";
|
||||
public static final boolean VALUE_SEC_SECURITY = false;
|
||||
/********************
|
||||
****** Download
|
||||
********************/
|
||||
public static final String KEY_DOWNLOAD_SAVE_SCHEME = "image_scheme";
|
||||
public static final String KEY_DOWNLOAD_SAVE_AUTHORITY = "image_authority";
|
||||
public static final String KEY_DOWNLOAD_SAVE_PATH = "image_path";
|
||||
public static final String KEY_DOWNLOAD_SAVE_QUERY = "image_query";
|
||||
public static final String KEY_DOWNLOAD_SAVE_FRAGMENT = "image_fragment";
|
||||
public static final String KEY_MEDIA_SCAN = "media_scan";
|
||||
public static final String KEY_IMAGE_RESOLUTION = "image_size";
|
||||
public static final String DEFAULT_IMAGE_RESOLUTION = EhConfig.IMAGE_SIZE_AUTO;
|
||||
public static final int INVALID_DEFAULT_FAV_SLOT = -2;
|
||||
public static final String KEY_ENABLE_ANALYTICS = "enable_analytics";
|
||||
/********************
|
||||
****** Advanced
|
||||
********************/
|
||||
public static final String KEY_SAVE_PARSE_ERROR_BODY = "save_parse_error_body";
|
||||
public static final String KEY_SECURITY = "security";
|
||||
public static final String DEFAULT_SECURITY = "";
|
||||
public static final String KEY_ENABLE_FINGERPRINT = "enable_fingerprint";
|
||||
public static final String KEY_READ_CACHE_SIZE = "read_cache_size";
|
||||
public static final int DEFAULT_READ_CACHE_SIZE = 160;
|
||||
public static final String KEY_BUILT_IN_HOSTS = "built_in_hosts";
|
||||
public static final String KEY_APP_LANGUAGE = "app_language";
|
||||
private static final String TAG = Settings.class.getSimpleName();
|
||||
private static final String KEY_VERSION_CODE = "version_code";
|
||||
private static final int DEFAULT_VERSION_CODE = 0;
|
||||
private static final String KEY_DISPLAY_NAME = "display_name";
|
||||
private static final String DEFAULT_DISPLAY_NAME = null;
|
||||
private static final String KEY_AVATAR = "avatar";
|
||||
private static final String DEFAULT_AVATAR = null;
|
||||
private static final String KEY_SHOW_WARNING = "show_warning";
|
||||
private static final boolean DEFAULT_SHOW_WARNING = true;
|
||||
private static final String KEY_REMOVE_IMAGE_FILES = "include_pic";
|
||||
private static final boolean DEFAULT_REMOVE_IMAGE_FILES = true;
|
||||
private static final String KEY_NEED_SIGN_IN = "need_sign_in";
|
||||
private static final boolean DEFAULT_NEED_SIGN_IN = true;
|
||||
private static final String KEY_SELECT_SITE = "select_site";
|
||||
private static final boolean DEFAULT_SELECT_SITE = true;
|
||||
private static final String KEY_QUICK_SEARCH_TIP = "quick_search_tip";
|
||||
private static final boolean DEFAULT_QUICK_SEARCH_TIP = true;
|
||||
private static final int DEFAULT_THEME = THEME_LIGHT;
|
||||
private static final boolean DEFAULT_APPLY_NAV_BAR_THEME_COLOR = true;
|
||||
private static final int DEFAULT_GALLERY_SITE = 1;
|
||||
private static final String KEY_LAUNCH_PAGE = "launch_page";
|
||||
private static final int DEFAULT_LAUNCH_PAGE = 0;
|
||||
private static final int DEFAULT_LIST_MODE = 0;
|
||||
private static final int DEFAULT_DETAIL_SIZE = 0;
|
||||
private static final int DEFAULT_THUMB_SIZE = 1;
|
||||
private static final int DEFAULT_THUMB_RESOLUTION = 0;
|
||||
private static final String KEY_FIX_THUMB_URL = "fix_thumb_url";
|
||||
private static final boolean DEFAULT_FIX_THUMB_URL = false;
|
||||
private static final String KEY_SHOW_JPN_TITLE = "show_jpn_title";
|
||||
private static final boolean DEFAULT_SHOW_JPN_TITLE = false;
|
||||
private static final String KEY_SHOW_GALLERY_PAGES = "show_gallery_pages";
|
||||
private static final boolean DEFAULT_SHOW_GALLERY_PAGES = false;
|
||||
private static final boolean DEFAULT_SHOW_TAG_TRANSLATIONS = false;
|
||||
private static final int DEFAULT_EXCLUDED_TAG_NAMESPACES = 0;
|
||||
private static final String DEFAULT_EXCLUDED_LANGUAGES = null;
|
||||
private static final String KEY_CELLULAR_NETWORK_WARNING = "cellular_network_warning";
|
||||
private static final boolean DEFAULT_CELLULAR_NETWORK_WARNING = false;
|
||||
/********************
|
||||
****** Read
|
||||
********************/
|
||||
private static final String KEY_SCREEN_ROTATION = "screen_rotation";
|
||||
private static final int DEFAULT_SCREEN_ROTATION = 0;
|
||||
private static final String KEY_READING_DIRECTION = "reading_direction";
|
||||
private static final int DEFAULT_READING_DIRECTION = GalleryView.LAYOUT_RIGHT_TO_LEFT;
|
||||
private static final String KEY_PAGE_SCALING = "page_scaling";
|
||||
private static final int DEFAULT_PAGE_SCALING = GalleryView.SCALE_FIT;
|
||||
private static final String KEY_START_POSITION = "start_position";
|
||||
private static final int DEFAULT_START_POSITION = GalleryView.START_POSITION_TOP_RIGHT;
|
||||
private static final String KEY_KEEP_SCREEN_ON = "keep_screen_on";
|
||||
private static final boolean DEFAULT_KEEP_SCREEN_ON = false;
|
||||
private static final String KEY_SHOW_CLOCK = "gallery_show_clock";
|
||||
private static final boolean DEFAULT_SHOW_CLOCK = true;
|
||||
private static final String KEY_SHOW_PROGRESS = "gallery_show_progress";
|
||||
private static final boolean DEFAULT_SHOW_PROGRESS = true;
|
||||
private static final String KEY_SHOW_BATTERY = "gallery_show_battery";
|
||||
private static final boolean DEFAULT_SHOW_BATTERY = true;
|
||||
private static final String KEY_SHOW_PAGE_INTERVAL = "gallery_show_page_interval";
|
||||
private static final boolean DEFAULT_SHOW_PAGE_INTERVAL = true;
|
||||
private static final String KEY_VOLUME_PAGE = "volume_page";
|
||||
private static final boolean DEFAULT_VOLUME_PAGE = false;
|
||||
private static final String KEY_READING_FULLSCREEN = "reading_fullscreen";
|
||||
private static final boolean VALUE_READING_FULLSCREEN = true;
|
||||
private static final String KEY_CUSTOM_SCREEN_LIGHTNESS = "custom_screen_lightness";
|
||||
private static final boolean DEFAULT_CUSTOM_SCREEN_LIGHTNESS = false;
|
||||
private static final String KEY_SCREEN_LIGHTNESS = "screen_lightness";
|
||||
private static final int DEFAULT_SCREEN_LIGHTNESS = 50;
|
||||
private static final boolean DEFAULT_MEDIA_SCAN = false;
|
||||
private static final String KEY_RECENT_DOWNLOAD_LABEL = "recent_download_label";
|
||||
private static final String DEFAULT_RECENT_DOWNLOAD_LABEL = null;
|
||||
private static final String KEY_HAS_DEFAULT_DOWNLOAD_LABEL = "has_default_download_label";
|
||||
private static final boolean DEFAULT_HAS_DOWNLOAD_LABEL = false;
|
||||
private static final String KEY_DEFAULT_DOWNLOAD_LABEL = "default_download_label";
|
||||
private static final String DEFAULT_DOWNLOAD_LABEL = null;
|
||||
private static final String KEY_MULTI_THREAD_DOWNLOAD = "download_thread";
|
||||
private static final int DEFAULT_MULTI_THREAD_DOWNLOAD = 3;
|
||||
private static final String KEY_PRELOAD_IMAGE = "preload_image";
|
||||
private static final int DEFAULT_PRELOAD_IMAGE = 5;
|
||||
private static final String KEY_DOWNLOAD_ORIGIN_IMAGE = "download_origin_image";
|
||||
private static final boolean DEFAULT_DOWNLOAD_ORIGIN_IMAGE = false;
|
||||
/********************
|
||||
****** Favorites
|
||||
********************/
|
||||
private static final String KEY_FAV_CAT_0 = "fav_cat_0";
|
||||
private static final String KEY_FAV_CAT_1 = "fav_cat_1";
|
||||
private static final String KEY_FAV_CAT_2 = "fav_cat_2";
|
||||
private static final String KEY_FAV_CAT_3 = "fav_cat_3";
|
||||
private static final String KEY_FAV_CAT_4 = "fav_cat_4";
|
||||
private static final String KEY_FAV_CAT_5 = "fav_cat_5";
|
||||
private static final String KEY_FAV_CAT_6 = "fav_cat_6";
|
||||
private static final String KEY_FAV_CAT_7 = "fav_cat_7";
|
||||
private static final String KEY_FAV_CAT_8 = "fav_cat_8";
|
||||
private static final String KEY_FAV_CAT_9 = "fav_cat_9";
|
||||
private static final String DEFAULT_FAV_CAT_0 = "Favorites 0";
|
||||
private static final String DEFAULT_FAV_CAT_1 = "Favorites 1";
|
||||
private static final String DEFAULT_FAV_CAT_2 = "Favorites 2";
|
||||
private static final String DEFAULT_FAV_CAT_3 = "Favorites 3";
|
||||
private static final String DEFAULT_FAV_CAT_4 = "Favorites 4";
|
||||
private static final String DEFAULT_FAV_CAT_5 = "Favorites 5";
|
||||
private static final String DEFAULT_FAV_CAT_6 = "Favorites 6";
|
||||
private static final String DEFAULT_FAV_CAT_7 = "Favorites 7";
|
||||
private static final String DEFAULT_FAV_CAT_8 = "Favorites 8";
|
||||
private static final String DEFAULT_FAV_CAT_9 = "Favorites 9";
|
||||
private static final String KEY_FAV_COUNT_0 = "fav_count_0";
|
||||
private static final String KEY_FAV_COUNT_1 = "fav_count_1";
|
||||
private static final String KEY_FAV_COUNT_2 = "fav_count_2";
|
||||
private static final String KEY_FAV_COUNT_3 = "fav_count_3";
|
||||
private static final String KEY_FAV_COUNT_4 = "fav_count_4";
|
||||
private static final String KEY_FAV_COUNT_5 = "fav_count_5";
|
||||
private static final String KEY_FAV_COUNT_6 = "fav_count_6";
|
||||
private static final String KEY_FAV_COUNT_7 = "fav_count_7";
|
||||
private static final String KEY_FAV_COUNT_8 = "fav_count_8";
|
||||
private static final String KEY_FAV_COUNT_9 = "fav_count_9";
|
||||
private static final String KEY_FAV_LOCAL = "fav_local";
|
||||
private static final String KEY_FAV_CLOUD = "fav_cloud";
|
||||
private static final int DEFAULT_FAV_COUNT = 0;
|
||||
private static final String KEY_RECENT_FAV_CAT = "recent_fav_cat";
|
||||
private static final int DEFAULT_RECENT_FAV_CAT = FavListUrlBuilder.FAV_CAT_ALL;
|
||||
// -1 for local, 0 - 9 for cloud favorite, other for no default fav slot
|
||||
private static final String KEY_DEFAULT_FAV_SLOT = "default_favorite_2";
|
||||
private static final int DEFAULT_DEFAULT_FAV_SLOT = INVALID_DEFAULT_FAV_SLOT;
|
||||
/********************
|
||||
****** Analytics
|
||||
********************/
|
||||
private static final String KEY_ASK_ANALYTICS = "ask_analytics";
|
||||
private static final boolean DEFAULT_ASK_ANALYTICS = true;
|
||||
private static final boolean DEFAULT_ENABLE_ANALYTICS = false;
|
||||
private static final String KEY_USER_ID = "user_id";
|
||||
private static final String FILENAME_USER_ID = ".user_id";
|
||||
private static final int LENGTH_USER_ID = 32;
|
||||
/********************
|
||||
****** Update
|
||||
********************/
|
||||
private static final String KEY_BETA_UPDATE_CHANNEL = "beta_update_channel";
|
||||
private static final boolean DEFAULT_BETA_UPDATE_CHANNEL = EhApplication.BETA;
|
||||
private static final String KEY_SKIP_UPDATE_VERSION = "skip_update_version";
|
||||
private static final int DEFAULT_SKIP_UPDATE_VERSION = 0;
|
||||
private static final boolean DEFAULT_SAVE_PARSE_ERROR_BODY = EhApplication.BETA;
|
||||
private static final String KEY_SAVE_CRASH_LOG = "save_crash_log";
|
||||
private static final boolean DEFAULT_SAVE_CRASH_LOG = false;
|
||||
private static final boolean DEFAULT_BUILT_IN_HOSTS = false;
|
||||
private static final String DEFAULT_APP_LANGUAGE = "system";
|
||||
private static final String KEY_PROXY_TYPE = "proxy_type";
|
||||
private static final int DEFAULT_PROXY_TYPE = EhProxySelector.TYPE_SYSTEM;
|
||||
private static final String KEY_PROXY_IP = "proxy_ip";
|
||||
private static final String DEFAULT_PROXY_IP = null;
|
||||
private static final String KEY_PROXY_PORT = "proxy_port";
|
||||
private static final int DEFAULT_PROXY_PORT = -1;
|
||||
/********************
|
||||
****** Guide
|
||||
********************/
|
||||
private static final String KEY_GUIDE_QUICK_SEARCH = "guide_quick_search";
|
||||
private static final boolean DEFAULT_GUIDE_QUICK_SEARCH = true;
|
||||
private static final String KEY_GUIDE_COLLECTIONS = "guide_collections";
|
||||
private static final boolean DEFAULT_GUIDE_COLLECTIONS = true;
|
||||
private static final String KEY_GUIDE_DOWNLOAD_THUMB = "guide_download_thumb";
|
||||
private static final boolean DEFAULT_GUIDE_DOWNLOAD_THUMB = true;
|
||||
private static final String KEY_GUIDE_DOWNLOAD_LABELS = "guide_download_labels";
|
||||
private static final boolean DEFAULT_GUIDE_DOWNLOAD_LABELS = true;
|
||||
private static final String KEY_GUIDE_GALLERY = "guide_gallery";
|
||||
private static final boolean DEFAULT_GUIDE_GALLERY = true;
|
||||
private static final String KEY_CLIPBOARD_TEXT_HASH_CODE = "clipboard_text_hash_code";
|
||||
private static final int DEFAULT_CLIPBOARD_TEXT_HASH_CODE = 0;
|
||||
private static Context sContext;
|
||||
private static SharedPreferences sSettingsPre;
|
||||
private static EhConfig sEhConfig;
|
||||
@@ -64,7 +275,7 @@ public class Settings {
|
||||
}
|
||||
|
||||
private static EhConfig loadEhConfig() {
|
||||
EhConfig ehConfig= new EhConfig();
|
||||
EhConfig ehConfig = new EhConfig();
|
||||
ehConfig.imageSize = getImageResolution();
|
||||
ehConfig.excludedLanguages = getExcludedLanguages();
|
||||
ehConfig.defaultCategories = getDefaultCategories();
|
||||
@@ -151,9 +362,6 @@ public class Settings {
|
||||
sSettingsPre.edit().putString(key, Integer.toString(value)).apply();
|
||||
}
|
||||
|
||||
private static final String KEY_VERSION_CODE = "version_code";
|
||||
private static final int DEFAULT_VERSION_CODE = 0;
|
||||
|
||||
public static int getVersionCode() {
|
||||
return getInt(KEY_VERSION_CODE, DEFAULT_VERSION_CODE);
|
||||
}
|
||||
@@ -162,9 +370,6 @@ public class Settings {
|
||||
putInt(KEY_VERSION_CODE, value);
|
||||
}
|
||||
|
||||
private static final String KEY_DISPLAY_NAME = "display_name";
|
||||
private static final String DEFAULT_DISPLAY_NAME = null;
|
||||
|
||||
public static String getDisplayName() {
|
||||
return getString(KEY_DISPLAY_NAME, DEFAULT_DISPLAY_NAME);
|
||||
}
|
||||
@@ -173,9 +378,6 @@ public class Settings {
|
||||
putString(KEY_DISPLAY_NAME, value);
|
||||
}
|
||||
|
||||
private static final String KEY_AVATAR = "avatar";
|
||||
private static final String DEFAULT_AVATAR = null;
|
||||
|
||||
public static String getAvatar() {
|
||||
return getString(KEY_AVATAR, DEFAULT_AVATAR);
|
||||
}
|
||||
@@ -184,9 +386,6 @@ public class Settings {
|
||||
putString(KEY_AVATAR, value);
|
||||
}
|
||||
|
||||
private static final String KEY_SHOW_WARNING = "show_warning";
|
||||
private static final boolean DEFAULT_SHOW_WARNING = true;
|
||||
|
||||
public static boolean getShowWarning() {
|
||||
return getBoolean(KEY_SHOW_WARNING, DEFAULT_SHOW_WARNING);
|
||||
}
|
||||
@@ -195,9 +394,6 @@ public class Settings {
|
||||
putBoolean(KEY_SHOW_WARNING, value);
|
||||
}
|
||||
|
||||
private static final String KEY_REMOVE_IMAGE_FILES = "include_pic";
|
||||
private static final boolean DEFAULT_REMOVE_IMAGE_FILES = true;
|
||||
|
||||
public static boolean getRemoveImageFiles() {
|
||||
return getBoolean(KEY_REMOVE_IMAGE_FILES, DEFAULT_REMOVE_IMAGE_FILES);
|
||||
}
|
||||
@@ -210,9 +406,6 @@ public class Settings {
|
||||
return sEhConfig;
|
||||
}
|
||||
|
||||
private static final String KEY_NEED_SIGN_IN = "need_sign_in";
|
||||
private static final boolean DEFAULT_NEED_SIGN_IN = true;
|
||||
|
||||
public static boolean getNeedSignIn() {
|
||||
return getBoolean(KEY_NEED_SIGN_IN, DEFAULT_NEED_SIGN_IN);
|
||||
}
|
||||
@@ -221,9 +414,6 @@ public class Settings {
|
||||
putBoolean(KEY_NEED_SIGN_IN, value);
|
||||
}
|
||||
|
||||
private static final String KEY_SELECT_SITE = "select_site";
|
||||
private static final boolean DEFAULT_SELECT_SITE = true;
|
||||
|
||||
public static boolean getSelectSite() {
|
||||
return getBoolean(KEY_SELECT_SITE, DEFAULT_SELECT_SITE);
|
||||
}
|
||||
@@ -232,9 +422,6 @@ public class Settings {
|
||||
putBoolean(KEY_SELECT_SITE, value);
|
||||
}
|
||||
|
||||
private static final String KEY_QUICK_SEARCH_TIP = "quick_search_tip";
|
||||
private static final boolean DEFAULT_QUICK_SEARCH_TIP = true;
|
||||
|
||||
public static boolean getQuickSearchTip() {
|
||||
return getBoolean(KEY_QUICK_SEARCH_TIP, DEFAULT_QUICK_SEARCH_TIP);
|
||||
}
|
||||
@@ -243,16 +430,6 @@ public class Settings {
|
||||
putBoolean(KEY_QUICK_SEARCH_TIP, value);
|
||||
}
|
||||
|
||||
/********************
|
||||
****** Eh
|
||||
********************/
|
||||
|
||||
public static final String KEY_THEME = "theme";
|
||||
public static final int THEME_LIGHT = 0;
|
||||
public static final int THEME_DARK = 1;
|
||||
public static final int THEME_BLACK = 2;
|
||||
private static final int DEFAULT_THEME = THEME_LIGHT;
|
||||
|
||||
public static int getTheme() {
|
||||
return getIntFromStr(KEY_THEME, DEFAULT_THEME);
|
||||
}
|
||||
@@ -261,16 +438,10 @@ public class Settings {
|
||||
putIntToStr(KEY_THEME, theme);
|
||||
}
|
||||
|
||||
public static final String KEY_APPLY_NAV_BAR_THEME_COLOR = "apply_nav_bar_theme_color";
|
||||
private static final boolean DEFAULT_APPLY_NAV_BAR_THEME_COLOR = true;
|
||||
|
||||
public static boolean getApplyNavBarThemeColor() {
|
||||
return getBoolean(KEY_APPLY_NAV_BAR_THEME_COLOR, DEFAULT_APPLY_NAV_BAR_THEME_COLOR);
|
||||
}
|
||||
|
||||
public static final String KEY_GALLERY_SITE = "gallery_site";
|
||||
private static final int DEFAULT_GALLERY_SITE = 1;
|
||||
|
||||
public static int getGallerySite() {
|
||||
return getIntFromStr(KEY_GALLERY_SITE, DEFAULT_GALLERY_SITE);
|
||||
}
|
||||
@@ -279,9 +450,6 @@ public class Settings {
|
||||
putIntToStr(KEY_GALLERY_SITE, value);
|
||||
}
|
||||
|
||||
private static final String KEY_LAUNCH_PAGE = "launch_page";
|
||||
private static final int DEFAULT_LAUNCH_PAGE = 0;
|
||||
|
||||
public static String getLaunchPageGalleryListSceneAction() {
|
||||
int value = getIntFromStr(KEY_LAUNCH_PAGE, DEFAULT_LAUNCH_PAGE);
|
||||
switch (value) {
|
||||
@@ -295,16 +463,10 @@ public class Settings {
|
||||
}
|
||||
}
|
||||
|
||||
public static final String KEY_LIST_MODE = "list_mode";
|
||||
private static final int DEFAULT_LIST_MODE = 0;
|
||||
|
||||
public static int getListMode() {
|
||||
return getIntFromStr(KEY_LIST_MODE, DEFAULT_LIST_MODE);
|
||||
}
|
||||
|
||||
public static final String KEY_DETAIL_SIZE = "detail_size";
|
||||
private static final int DEFAULT_DETAIL_SIZE = 0;
|
||||
|
||||
public static int getDetailSize() {
|
||||
return getIntFromStr(KEY_DETAIL_SIZE, DEFAULT_DETAIL_SIZE);
|
||||
}
|
||||
@@ -320,9 +482,6 @@ public class Settings {
|
||||
}
|
||||
}
|
||||
|
||||
public static final String KEY_THUMB_SIZE = "thumb_size";
|
||||
private static final int DEFAULT_THUMB_SIZE = 1;
|
||||
|
||||
public static int getThumbSize() {
|
||||
return getIntFromStr(KEY_THUMB_SIZE, DEFAULT_THUMB_SIZE);
|
||||
}
|
||||
@@ -340,44 +499,26 @@ public class Settings {
|
||||
}
|
||||
}
|
||||
|
||||
public static final String KEY_THUMB_RESOLUTION = "thumb_resolution";
|
||||
private static final int DEFAULT_THUMB_RESOLUTION = 0;
|
||||
|
||||
public static int getThumbResolution() {
|
||||
return getIntFromStr(KEY_THUMB_RESOLUTION, DEFAULT_THUMB_RESOLUTION);
|
||||
}
|
||||
|
||||
private static final String KEY_FIX_THUMB_URL = "fix_thumb_url";
|
||||
private static final boolean DEFAULT_FIX_THUMB_URL = false;
|
||||
|
||||
public static boolean getFixThumbUrl() {
|
||||
return getBoolean(KEY_FIX_THUMB_URL, DEFAULT_FIX_THUMB_URL);
|
||||
}
|
||||
|
||||
private static final String KEY_SHOW_JPN_TITLE = "show_jpn_title";
|
||||
private static final boolean DEFAULT_SHOW_JPN_TITLE = false;
|
||||
|
||||
public static boolean getShowJpnTitle() {
|
||||
return getBoolean(KEY_SHOW_JPN_TITLE, DEFAULT_SHOW_JPN_TITLE);
|
||||
}
|
||||
|
||||
private static final String KEY_SHOW_GALLERY_PAGES = "show_gallery_pages";
|
||||
private static final boolean DEFAULT_SHOW_GALLERY_PAGES = false;
|
||||
|
||||
public static boolean getShowGalleryPages() {
|
||||
return getBoolean(KEY_SHOW_GALLERY_PAGES, DEFAULT_SHOW_GALLERY_PAGES);
|
||||
}
|
||||
|
||||
public static final String KEY_SHOW_TAG_TRANSLATIONS = "show_tag_translations";
|
||||
private static final boolean DEFAULT_SHOW_TAG_TRANSLATIONS = false;
|
||||
|
||||
public static boolean getShowTagTranslations() {
|
||||
return getBoolean(KEY_SHOW_TAG_TRANSLATIONS, DEFAULT_SHOW_TAG_TRANSLATIONS);
|
||||
}
|
||||
|
||||
public static final String KEY_DEFAULT_CATEGORIES = "default_categories";
|
||||
public static final int DEFAULT_DEFAULT_CATEGORIES = EhUtils.ALL_CATEGORY;
|
||||
|
||||
public static int getDefaultCategories() {
|
||||
return getInt(KEY_DEFAULT_CATEGORIES, DEFAULT_DEFAULT_CATEGORIES);
|
||||
}
|
||||
@@ -388,9 +529,6 @@ public class Settings {
|
||||
putInt(KEY_DEFAULT_CATEGORIES, value);
|
||||
}
|
||||
|
||||
public static final String KEY_EXCLUDED_TAG_NAMESPACES = "excluded_tag_namespaces";
|
||||
private static final int DEFAULT_EXCLUDED_TAG_NAMESPACES = 0;
|
||||
|
||||
public static int getExcludedTagNamespaces() {
|
||||
return getInt(KEY_EXCLUDED_TAG_NAMESPACES, DEFAULT_EXCLUDED_TAG_NAMESPACES);
|
||||
}
|
||||
@@ -401,9 +539,6 @@ public class Settings {
|
||||
putInt(KEY_EXCLUDED_TAG_NAMESPACES, value);
|
||||
}
|
||||
|
||||
public static final String KEY_EXCLUDED_LANGUAGES = "excluded_languages";
|
||||
private static final String DEFAULT_EXCLUDED_LANGUAGES = null;
|
||||
|
||||
public static String getExcludedLanguages() {
|
||||
return getString(KEY_EXCLUDED_LANGUAGES, DEFAULT_EXCLUDED_LANGUAGES);
|
||||
}
|
||||
@@ -414,19 +549,10 @@ public class Settings {
|
||||
putString(KEY_EXCLUDED_LANGUAGES, value);
|
||||
}
|
||||
|
||||
private static final String KEY_CELLULAR_NETWORK_WARNING = "cellular_network_warning";
|
||||
private static final boolean DEFAULT_CELLULAR_NETWORK_WARNING = false;
|
||||
|
||||
public static boolean getCellularNetworkWarning() {
|
||||
return getBoolean(KEY_CELLULAR_NETWORK_WARNING, DEFAULT_CELLULAR_NETWORK_WARNING);
|
||||
}
|
||||
|
||||
/********************
|
||||
****** Read
|
||||
********************/
|
||||
private static final String KEY_SCREEN_ROTATION = "screen_rotation";
|
||||
private static final int DEFAULT_SCREEN_ROTATION = 0;
|
||||
|
||||
public static int getScreenRotation() {
|
||||
return getIntFromStr(KEY_SCREEN_ROTATION, DEFAULT_SCREEN_ROTATION);
|
||||
}
|
||||
@@ -435,9 +561,6 @@ public class Settings {
|
||||
putIntToStr(KEY_SCREEN_ROTATION, value);
|
||||
}
|
||||
|
||||
private static final String KEY_READING_DIRECTION = "reading_direction";
|
||||
private static final int DEFAULT_READING_DIRECTION = GalleryView.LAYOUT_RIGHT_TO_LEFT;
|
||||
|
||||
@GalleryView.LayoutMode
|
||||
public static int getReadingDirection() {
|
||||
return GalleryView.sanitizeLayoutMode(getIntFromStr(KEY_READING_DIRECTION, DEFAULT_READING_DIRECTION));
|
||||
@@ -447,9 +570,6 @@ public class Settings {
|
||||
putIntToStr(KEY_READING_DIRECTION, value);
|
||||
}
|
||||
|
||||
private static final String KEY_PAGE_SCALING = "page_scaling";
|
||||
private static final int DEFAULT_PAGE_SCALING = GalleryView.SCALE_FIT;
|
||||
|
||||
@GalleryView.ScaleMode
|
||||
public static int getPageScaling() {
|
||||
return GalleryView.sanitizeScaleMode(getIntFromStr(KEY_PAGE_SCALING, DEFAULT_PAGE_SCALING));
|
||||
@@ -459,9 +579,6 @@ public class Settings {
|
||||
putIntToStr(KEY_PAGE_SCALING, value);
|
||||
}
|
||||
|
||||
private static final String KEY_START_POSITION = "start_position";
|
||||
private static final int DEFAULT_START_POSITION = GalleryView.START_POSITION_TOP_RIGHT;
|
||||
|
||||
@GalleryView.StartPosition
|
||||
public static int getStartPosition() {
|
||||
return GalleryView.sanitizeStartPosition(getIntFromStr(KEY_START_POSITION, DEFAULT_START_POSITION));
|
||||
@@ -471,9 +588,6 @@ public class Settings {
|
||||
putIntToStr(KEY_START_POSITION, value);
|
||||
}
|
||||
|
||||
private static final String KEY_KEEP_SCREEN_ON = "keep_screen_on";
|
||||
private static final boolean DEFAULT_KEEP_SCREEN_ON = false;
|
||||
|
||||
public static boolean getKeepScreenOn() {
|
||||
return getBoolean(KEY_KEEP_SCREEN_ON, DEFAULT_KEEP_SCREEN_ON);
|
||||
}
|
||||
@@ -482,9 +596,6 @@ public class Settings {
|
||||
putBoolean(KEY_KEEP_SCREEN_ON, value);
|
||||
}
|
||||
|
||||
private static final String KEY_SHOW_CLOCK = "gallery_show_clock";
|
||||
private static final boolean DEFAULT_SHOW_CLOCK = true;
|
||||
|
||||
public static boolean getShowClock() {
|
||||
return getBoolean(KEY_SHOW_CLOCK, DEFAULT_SHOW_CLOCK);
|
||||
}
|
||||
@@ -493,9 +604,6 @@ public class Settings {
|
||||
putBoolean(KEY_SHOW_CLOCK, value);
|
||||
}
|
||||
|
||||
private static final String KEY_SHOW_PROGRESS = "gallery_show_progress";
|
||||
private static final boolean DEFAULT_SHOW_PROGRESS = true;
|
||||
|
||||
public static boolean getShowProgress() {
|
||||
return getBoolean(KEY_SHOW_PROGRESS, DEFAULT_SHOW_PROGRESS);
|
||||
}
|
||||
@@ -504,9 +612,6 @@ public class Settings {
|
||||
putBoolean(KEY_SHOW_PROGRESS, value);
|
||||
}
|
||||
|
||||
private static final String KEY_SHOW_BATTERY = "gallery_show_battery";
|
||||
private static final boolean DEFAULT_SHOW_BATTERY = true;
|
||||
|
||||
public static boolean getShowBattery() {
|
||||
return getBoolean(KEY_SHOW_BATTERY, DEFAULT_SHOW_BATTERY);
|
||||
}
|
||||
@@ -515,9 +620,6 @@ public class Settings {
|
||||
putBoolean(KEY_SHOW_BATTERY, value);
|
||||
}
|
||||
|
||||
private static final String KEY_SHOW_PAGE_INTERVAL = "gallery_show_page_interval";
|
||||
private static final boolean DEFAULT_SHOW_PAGE_INTERVAL = true;
|
||||
|
||||
public static boolean getShowPageInterval() {
|
||||
return getBoolean(KEY_SHOW_PAGE_INTERVAL, DEFAULT_SHOW_PAGE_INTERVAL);
|
||||
}
|
||||
@@ -526,9 +628,6 @@ public class Settings {
|
||||
putBoolean(KEY_SHOW_PAGE_INTERVAL, value);
|
||||
}
|
||||
|
||||
private static final String KEY_VOLUME_PAGE = "volume_page";
|
||||
private static final boolean DEFAULT_VOLUME_PAGE = false;
|
||||
|
||||
public static boolean getVolumePage() {
|
||||
return getBoolean(KEY_VOLUME_PAGE, DEFAULT_VOLUME_PAGE);
|
||||
}
|
||||
@@ -537,9 +636,6 @@ public class Settings {
|
||||
putBoolean(KEY_VOLUME_PAGE, value);
|
||||
}
|
||||
|
||||
private static final String KEY_READING_FULLSCREEN = "reading_fullscreen";
|
||||
private static final boolean VALUE_READING_FULLSCREEN = true;
|
||||
|
||||
public static boolean getReadingFullscreen() {
|
||||
return getBoolean(KEY_READING_FULLSCREEN, VALUE_READING_FULLSCREEN);
|
||||
}
|
||||
@@ -548,9 +644,6 @@ public class Settings {
|
||||
putBoolean(KEY_READING_FULLSCREEN, value);
|
||||
}
|
||||
|
||||
private static final String KEY_CUSTOM_SCREEN_LIGHTNESS = "custom_screen_lightness";
|
||||
private static final boolean DEFAULT_CUSTOM_SCREEN_LIGHTNESS = false;
|
||||
|
||||
public static boolean getCustomScreenLightness() {
|
||||
return getBoolean(KEY_CUSTOM_SCREEN_LIGHTNESS, DEFAULT_CUSTOM_SCREEN_LIGHTNESS);
|
||||
}
|
||||
@@ -559,9 +652,6 @@ public class Settings {
|
||||
putBoolean(KEY_CUSTOM_SCREEN_LIGHTNESS, value);
|
||||
}
|
||||
|
||||
private static final String KEY_SCREEN_LIGHTNESS = "screen_lightness";
|
||||
private static final int DEFAULT_SCREEN_LIGHTNESS = 50;
|
||||
|
||||
public static int getScreenLightness() {
|
||||
return getInt(KEY_SCREEN_LIGHTNESS, DEFAULT_SCREEN_LIGHTNESS);
|
||||
}
|
||||
@@ -570,28 +660,14 @@ public class Settings {
|
||||
putInt(KEY_SCREEN_LIGHTNESS, value);
|
||||
}
|
||||
|
||||
/********************
|
||||
****** Privacy and Security
|
||||
********************/
|
||||
public static final String KEY_SEC_SECURITY = "enable_secure";
|
||||
public static final boolean VALUE_SEC_SECURITY = false;
|
||||
|
||||
public static boolean getEnabledSecurity() {
|
||||
return getBoolean(KEY_SEC_SECURITY, VALUE_SEC_SECURITY);
|
||||
}
|
||||
|
||||
public static void putEnabledSecurity(boolean value) {
|
||||
putBoolean(KEY_READING_FULLSCREEN, value);
|
||||
}
|
||||
|
||||
/********************
|
||||
****** Download
|
||||
********************/
|
||||
public static final String KEY_DOWNLOAD_SAVE_SCHEME = "image_scheme";
|
||||
public static final String KEY_DOWNLOAD_SAVE_AUTHORITY = "image_authority";
|
||||
public static final String KEY_DOWNLOAD_SAVE_PATH = "image_path";
|
||||
public static final String KEY_DOWNLOAD_SAVE_QUERY = "image_query";
|
||||
public static final String KEY_DOWNLOAD_SAVE_FRAGMENT = "image_fragment";
|
||||
|
||||
@Nullable
|
||||
public static UniFile getDownloadLocation() {
|
||||
UniFile dir = null;
|
||||
@@ -625,16 +701,10 @@ public class Settings {
|
||||
}
|
||||
}
|
||||
|
||||
public static final String KEY_MEDIA_SCAN = "media_scan";
|
||||
private static final boolean DEFAULT_MEDIA_SCAN = false;
|
||||
|
||||
public static boolean getMediaScan() {
|
||||
return getBoolean(KEY_MEDIA_SCAN, DEFAULT_MEDIA_SCAN);
|
||||
}
|
||||
|
||||
private static final String KEY_RECENT_DOWNLOAD_LABEL = "recent_download_label";
|
||||
private static final String DEFAULT_RECENT_DOWNLOAD_LABEL = null;
|
||||
|
||||
public static String getRecentDownloadLabel() {
|
||||
return getString(KEY_RECENT_DOWNLOAD_LABEL, DEFAULT_RECENT_DOWNLOAD_LABEL);
|
||||
}
|
||||
@@ -643,9 +713,6 @@ public class Settings {
|
||||
putString(KEY_RECENT_DOWNLOAD_LABEL, value);
|
||||
}
|
||||
|
||||
private static final String KEY_HAS_DEFAULT_DOWNLOAD_LABEL = "has_default_download_label";
|
||||
private static final boolean DEFAULT_HAS_DOWNLOAD_LABEL = false;
|
||||
|
||||
public static boolean getHasDefaultDownloadLabel() {
|
||||
return getBoolean(KEY_HAS_DEFAULT_DOWNLOAD_LABEL, DEFAULT_HAS_DOWNLOAD_LABEL);
|
||||
}
|
||||
@@ -654,9 +721,6 @@ public class Settings {
|
||||
putBoolean(KEY_HAS_DEFAULT_DOWNLOAD_LABEL, hasDefaultDownloadLabel);
|
||||
}
|
||||
|
||||
private static final String KEY_DEFAULT_DOWNLOAD_LABEL = "default_download_label";
|
||||
private static final String DEFAULT_DOWNLOAD_LABEL = null;
|
||||
|
||||
public static String getDefaultDownloadLabel() {
|
||||
return getString(KEY_DEFAULT_DOWNLOAD_LABEL, DEFAULT_DOWNLOAD_LABEL);
|
||||
}
|
||||
@@ -665,9 +729,6 @@ public class Settings {
|
||||
putString(KEY_DEFAULT_DOWNLOAD_LABEL, value);
|
||||
}
|
||||
|
||||
private static final String KEY_MULTI_THREAD_DOWNLOAD = "download_thread";
|
||||
private static final int DEFAULT_MULTI_THREAD_DOWNLOAD = 3;
|
||||
|
||||
public static int getMultiThreadDownload() {
|
||||
return getIntFromStr(KEY_MULTI_THREAD_DOWNLOAD, DEFAULT_MULTI_THREAD_DOWNLOAD);
|
||||
}
|
||||
@@ -676,9 +737,6 @@ public class Settings {
|
||||
putIntToStr(KEY_MULTI_THREAD_DOWNLOAD, value);
|
||||
}
|
||||
|
||||
private static final String KEY_PRELOAD_IMAGE = "preload_image";
|
||||
private static final int DEFAULT_PRELOAD_IMAGE = 5;
|
||||
|
||||
public static int getPreloadImage() {
|
||||
return getIntFromStr(KEY_PRELOAD_IMAGE, DEFAULT_PRELOAD_IMAGE);
|
||||
}
|
||||
@@ -687,9 +745,6 @@ public class Settings {
|
||||
putIntToStr(KEY_PRELOAD_IMAGE, value);
|
||||
}
|
||||
|
||||
public static final String KEY_IMAGE_RESOLUTION = "image_size";
|
||||
public static final String DEFAULT_IMAGE_RESOLUTION = EhConfig.IMAGE_SIZE_AUTO;
|
||||
|
||||
public static String getImageResolution() {
|
||||
return getString(KEY_IMAGE_RESOLUTION, DEFAULT_IMAGE_RESOLUTION);
|
||||
}
|
||||
@@ -700,9 +755,6 @@ public class Settings {
|
||||
putString(KEY_IMAGE_RESOLUTION, value);
|
||||
}
|
||||
|
||||
private static final String KEY_DOWNLOAD_ORIGIN_IMAGE = "download_origin_image";
|
||||
private static final boolean DEFAULT_DOWNLOAD_ORIGIN_IMAGE = false;
|
||||
|
||||
public static boolean getDownloadOriginImage() {
|
||||
return getBoolean(KEY_DOWNLOAD_ORIGIN_IMAGE, DEFAULT_DOWNLOAD_ORIGIN_IMAGE);
|
||||
}
|
||||
@@ -711,46 +763,6 @@ public class Settings {
|
||||
putBoolean(KEY_DOWNLOAD_ORIGIN_IMAGE, value);
|
||||
}
|
||||
|
||||
/********************
|
||||
****** Favorites
|
||||
********************/
|
||||
private static final String KEY_FAV_CAT_0 = "fav_cat_0";
|
||||
private static final String KEY_FAV_CAT_1 = "fav_cat_1";
|
||||
private static final String KEY_FAV_CAT_2 = "fav_cat_2";
|
||||
private static final String KEY_FAV_CAT_3 = "fav_cat_3";
|
||||
private static final String KEY_FAV_CAT_4 = "fav_cat_4";
|
||||
private static final String KEY_FAV_CAT_5 = "fav_cat_5";
|
||||
private static final String KEY_FAV_CAT_6 = "fav_cat_6";
|
||||
private static final String KEY_FAV_CAT_7 = "fav_cat_7";
|
||||
private static final String KEY_FAV_CAT_8 = "fav_cat_8";
|
||||
private static final String KEY_FAV_CAT_9 = "fav_cat_9";
|
||||
private static final String DEFAULT_FAV_CAT_0 = "Favorites 0";
|
||||
private static final String DEFAULT_FAV_CAT_1 = "Favorites 1";
|
||||
private static final String DEFAULT_FAV_CAT_2 = "Favorites 2";
|
||||
private static final String DEFAULT_FAV_CAT_3 = "Favorites 3";
|
||||
private static final String DEFAULT_FAV_CAT_4 = "Favorites 4";
|
||||
private static final String DEFAULT_FAV_CAT_5 = "Favorites 5";
|
||||
private static final String DEFAULT_FAV_CAT_6 = "Favorites 6";
|
||||
private static final String DEFAULT_FAV_CAT_7 = "Favorites 7";
|
||||
private static final String DEFAULT_FAV_CAT_8 = "Favorites 8";
|
||||
private static final String DEFAULT_FAV_CAT_9 = "Favorites 9";
|
||||
|
||||
private static final String KEY_FAV_COUNT_0 = "fav_count_0";
|
||||
private static final String KEY_FAV_COUNT_1 = "fav_count_1";
|
||||
private static final String KEY_FAV_COUNT_2 = "fav_count_2";
|
||||
private static final String KEY_FAV_COUNT_3 = "fav_count_3";
|
||||
private static final String KEY_FAV_COUNT_4 = "fav_count_4";
|
||||
private static final String KEY_FAV_COUNT_5 = "fav_count_5";
|
||||
private static final String KEY_FAV_COUNT_6 = "fav_count_6";
|
||||
private static final String KEY_FAV_COUNT_7 = "fav_count_7";
|
||||
private static final String KEY_FAV_COUNT_8 = "fav_count_8";
|
||||
private static final String KEY_FAV_COUNT_9 = "fav_count_9";
|
||||
|
||||
private static final String KEY_FAV_LOCAL = "fav_local";
|
||||
private static final String KEY_FAV_CLOUD = "fav_cloud";
|
||||
|
||||
private static final int DEFAULT_FAV_COUNT = 0;
|
||||
|
||||
public static String[] getFavCat() {
|
||||
String[] favCat = new String[10];
|
||||
favCat[0] = sSettingsPre.getString(KEY_FAV_CAT_0, DEFAULT_FAV_CAT_0);
|
||||
@@ -829,9 +841,6 @@ public class Settings {
|
||||
sSettingsPre.edit().putInt(KEY_FAV_CLOUD, count).apply();
|
||||
}
|
||||
|
||||
private static final String KEY_RECENT_FAV_CAT = "recent_fav_cat";
|
||||
private static final int DEFAULT_RECENT_FAV_CAT = FavListUrlBuilder.FAV_CAT_ALL;
|
||||
|
||||
public static int getRecentFavCat() {
|
||||
return getInt(KEY_RECENT_FAV_CAT, DEFAULT_RECENT_FAV_CAT);
|
||||
}
|
||||
@@ -840,11 +849,6 @@ public class Settings {
|
||||
putInt(KEY_RECENT_FAV_CAT, value);
|
||||
}
|
||||
|
||||
// -1 for local, 0 - 9 for cloud favorite, other for no default fav slot
|
||||
private static final String KEY_DEFAULT_FAV_SLOT = "default_favorite_2";
|
||||
public static final int INVALID_DEFAULT_FAV_SLOT = -2;
|
||||
private static final int DEFAULT_DEFAULT_FAV_SLOT = INVALID_DEFAULT_FAV_SLOT;
|
||||
|
||||
public static int getDefaultFavSlot() {
|
||||
return getInt(KEY_DEFAULT_FAV_SLOT, DEFAULT_DEFAULT_FAV_SLOT);
|
||||
}
|
||||
@@ -853,12 +857,6 @@ public class Settings {
|
||||
putInt(KEY_DEFAULT_FAV_SLOT, value);
|
||||
}
|
||||
|
||||
/********************
|
||||
****** Analytics
|
||||
********************/
|
||||
private static final String KEY_ASK_ANALYTICS = "ask_analytics";
|
||||
private static final boolean DEFAULT_ASK_ANALYTICS = true;
|
||||
|
||||
public static boolean getAskAnalytics() {
|
||||
return getBoolean(KEY_ASK_ANALYTICS, DEFAULT_ASK_ANALYTICS);
|
||||
}
|
||||
@@ -867,9 +865,6 @@ public class Settings {
|
||||
putBoolean(KEY_ASK_ANALYTICS, value);
|
||||
}
|
||||
|
||||
public static final String KEY_ENABLE_ANALYTICS = "enable_analytics";
|
||||
private static final boolean DEFAULT_ENABLE_ANALYTICS = false;
|
||||
|
||||
public static boolean getEnableAnalytics() {
|
||||
return getBoolean(KEY_ENABLE_ANALYTICS, DEFAULT_ENABLE_ANALYTICS);
|
||||
}
|
||||
@@ -878,10 +873,6 @@ public class Settings {
|
||||
putBoolean(KEY_ENABLE_ANALYTICS, value);
|
||||
}
|
||||
|
||||
private static final String KEY_USER_ID = "user_id";
|
||||
private static final String FILENAME_USER_ID = ".user_id";
|
||||
private static final int LENGTH_USER_ID = 32;
|
||||
|
||||
public static String getUserID() {
|
||||
boolean writeXml = false;
|
||||
boolean writeFile = false;
|
||||
@@ -940,14 +931,6 @@ public class Settings {
|
||||
return true;
|
||||
}
|
||||
|
||||
/********************
|
||||
****** Update
|
||||
********************/
|
||||
private static final String KEY_BETA_UPDATE_CHANNEL = "beta_update_channel";
|
||||
private static final boolean DEFAULT_BETA_UPDATE_CHANNEL = EhApplication.BETA;
|
||||
private static final String KEY_SKIP_UPDATE_VERSION = "skip_update_version";
|
||||
private static final int DEFAULT_SKIP_UPDATE_VERSION = 0;
|
||||
|
||||
public static boolean getBetaUpdateChannel() {
|
||||
return getBoolean(KEY_BETA_UPDATE_CHANNEL, DEFAULT_BETA_UPDATE_CHANNEL);
|
||||
}
|
||||
@@ -964,12 +947,6 @@ public class Settings {
|
||||
putInt(KEY_SKIP_UPDATE_VERSION, value);
|
||||
}
|
||||
|
||||
/********************
|
||||
****** Advanced
|
||||
********************/
|
||||
public static final String KEY_SAVE_PARSE_ERROR_BODY = "save_parse_error_body";
|
||||
private static final boolean DEFAULT_SAVE_PARSE_ERROR_BODY = EhApplication.BETA;
|
||||
|
||||
public static boolean getSaveParseErrorBody() {
|
||||
return getBoolean(KEY_SAVE_PARSE_ERROR_BODY, DEFAULT_SAVE_PARSE_ERROR_BODY);
|
||||
}
|
||||
@@ -978,16 +955,10 @@ public class Settings {
|
||||
putBoolean(KEY_SAVE_PARSE_ERROR_BODY, value);
|
||||
}
|
||||
|
||||
private static final String KEY_SAVE_CRASH_LOG = "save_crash_log";
|
||||
private static final boolean DEFAULT_SAVE_CRASH_LOG = false;
|
||||
|
||||
public static boolean getSaveCrashLog() {
|
||||
return getBoolean(KEY_SAVE_CRASH_LOG, DEFAULT_SAVE_CRASH_LOG);
|
||||
}
|
||||
|
||||
public static final String KEY_SECURITY = "security";
|
||||
public static final String DEFAULT_SECURITY = "";
|
||||
|
||||
public static String getSecurity() {
|
||||
return getString(KEY_SECURITY, DEFAULT_SECURITY);
|
||||
}
|
||||
@@ -996,8 +967,6 @@ public class Settings {
|
||||
putString(KEY_SECURITY, value);
|
||||
}
|
||||
|
||||
public static final String KEY_ENABLE_FINGERPRINT = "enable_fingerprint";
|
||||
|
||||
public static boolean getEnableFingerprint() {
|
||||
return getBoolean(KEY_ENABLE_FINGERPRINT, false);
|
||||
}
|
||||
@@ -1006,16 +975,10 @@ public class Settings {
|
||||
putBoolean(KEY_ENABLE_FINGERPRINT, value);
|
||||
}
|
||||
|
||||
public static final String KEY_READ_CACHE_SIZE = "read_cache_size";
|
||||
public static final int DEFAULT_READ_CACHE_SIZE = 160;
|
||||
|
||||
public static int getReadCacheSize() {
|
||||
return getIntFromStr(KEY_READ_CACHE_SIZE, DEFAULT_READ_CACHE_SIZE);
|
||||
}
|
||||
|
||||
public static final String KEY_BUILT_IN_HOSTS = "built_in_hosts";
|
||||
private static final boolean DEFAULT_BUILT_IN_HOSTS = false;
|
||||
|
||||
public static boolean getBuiltInHosts() {
|
||||
return getBoolean(KEY_BUILT_IN_HOSTS, DEFAULT_BUILT_IN_HOSTS);
|
||||
}
|
||||
@@ -1024,9 +987,6 @@ public class Settings {
|
||||
putBoolean(KEY_BUILT_IN_HOSTS, value);
|
||||
}
|
||||
|
||||
public static final String KEY_APP_LANGUAGE = "app_language";
|
||||
private static final String DEFAULT_APP_LANGUAGE = "system";
|
||||
|
||||
public static String getAppLanguage() {
|
||||
return getString(KEY_APP_LANGUAGE, DEFAULT_APP_LANGUAGE);
|
||||
}
|
||||
@@ -1035,9 +995,6 @@ public class Settings {
|
||||
putString(KEY_APP_LANGUAGE, value);
|
||||
}
|
||||
|
||||
private static final String KEY_PROXY_TYPE = "proxy_type";
|
||||
private static final int DEFAULT_PROXY_TYPE = EhProxySelector.TYPE_SYSTEM;
|
||||
|
||||
public static int getProxyType() {
|
||||
return getInt(KEY_PROXY_TYPE, DEFAULT_PROXY_TYPE);
|
||||
}
|
||||
@@ -1046,9 +1003,6 @@ public class Settings {
|
||||
putInt(KEY_PROXY_TYPE, value);
|
||||
}
|
||||
|
||||
private static final String KEY_PROXY_IP = "proxy_ip";
|
||||
private static final String DEFAULT_PROXY_IP = null;
|
||||
|
||||
public static String getProxyIp() {
|
||||
return getString(KEY_PROXY_IP, DEFAULT_PROXY_IP);
|
||||
}
|
||||
@@ -1057,9 +1011,6 @@ public class Settings {
|
||||
putString(KEY_PROXY_IP, value);
|
||||
}
|
||||
|
||||
private static final String KEY_PROXY_PORT = "proxy_port";
|
||||
private static final int DEFAULT_PROXY_PORT = -1;
|
||||
|
||||
public static int getProxyPort() {
|
||||
return getInt(KEY_PROXY_PORT, DEFAULT_PROXY_PORT);
|
||||
}
|
||||
@@ -1068,12 +1019,6 @@ public class Settings {
|
||||
putInt(KEY_PROXY_PORT, value);
|
||||
}
|
||||
|
||||
/********************
|
||||
****** Guide
|
||||
********************/
|
||||
private static final String KEY_GUIDE_QUICK_SEARCH = "guide_quick_search";
|
||||
private static final boolean DEFAULT_GUIDE_QUICK_SEARCH = true;
|
||||
|
||||
public static boolean getGuideQuickSearch() {
|
||||
return getBoolean(KEY_GUIDE_QUICK_SEARCH, DEFAULT_GUIDE_QUICK_SEARCH);
|
||||
}
|
||||
@@ -1082,9 +1027,6 @@ public class Settings {
|
||||
putBoolean(KEY_GUIDE_QUICK_SEARCH, value);
|
||||
}
|
||||
|
||||
private static final String KEY_GUIDE_COLLECTIONS = "guide_collections";
|
||||
private static final boolean DEFAULT_GUIDE_COLLECTIONS = true;
|
||||
|
||||
public static boolean getGuideCollections() {
|
||||
return getBoolean(KEY_GUIDE_COLLECTIONS, DEFAULT_GUIDE_COLLECTIONS);
|
||||
}
|
||||
@@ -1093,9 +1035,6 @@ public class Settings {
|
||||
putBoolean(KEY_GUIDE_COLLECTIONS, value);
|
||||
}
|
||||
|
||||
private static final String KEY_GUIDE_DOWNLOAD_THUMB = "guide_download_thumb";
|
||||
private static final boolean DEFAULT_GUIDE_DOWNLOAD_THUMB = true;
|
||||
|
||||
public static boolean getGuideDownloadThumb() {
|
||||
return getBoolean(KEY_GUIDE_DOWNLOAD_THUMB, DEFAULT_GUIDE_DOWNLOAD_THUMB);
|
||||
}
|
||||
@@ -1104,9 +1043,6 @@ public class Settings {
|
||||
putBoolean(KEY_GUIDE_DOWNLOAD_THUMB, value);
|
||||
}
|
||||
|
||||
private static final String KEY_GUIDE_DOWNLOAD_LABELS = "guide_download_labels";
|
||||
private static final boolean DEFAULT_GUIDE_DOWNLOAD_LABELS = true;
|
||||
|
||||
public static boolean getGuideDownloadLabels() {
|
||||
return getBoolean(KEY_GUIDE_DOWNLOAD_LABELS, DEFAULT_GUIDE_DOWNLOAD_LABELS);
|
||||
}
|
||||
@@ -1115,9 +1051,6 @@ public class Settings {
|
||||
putBoolean(KEY_GUIDE_DOWNLOAD_LABELS, value);
|
||||
}
|
||||
|
||||
private static final String KEY_GUIDE_GALLERY = "guide_gallery";
|
||||
private static final boolean DEFAULT_GUIDE_GALLERY = true;
|
||||
|
||||
public static boolean getGuideGallery() {
|
||||
return getBoolean(KEY_GUIDE_GALLERY, DEFAULT_GUIDE_GALLERY);
|
||||
}
|
||||
@@ -1126,9 +1059,6 @@ public class Settings {
|
||||
putBoolean(KEY_GUIDE_GALLERY, value);
|
||||
}
|
||||
|
||||
private static final String KEY_CLIPBOARD_TEXT_HASH_CODE = "clipboard_text_hash_code";
|
||||
private static final int DEFAULT_CLIPBOARD_TEXT_HASH_CODE = 0;
|
||||
|
||||
public static int getClipboardTextHashCode() {
|
||||
return getInt(KEY_CLIPBOARD_TEXT_HASH_CODE, DEFAULT_CLIPBOARD_TEXT_HASH_CODE);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,9 @@ import android.net.Uri;
|
||||
import android.provider.Browser;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.hippo.ehviewer.client.EhUrlOpener;
|
||||
import com.hippo.ehviewer.ui.MainActivity;
|
||||
import com.hippo.scene.Announcer;
|
||||
|
||||
@@ -18,15 +18,18 @@ package com.hippo.ehviewer.client;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.client.exception.CancelledException;
|
||||
import com.hippo.util.ExceptionUtils;
|
||||
import com.hippo.util.IoThreadPoolExecutor;
|
||||
import com.hippo.yorozuya.SimpleHandler;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
@@ -70,14 +73,22 @@ public class EhClient {
|
||||
}
|
||||
}
|
||||
|
||||
public interface Callback<E> {
|
||||
|
||||
void onSuccess(E result);
|
||||
|
||||
void onFailure(Exception e);
|
||||
|
||||
void onCancel();
|
||||
}
|
||||
|
||||
public class Task extends AsyncTask<Object, Void, Object> {
|
||||
|
||||
private final int mMethod;
|
||||
private Callback mCallback;
|
||||
private EhConfig mEhConfig;
|
||||
|
||||
private final AtomicReference<Call> mCall = new AtomicReference<>();
|
||||
private final AtomicBoolean mStop = new AtomicBoolean();
|
||||
private Callback mCallback;
|
||||
private EhConfig mEhConfig;
|
||||
|
||||
public Task(int method, Callback callback, EhConfig ehConfig) {
|
||||
mMethod = method;
|
||||
@@ -202,13 +213,4 @@ public class EhClient {
|
||||
mCall.lazySet(null);
|
||||
}
|
||||
}
|
||||
|
||||
public interface Callback<E> {
|
||||
|
||||
void onSuccess(E result);
|
||||
|
||||
void onFailure(Exception e);
|
||||
|
||||
void onCancel();
|
||||
}
|
||||
}
|
||||
@@ -27,180 +27,14 @@ public class EhConfig implements Cloneable {
|
||||
* The Cookie key of uconfig
|
||||
*/
|
||||
public static final String KEY_UCONFIG = "uconfig";
|
||||
|
||||
/**
|
||||
* The key of load images through the Hentai@Home Network
|
||||
* @see #LOAD_FROM_HAH_YES
|
||||
* @see #LOAD_FROM_HAH_NO
|
||||
*/
|
||||
private static final String KEY_LOAD_FROM_HAH = "uh";
|
||||
/**
|
||||
* The key of Image Size Settings
|
||||
* @see #IMAGE_SIZE_AUTO
|
||||
* @see #IMAGE_SIZE_780X
|
||||
* @see #IMAGE_SIZE_980X
|
||||
* @see #IMAGE_SIZE_1280X
|
||||
* @see #IMAGE_SIZE_1600X
|
||||
* @see #IMAGE_SIZE_2400X
|
||||
*/
|
||||
private static final String KEY_IMAGE_SIZE = "xr";
|
||||
/**
|
||||
* The key of scale images width
|
||||
*/
|
||||
private static final String KEY_SCALE_WIDTH = "rx";
|
||||
/**
|
||||
* The key of scale images height
|
||||
*/
|
||||
private static final String KEY_SCALE_HEIGHT = "ry";
|
||||
/**
|
||||
* The key of Gallery Name Display
|
||||
* @see #GALLERY_TITLE_DEFAULT
|
||||
* @see #GALLERY_TITLE_JAPANESE
|
||||
*/
|
||||
private static final String KEY_GALLERY_TITLE = "tl";
|
||||
/**
|
||||
* The key of the behavior for downloading archiver
|
||||
* @see #ARCHIVER_DOWNLOAD_MAMS
|
||||
* @see #ARCHIVER_DOWNLOAD_MAAS
|
||||
* @see #ARCHIVER_DOWNLOAD_AAMS
|
||||
* @see #ARCHIVER_DOWNLOAD_AAAS
|
||||
*/
|
||||
private static final String KEY_ARCHIVER_DOWNLOAD = "ar";
|
||||
/**
|
||||
* The key of display mode would you like to use on the front and search pages
|
||||
* @see #LAYOUT_MODE_LIST
|
||||
* @see #LAYOUT_MODE_THUMB
|
||||
*/
|
||||
private static final String KEY_LAYOUT_MODE = "dm";
|
||||
/**
|
||||
* The key for show popular
|
||||
* @see #POPULAR_YES
|
||||
* @see #POPULAR_NO
|
||||
*/
|
||||
private static final String KEY_POPULAR = "prn";
|
||||
/**
|
||||
* The key of categories would you like to view as default on the front page
|
||||
*/
|
||||
private static final String KEY_DEFAULT_CATEGORIES = "cats";
|
||||
/**
|
||||
* The key for favorites sort
|
||||
* @see #FAVORITES_SORT_GALLERY_UPDATE_TIME
|
||||
* @see #FAVORITES_SORT_FAVORITED_TIME
|
||||
*/
|
||||
private static final String KEY_FAVORITES_SORT = "fs";
|
||||
/**
|
||||
* The key of exclude certain namespaces from a default tag search
|
||||
*/
|
||||
private static final String KEY_EXCLUDED_NAMESPACES = "xns";
|
||||
/**
|
||||
* The key of hide galleries in certain languages from the gallery list and searches
|
||||
*/
|
||||
private static final String KEY_EXCLUDED_LANGUAGES = "xl";
|
||||
/**
|
||||
* The key of how many results would you like per page for the index/search page and torrent search pages
|
||||
* @see #RESULT_COUNT_25
|
||||
* @see #RESULT_COUNT_50
|
||||
* @see #RESULT_COUNT_100
|
||||
* @see #RESULT_COUNT_200
|
||||
*/
|
||||
private static final String KEY_RESULT_COUNT = "rc";
|
||||
/**
|
||||
* The key of mouse-over thumb
|
||||
* @see #MOUSE_OVER_YES
|
||||
* @see #MOUSE_OVER_NO
|
||||
*/
|
||||
private static final String KEY_MOUSE_OVER = "lt";
|
||||
/**
|
||||
* The key of preview size
|
||||
* @see #PREVIEW_SIZE_NORMAL
|
||||
* @see #PREVIEW_SIZE_LARGE
|
||||
*/
|
||||
private static final String KEY_PREVIEW_SIZE = "ts";
|
||||
/**
|
||||
* The key of preview row per page
|
||||
* @see #PREVIEW_ROW_4
|
||||
* @see #PREVIEW_ROW_10
|
||||
* @see #PREVIEW_ROW_20
|
||||
* @see #PREVIEW_ROW_40
|
||||
*/
|
||||
private static final String KEY_PREVIEW_ROW = "tr";
|
||||
/**
|
||||
* The key of sort order for gallery comments
|
||||
* @see #COMMENTS_SORT_OLDEST_FIRST
|
||||
* @see #COMMENTS_SORT_RECENT_FIRST
|
||||
* @see #COMMENTS_SORT_HIGHEST_SCORE_FIRST
|
||||
*/
|
||||
private static final String KEY_COMMENTS_SORT = "cs";
|
||||
/**
|
||||
* The key of show gallery comment votes
|
||||
* @see #COMMENTS_VOTES_POP
|
||||
* @see #COMMENTS_VOTES_ALWAYS
|
||||
*/
|
||||
private static final String KEY_COMMENTS_VOTES = "sc";
|
||||
/**
|
||||
* The key of sort order for gallery tags
|
||||
* @see #TAGS_SORT_ALPHABETICAL
|
||||
* @see #TAGS_SORT_POWER
|
||||
*/
|
||||
private static final String KEY_TAGS_SORT = "to";
|
||||
/**
|
||||
* The key of show gallery page numbers
|
||||
* @see #SHOW_GALLERY_INDEX_NO
|
||||
* @see #SHOW_GALLERY_INDEX_YES
|
||||
*/
|
||||
private static final String KEY_SHOW_GALLERY_INDEX = "pn";
|
||||
/**
|
||||
* The key of the IP:Port of a proxy-enabled Hentai@Home Client
|
||||
* to load all images
|
||||
*/
|
||||
private static final String KEY_HAH_CLIENT_IP_PORT = "hp";
|
||||
/**
|
||||
* The key of the passkey of a proxy-enabled Hentai@Home Client
|
||||
* to load all images
|
||||
*/
|
||||
private static final String KEY_HAH_CLIENT_PASSKEY = "hk";
|
||||
/**
|
||||
* The key of enable Tag Flagging
|
||||
* @see #ENABLE_TAG_FLAGGING_NO
|
||||
* @see #ENABLE_TAG_FLAGGING_YES
|
||||
*/
|
||||
private static final String KEY_ENABLE_TAG_FLAGGING = "tf";
|
||||
/**
|
||||
* The key of always display the original images instead of the resampled versions
|
||||
* @see #ALWAYS_ORIGINAL_NO
|
||||
* @see #ALWAYS_ORIGINAL_YES
|
||||
*/
|
||||
private static final String KEY_ALWAYS_ORIGINAL = "oi";
|
||||
/**
|
||||
* The key of enable the multi-Page Viewer
|
||||
* @see #MULTI_PAGE_NO
|
||||
* @see #MULTI_PAGE_YES
|
||||
*/
|
||||
private static final String KEY_MULTI_PAGE = "qb";
|
||||
/**
|
||||
* The key of multi-Page Viewer Display Style
|
||||
* @see #MULTI_PAGE_STYLE_C
|
||||
* @see #MULTI_PAGE_STYLE_N
|
||||
* @see #MULTI_PAGE_STYLE_Y
|
||||
*/
|
||||
private static final String KEY_MULTI_PAGE_STYLE = "ms";
|
||||
/**
|
||||
* The key of multi-Page Viewer Thumbnail Pane
|
||||
* @see #MULTI_PAGE_THUMB_HIDE
|
||||
* @see #MULTI_PAGE_THUMB_SHOW
|
||||
*/
|
||||
private static final String KEY_MULTI_PAGE_THUMB = "mt";
|
||||
|
||||
/**
|
||||
* The Cookie key of lofi resolution
|
||||
*/
|
||||
public static final String KEY_LOFI_RESOLUTION = "xres";
|
||||
|
||||
/**
|
||||
* The Cookie key of show warning
|
||||
*/
|
||||
public static final String KEY_CONTENT_WARNING = "nw";
|
||||
|
||||
/**
|
||||
* load images through the Hentai@Home Network
|
||||
*/
|
||||
@@ -209,7 +43,6 @@ public class EhConfig implements Cloneable {
|
||||
* do not load images through the Hentai@Home Network
|
||||
*/
|
||||
public static final String LOAD_FROM_HAH_NO = "n";
|
||||
|
||||
/**
|
||||
* Image Size Auto
|
||||
*/
|
||||
@@ -234,16 +67,6 @@ public class EhConfig implements Cloneable {
|
||||
* Image Size 2400x
|
||||
*/
|
||||
public static final String IMAGE_SIZE_2400X = "2400";
|
||||
|
||||
/**
|
||||
* Default gallery title
|
||||
*/
|
||||
private static final String GALLERY_TITLE_DEFAULT = "r";
|
||||
/**
|
||||
* Japanese gallery title
|
||||
*/
|
||||
private static final String GALLERY_TITLE_JAPANESE = "j";
|
||||
|
||||
/**
|
||||
* Manual Accept, Manual Start
|
||||
*/
|
||||
@@ -260,7 +83,6 @@ public class EhConfig implements Cloneable {
|
||||
* Auto Accept, Auto Start
|
||||
*/
|
||||
public static final String ARCHIVER_DOWNLOAD_AAAS = "3";
|
||||
|
||||
/**
|
||||
* List View on the front and search pages
|
||||
*/
|
||||
@@ -269,16 +91,6 @@ public class EhConfig implements Cloneable {
|
||||
* Thumbnail View on the front and search pages
|
||||
*/
|
||||
public static final String LAYOUT_MODE_THUMB = "t";
|
||||
|
||||
/**
|
||||
* Show popular
|
||||
*/
|
||||
private static final String POPULAR_YES = "y";
|
||||
/**
|
||||
* Don't show popular
|
||||
*/
|
||||
private static final String POPULAR_NO = "n";
|
||||
|
||||
public static final int MISC = 0x1;
|
||||
public static final int DOUJINSHI = 0x2;
|
||||
public static final int MANGA = 0x4;
|
||||
@@ -290,7 +102,6 @@ public class EhConfig implements Cloneable {
|
||||
public static final int NON_H = 0x100;
|
||||
public static final int WESTERN = 0x200;
|
||||
public static final int ALL_CATEGORY = 0x3ff;
|
||||
|
||||
public static final int NAMESPACES_RECLASS = 0x1;
|
||||
public static final int NAMESPACES_LANGUAGE = 0x2;
|
||||
public static final int NAMESPACES_PARODY = 0x4;
|
||||
@@ -299,7 +110,6 @@ public class EhConfig implements Cloneable {
|
||||
public static final int NAMESPACES_ARTIST = 0x20;
|
||||
public static final int NAMESPACES_MALE = 0x40;
|
||||
public static final int NAMESPACES_FEMALE = 0x80;
|
||||
|
||||
public static final String JAPANESE_ORIGINAL = "0";
|
||||
public static final String JAPANESE_TRANSLATED = "1024";
|
||||
public static final String JAPANESE_REWRITE = "2048";
|
||||
@@ -351,16 +161,6 @@ public class EhConfig implements Cloneable {
|
||||
public static final String OTHER_ORIGINAL = "255";
|
||||
public static final String OTHER_TRANSLATED = "1279";
|
||||
public static final String OTHER_REWRITE = "2303";
|
||||
|
||||
/**
|
||||
* Sort favorites by last gallery update time
|
||||
*/
|
||||
private static final String FAVORITES_SORT_GALLERY_UPDATE_TIME = "p";
|
||||
/**
|
||||
* Sort favorites by favorited time
|
||||
*/
|
||||
private static final String FAVORITES_SORT_FAVORITED_TIME = "f";
|
||||
|
||||
/**
|
||||
* 25 results per page for the index/search page and torrent search pages
|
||||
*/
|
||||
@@ -377,7 +177,6 @@ public class EhConfig implements Cloneable {
|
||||
* 200 results per page for the index/search page and torrent search pages
|
||||
*/
|
||||
public static final String RESULT_COUNT_200 = "3";
|
||||
|
||||
/**
|
||||
* On mouse-over
|
||||
*/
|
||||
@@ -386,7 +185,6 @@ public class EhConfig implements Cloneable {
|
||||
* On page load
|
||||
*/
|
||||
public static final String MOUSE_OVER_NO = "p";
|
||||
|
||||
/**
|
||||
* Preview normal size
|
||||
*/
|
||||
@@ -395,7 +193,6 @@ public class EhConfig implements Cloneable {
|
||||
* Preview large size
|
||||
*/
|
||||
public static final String PREVIEW_SIZE_LARGE = "l";
|
||||
|
||||
/**
|
||||
* 4 row preview per page
|
||||
*/
|
||||
@@ -412,7 +209,6 @@ public class EhConfig implements Cloneable {
|
||||
* 40 row preview per page
|
||||
*/
|
||||
public static final String PREVIEW_ROW_40 = "20";
|
||||
|
||||
/**
|
||||
* Oldest comments first
|
||||
*/
|
||||
@@ -425,7 +221,6 @@ public class EhConfig implements Cloneable {
|
||||
* By highest score
|
||||
*/
|
||||
public static final String COMMENTS_SORT_HIGHEST_SCORE_FIRST = "s";
|
||||
|
||||
/**
|
||||
* Show gallery comment votes On score hover or click
|
||||
*/
|
||||
@@ -434,7 +229,6 @@ public class EhConfig implements Cloneable {
|
||||
* Always show gallery comment votes
|
||||
*/
|
||||
public static final String COMMENTS_VOTES_ALWAYS = "1";
|
||||
|
||||
/**
|
||||
* Sort order for gallery tags alphabetically
|
||||
*/
|
||||
@@ -443,7 +237,6 @@ public class EhConfig implements Cloneable {
|
||||
* Sort order for gallery tags by tag power
|
||||
*/
|
||||
public static final String TAGS_SORT_POWER = "p";
|
||||
|
||||
/**
|
||||
* Show gallery page numbers
|
||||
*/
|
||||
@@ -452,7 +245,6 @@ public class EhConfig implements Cloneable {
|
||||
* Do not show gallery page numbers
|
||||
*/
|
||||
public static final String SHOW_GALLERY_INDEX_NO = "0";
|
||||
|
||||
/**
|
||||
* Enable Tag Flagging
|
||||
*/
|
||||
@@ -461,7 +253,6 @@ public class EhConfig implements Cloneable {
|
||||
* Do not enable Tag Flagging
|
||||
*/
|
||||
public static final String ENABLE_TAG_FLAGGING_NO = "n";
|
||||
|
||||
/**
|
||||
* Always display the original images
|
||||
*/
|
||||
@@ -470,7 +261,6 @@ public class EhConfig implements Cloneable {
|
||||
* Do not Always display the original images
|
||||
*/
|
||||
public static final String ALWAYS_ORIGINAL_NO = "n";
|
||||
|
||||
/**
|
||||
* Enable the Multi-Page Viewe
|
||||
*/
|
||||
@@ -479,7 +269,6 @@ public class EhConfig implements Cloneable {
|
||||
* Do not enable the Multi-Page Viewe
|
||||
*/
|
||||
public static final String MULTI_PAGE_NO = "n";
|
||||
|
||||
/**
|
||||
* Align left, only scale if image is larger than browser width
|
||||
*/
|
||||
@@ -492,7 +281,6 @@ public class EhConfig implements Cloneable {
|
||||
* Align center, Always scale images to fit browser width
|
||||
*/
|
||||
public static final String MULTI_PAGE_STYLE_Y = "y";
|
||||
|
||||
/**
|
||||
* Show Multi-Page Viewer Thumbnail Pane
|
||||
*/
|
||||
@@ -501,32 +289,232 @@ public class EhConfig implements Cloneable {
|
||||
* Hide Multi-Page Viewer Thumbnail Pane
|
||||
*/
|
||||
public static final String MULTI_PAGE_THUMB_HIDE = "y";
|
||||
|
||||
/**
|
||||
* 460x for lofi resolution
|
||||
*/
|
||||
public static final String LOFI_RESOLUTION_460X = "1";
|
||||
|
||||
/**
|
||||
* 780X for lofi resolution
|
||||
*/
|
||||
public static final String LOFI_RESOLUTION_780X = "2";
|
||||
|
||||
/**
|
||||
* 980X for lofi resolution
|
||||
*/
|
||||
public static final String LOFI_RESOLUTION_980X = "3";
|
||||
|
||||
/**
|
||||
* show warning
|
||||
*/
|
||||
public static final String CONTENT_WARNING_SHOW = "0";
|
||||
|
||||
/**
|
||||
* not show warning
|
||||
*/
|
||||
public static final String CONTENT_WARNING_NOT_SHOW = "1";
|
||||
|
||||
/**
|
||||
* The key of load images through the Hentai@Home Network
|
||||
*
|
||||
* @see #LOAD_FROM_HAH_YES
|
||||
* @see #LOAD_FROM_HAH_NO
|
||||
*/
|
||||
private static final String KEY_LOAD_FROM_HAH = "uh";
|
||||
/**
|
||||
* The key of Image Size Settings
|
||||
*
|
||||
* @see #IMAGE_SIZE_AUTO
|
||||
* @see #IMAGE_SIZE_780X
|
||||
* @see #IMAGE_SIZE_980X
|
||||
* @see #IMAGE_SIZE_1280X
|
||||
* @see #IMAGE_SIZE_1600X
|
||||
* @see #IMAGE_SIZE_2400X
|
||||
*/
|
||||
private static final String KEY_IMAGE_SIZE = "xr";
|
||||
/**
|
||||
* The key of scale images width
|
||||
*/
|
||||
private static final String KEY_SCALE_WIDTH = "rx";
|
||||
/**
|
||||
* The key of scale images height
|
||||
*/
|
||||
private static final String KEY_SCALE_HEIGHT = "ry";
|
||||
/**
|
||||
* The key of Gallery Name Display
|
||||
*
|
||||
* @see #GALLERY_TITLE_DEFAULT
|
||||
* @see #GALLERY_TITLE_JAPANESE
|
||||
*/
|
||||
private static final String KEY_GALLERY_TITLE = "tl";
|
||||
/**
|
||||
* The key of the behavior for downloading archiver
|
||||
*
|
||||
* @see #ARCHIVER_DOWNLOAD_MAMS
|
||||
* @see #ARCHIVER_DOWNLOAD_MAAS
|
||||
* @see #ARCHIVER_DOWNLOAD_AAMS
|
||||
* @see #ARCHIVER_DOWNLOAD_AAAS
|
||||
*/
|
||||
private static final String KEY_ARCHIVER_DOWNLOAD = "ar";
|
||||
/**
|
||||
* The key of display mode would you like to use on the front and search pages
|
||||
*
|
||||
* @see #LAYOUT_MODE_LIST
|
||||
* @see #LAYOUT_MODE_THUMB
|
||||
*/
|
||||
private static final String KEY_LAYOUT_MODE = "dm";
|
||||
/**
|
||||
* The key for show popular
|
||||
*
|
||||
* @see #POPULAR_YES
|
||||
* @see #POPULAR_NO
|
||||
*/
|
||||
private static final String KEY_POPULAR = "prn";
|
||||
/**
|
||||
* The key of categories would you like to view as default on the front page
|
||||
*/
|
||||
private static final String KEY_DEFAULT_CATEGORIES = "cats";
|
||||
/**
|
||||
* The key for favorites sort
|
||||
*
|
||||
* @see #FAVORITES_SORT_GALLERY_UPDATE_TIME
|
||||
* @see #FAVORITES_SORT_FAVORITED_TIME
|
||||
*/
|
||||
private static final String KEY_FAVORITES_SORT = "fs";
|
||||
/**
|
||||
* The key of exclude certain namespaces from a default tag search
|
||||
*/
|
||||
private static final String KEY_EXCLUDED_NAMESPACES = "xns";
|
||||
/**
|
||||
* The key of hide galleries in certain languages from the gallery list and searches
|
||||
*/
|
||||
private static final String KEY_EXCLUDED_LANGUAGES = "xl";
|
||||
/**
|
||||
* The key of how many results would you like per page for the index/search page and torrent search pages
|
||||
*
|
||||
* @see #RESULT_COUNT_25
|
||||
* @see #RESULT_COUNT_50
|
||||
* @see #RESULT_COUNT_100
|
||||
* @see #RESULT_COUNT_200
|
||||
*/
|
||||
private static final String KEY_RESULT_COUNT = "rc";
|
||||
/**
|
||||
* The key of mouse-over thumb
|
||||
*
|
||||
* @see #MOUSE_OVER_YES
|
||||
* @see #MOUSE_OVER_NO
|
||||
*/
|
||||
private static final String KEY_MOUSE_OVER = "lt";
|
||||
/**
|
||||
* The key of preview size
|
||||
*
|
||||
* @see #PREVIEW_SIZE_NORMAL
|
||||
* @see #PREVIEW_SIZE_LARGE
|
||||
*/
|
||||
private static final String KEY_PREVIEW_SIZE = "ts";
|
||||
/**
|
||||
* The key of preview row per page
|
||||
*
|
||||
* @see #PREVIEW_ROW_4
|
||||
* @see #PREVIEW_ROW_10
|
||||
* @see #PREVIEW_ROW_20
|
||||
* @see #PREVIEW_ROW_40
|
||||
*/
|
||||
private static final String KEY_PREVIEW_ROW = "tr";
|
||||
/**
|
||||
* The key of sort order for gallery comments
|
||||
*
|
||||
* @see #COMMENTS_SORT_OLDEST_FIRST
|
||||
* @see #COMMENTS_SORT_RECENT_FIRST
|
||||
* @see #COMMENTS_SORT_HIGHEST_SCORE_FIRST
|
||||
*/
|
||||
private static final String KEY_COMMENTS_SORT = "cs";
|
||||
/**
|
||||
* The key of show gallery comment votes
|
||||
*
|
||||
* @see #COMMENTS_VOTES_POP
|
||||
* @see #COMMENTS_VOTES_ALWAYS
|
||||
*/
|
||||
private static final String KEY_COMMENTS_VOTES = "sc";
|
||||
/**
|
||||
* The key of sort order for gallery tags
|
||||
*
|
||||
* @see #TAGS_SORT_ALPHABETICAL
|
||||
* @see #TAGS_SORT_POWER
|
||||
*/
|
||||
private static final String KEY_TAGS_SORT = "to";
|
||||
/**
|
||||
* The key of show gallery page numbers
|
||||
*
|
||||
* @see #SHOW_GALLERY_INDEX_NO
|
||||
* @see #SHOW_GALLERY_INDEX_YES
|
||||
*/
|
||||
private static final String KEY_SHOW_GALLERY_INDEX = "pn";
|
||||
/**
|
||||
* The key of the IP:Port of a proxy-enabled Hentai@Home Client
|
||||
* to load all images
|
||||
*/
|
||||
private static final String KEY_HAH_CLIENT_IP_PORT = "hp";
|
||||
/**
|
||||
* The key of the passkey of a proxy-enabled Hentai@Home Client
|
||||
* to load all images
|
||||
*/
|
||||
private static final String KEY_HAH_CLIENT_PASSKEY = "hk";
|
||||
/**
|
||||
* The key of enable Tag Flagging
|
||||
*
|
||||
* @see #ENABLE_TAG_FLAGGING_NO
|
||||
* @see #ENABLE_TAG_FLAGGING_YES
|
||||
*/
|
||||
private static final String KEY_ENABLE_TAG_FLAGGING = "tf";
|
||||
/**
|
||||
* The key of always display the original images instead of the resampled versions
|
||||
*
|
||||
* @see #ALWAYS_ORIGINAL_NO
|
||||
* @see #ALWAYS_ORIGINAL_YES
|
||||
*/
|
||||
private static final String KEY_ALWAYS_ORIGINAL = "oi";
|
||||
/**
|
||||
* The key of enable the multi-Page Viewer
|
||||
*
|
||||
* @see #MULTI_PAGE_NO
|
||||
* @see #MULTI_PAGE_YES
|
||||
*/
|
||||
private static final String KEY_MULTI_PAGE = "qb";
|
||||
/**
|
||||
* The key of multi-Page Viewer Display Style
|
||||
*
|
||||
* @see #MULTI_PAGE_STYLE_C
|
||||
* @see #MULTI_PAGE_STYLE_N
|
||||
* @see #MULTI_PAGE_STYLE_Y
|
||||
*/
|
||||
private static final String KEY_MULTI_PAGE_STYLE = "ms";
|
||||
/**
|
||||
* The key of multi-Page Viewer Thumbnail Pane
|
||||
*
|
||||
* @see #MULTI_PAGE_THUMB_HIDE
|
||||
* @see #MULTI_PAGE_THUMB_SHOW
|
||||
*/
|
||||
private static final String KEY_MULTI_PAGE_THUMB = "mt";
|
||||
/**
|
||||
* Default gallery title
|
||||
*/
|
||||
private static final String GALLERY_TITLE_DEFAULT = "r";
|
||||
/**
|
||||
* Japanese gallery title
|
||||
*/
|
||||
private static final String GALLERY_TITLE_JAPANESE = "j";
|
||||
/**
|
||||
* Show popular
|
||||
*/
|
||||
private static final String POPULAR_YES = "y";
|
||||
/**
|
||||
* Don't show popular
|
||||
*/
|
||||
private static final String POPULAR_NO = "n";
|
||||
/**
|
||||
* Sort favorites by last gallery update time
|
||||
*/
|
||||
private static final String FAVORITES_SORT_GALLERY_UPDATE_TIME = "p";
|
||||
/**
|
||||
* Sort favorites by favorited time
|
||||
*/
|
||||
private static final String FAVORITES_SORT_FAVORITED_TIME = "f";
|
||||
/**
|
||||
* Load images through the Hentai@Home Network<br/>
|
||||
* key: {@link #KEY_LOAD_FROM_HAH}<br/>
|
||||
@@ -538,7 +526,7 @@ public class EhConfig implements Cloneable {
|
||||
* Image Size<br/>
|
||||
* key: {@link #KEY_IMAGE_SIZE}<br/>
|
||||
* value: {@link #IMAGE_SIZE_AUTO}, {@link #IMAGE_SIZE_780X}, {@link #IMAGE_SIZE_980X},
|
||||
* {@link #IMAGE_SIZE_1280X}, {@link #IMAGE_SIZE_1600X}, {@link #IMAGE_SIZE_2400X}
|
||||
* {@link #IMAGE_SIZE_1280X}, {@link #IMAGE_SIZE_1600X}, {@link #IMAGE_SIZE_2400X}
|
||||
*/
|
||||
public String imageSize = IMAGE_SIZE_AUTO;
|
||||
|
||||
|
||||
@@ -46,18 +46,8 @@ public class EhCookieStore extends CookieRepository {
|
||||
super(context, "okhttp3-cookie.db");
|
||||
}
|
||||
|
||||
public void signOut() {
|
||||
clear();
|
||||
}
|
||||
|
||||
public boolean hasSignedIn() {
|
||||
HttpUrl url = HttpUrl.parse(EhUrl.HOST_E);
|
||||
return contains(url, KEY_IPD_MEMBER_ID) &&
|
||||
contains(url, KEY_IPD_PASS_HASH);
|
||||
}
|
||||
|
||||
public static Cookie newCookie(Cookie cookie, String newDomain, boolean forcePersistent,
|
||||
boolean forceLongLive, boolean forceNotHostOnly) {
|
||||
boolean forceLongLive, boolean forceNotHostOnly) {
|
||||
Cookie.Builder builder = new Cookie.Builder();
|
||||
builder.name(cookie.name());
|
||||
builder.value(cookie.value());
|
||||
@@ -84,6 +74,16 @@ public class EhCookieStore extends CookieRepository {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public void signOut() {
|
||||
clear();
|
||||
}
|
||||
|
||||
public boolean hasSignedIn() {
|
||||
HttpUrl url = HttpUrl.parse(EhUrl.HOST_E);
|
||||
return contains(url, KEY_IPD_MEMBER_ID) &&
|
||||
contains(url, KEY_IPD_PASS_HASH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Cookie> loadForRequest(HttpUrl url) {
|
||||
List<Cookie> cookies = super.loadForRequest(url);
|
||||
@@ -93,7 +93,7 @@ public class EhCookieStore extends CookieRepository {
|
||||
if (checkTips) {
|
||||
List<Cookie> result = new ArrayList<>(cookies.size() + 1);
|
||||
// Add all but skip some
|
||||
for (Cookie cookie: cookies) {
|
||||
for (Cookie cookie : cookies) {
|
||||
String name = cookie.name();
|
||||
if (EhConfig.KEY_CONTENT_WARNING.equals(name)) {
|
||||
continue;
|
||||
|
||||
@@ -21,9 +21,11 @@ package com.hippo.ehviewer.client;
|
||||
*/
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.Hosts;
|
||||
import com.hippo.ehviewer.Settings;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Arrays;
|
||||
@@ -31,60 +33,61 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import okhttp3.Dns;
|
||||
|
||||
public class EhDns implements Dns {
|
||||
|
||||
private static final Map<String, InetAddress> builtInHosts;
|
||||
private static final Map<String, InetAddress> builtInHosts;
|
||||
|
||||
static {
|
||||
Map<String, InetAddress> map = new HashMap<>();
|
||||
put(map, "e-hentai.org", "104.20.26.25");
|
||||
put(map, "repo.e-hentai.org", "94.100.29.73");
|
||||
put(map, "forums.e-hentai.org", "94.100.18.243");
|
||||
put(map, "ehgt.org", "81.171.14.118");
|
||||
put(map, "ul.ehgt.org", "94.100.24.82");
|
||||
put(map, "github.com", "192.30.255.112");
|
||||
put(map, "raw.githubusercontent.com", "151.101.0.133");
|
||||
builtInHosts = map;
|
||||
}
|
||||
|
||||
private static void put(Map<String, InetAddress> map, String host, String ip) {
|
||||
InetAddress address = Hosts.toInetAddress(host, ip);
|
||||
if (address != null) {
|
||||
map.put(host, address);
|
||||
}
|
||||
}
|
||||
|
||||
private final Hosts hosts;
|
||||
|
||||
public EhDns(Context context) {
|
||||
hosts = EhApplication.getHosts(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InetAddress> lookup(String hostname) throws UnknownHostException {
|
||||
if (hostname == null) throw new UnknownHostException("hostname == null");
|
||||
|
||||
InetAddress inetAddress = hosts.get(hostname);
|
||||
if (inetAddress != null) {
|
||||
return Collections.singletonList(inetAddress);
|
||||
static {
|
||||
Map<String, InetAddress> map = new HashMap<>();
|
||||
put(map, "e-hentai.org", "104.20.26.25");
|
||||
put(map, "repo.e-hentai.org", "94.100.29.73");
|
||||
put(map, "forums.e-hentai.org", "94.100.18.243");
|
||||
put(map, "ehgt.org", "81.171.14.118");
|
||||
put(map, "ul.ehgt.org", "94.100.24.82");
|
||||
put(map, "github.com", "192.30.255.112");
|
||||
put(map, "raw.githubusercontent.com", "151.101.0.133");
|
||||
builtInHosts = map;
|
||||
}
|
||||
|
||||
if (Settings.getBuiltInHosts()) {
|
||||
inetAddress = builtInHosts.get(hostname);
|
||||
if (inetAddress != null) {
|
||||
return Collections.singletonList(inetAddress);
|
||||
}
|
||||
private final Hosts hosts;
|
||||
|
||||
public EhDns(Context context) {
|
||||
hosts = EhApplication.getHosts(context);
|
||||
}
|
||||
|
||||
try {
|
||||
return Arrays.asList(InetAddress.getAllByName(hostname));
|
||||
} catch (NullPointerException e) {
|
||||
UnknownHostException unknownHostException =
|
||||
new UnknownHostException("Broken system behaviour for dns lookup of " + hostname);
|
||||
unknownHostException.initCause(e);
|
||||
throw unknownHostException;
|
||||
private static void put(Map<String, InetAddress> map, String host, String ip) {
|
||||
InetAddress address = Hosts.toInetAddress(host, ip);
|
||||
if (address != null) {
|
||||
map.put(host, address);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InetAddress> lookup(String hostname) throws UnknownHostException {
|
||||
if (hostname == null) throw new UnknownHostException("hostname == null");
|
||||
|
||||
InetAddress inetAddress = hosts.get(hostname);
|
||||
if (inetAddress != null) {
|
||||
return Collections.singletonList(inetAddress);
|
||||
}
|
||||
|
||||
if (Settings.getBuiltInHosts()) {
|
||||
inetAddress = builtInHosts.get(hostname);
|
||||
if (inetAddress != null) {
|
||||
return Collections.singletonList(inetAddress);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return Arrays.asList(InetAddress.getAllByName(hostname));
|
||||
} catch (NullPointerException e) {
|
||||
UnknownHostException unknownHostException =
|
||||
new UnknownHostException("Broken system behaviour for dns lookup of " + hostname);
|
||||
unknownHostException.initCause(e);
|
||||
throw unknownHostException;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,9 @@ package com.hippo.ehviewer.client;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.ehviewer.AppConfig;
|
||||
import com.hippo.ehviewer.GetText;
|
||||
import com.hippo.ehviewer.R;
|
||||
@@ -49,11 +51,19 @@ import com.hippo.ehviewer.client.parser.VoteCommentParser;
|
||||
import com.hippo.network.StatusCodeException;
|
||||
import com.hippo.util.ExceptionUtils;
|
||||
import com.hippo.yorozuya.AssertUtils;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.select.Elements;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.FormBody;
|
||||
import okhttp3.Headers;
|
||||
@@ -63,23 +73,15 @@ import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.select.Elements;
|
||||
|
||||
public class EhEngine {
|
||||
|
||||
public static final MediaType MEDIA_TYPE_JSON = MediaType.parse("application/json; charset=utf-8");
|
||||
private static final String TAG = EhEngine.class.getSimpleName();
|
||||
|
||||
private static final String SAD_PANDA_DISPOSITION = "inline; filename=\"sadpanda.jpg\"";
|
||||
private static final String SAD_PANDA_TYPE = "image/gif";
|
||||
private static final String SAD_PANDA_LENGTH = "9615";
|
||||
|
||||
private static final String KOKOMADE_URL = "https://exhentai.org/img/kokomade.jpg";
|
||||
|
||||
public static final MediaType MEDIA_TYPE_JSON = MediaType.parse("application/json; charset=utf-8");
|
||||
private static final MediaType MEDIA_TYPE_JPEG = MediaType.parse("image/jpeg");
|
||||
|
||||
private static final Pattern PATTERN_NEED_HATH_CLIENT = Pattern.compile("(You must have a H@H client assigned to your account to use this feature\\.)");
|
||||
@@ -91,7 +93,7 @@ public class EhEngine {
|
||||
}
|
||||
|
||||
private static void doThrowException(Call call, int code, @Nullable Headers headers,
|
||||
@Nullable String body, Throwable e) throws Throwable {
|
||||
@Nullable String body, Throwable e) throws Throwable {
|
||||
if (call.isCanceled()) {
|
||||
throw new CancelledException();
|
||||
}
|
||||
@@ -131,7 +133,7 @@ public class EhEngine {
|
||||
}
|
||||
|
||||
private static void throwException(Call call, int code, @Nullable Headers headers,
|
||||
@Nullable String body, Throwable e) throws Throwable {
|
||||
@Nullable String body, Throwable e) throws Throwable {
|
||||
try {
|
||||
doThrowException(call, code, headers, body, e);
|
||||
} catch (Throwable error) {
|
||||
@@ -141,7 +143,7 @@ public class EhEngine {
|
||||
}
|
||||
|
||||
public static String signIn(@Nullable EhClient.Task task, OkHttpClient okHttpClient,
|
||||
String username, String password) throws Throwable {
|
||||
String username, String password) throws Throwable {
|
||||
FormBody.Builder builder = new FormBody.Builder()
|
||||
.add("UserName", username)
|
||||
.add("PassWord", password)
|
||||
@@ -232,7 +234,7 @@ public class EhEngine {
|
||||
}
|
||||
|
||||
public static GalleryListParser.Result getGalleryList(@Nullable EhClient.Task task, OkHttpClient okHttpClient,
|
||||
String url) throws Throwable {
|
||||
String url) throws Throwable {
|
||||
String referer = EhUrl.getReferer();
|
||||
Log.d(TAG, url);
|
||||
Request request = new EhRequestBuilder(url, referer).build();
|
||||
@@ -266,7 +268,7 @@ public class EhEngine {
|
||||
|
||||
// At least, GalleryInfo contain valid gid and token
|
||||
public static List<GalleryInfo> fillGalleryListByApi(@Nullable EhClient.Task task, OkHttpClient okHttpClient,
|
||||
List<GalleryInfo> galleryInfoList, String referer) throws Throwable {
|
||||
List<GalleryInfo> galleryInfoList, String referer) throws Throwable {
|
||||
// We can only request 25 items one time at most
|
||||
final int MAX_REQUEST_SIZE = 25;
|
||||
List<GalleryInfo> requestItems = new ArrayList<>(MAX_REQUEST_SIZE);
|
||||
@@ -281,7 +283,7 @@ public class EhEngine {
|
||||
}
|
||||
|
||||
private static void doFillGalleryListByApi(@Nullable EhClient.Task task, OkHttpClient okHttpClient,
|
||||
List<GalleryInfo> galleryInfoList, String referer) throws Throwable {
|
||||
List<GalleryInfo> galleryInfoList, String referer) throws Throwable {
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("method", "gdata");
|
||||
JSONArray ja = new JSONArray();
|
||||
@@ -324,7 +326,7 @@ public class EhEngine {
|
||||
}
|
||||
|
||||
public static GalleryDetail getGalleryDetail(@Nullable EhClient.Task task, OkHttpClient okHttpClient,
|
||||
String url) throws Throwable {
|
||||
String url) throws Throwable {
|
||||
String referer = EhUrl.getReferer();
|
||||
Log.d(TAG, url);
|
||||
Request request = new EhRequestBuilder(url, referer).build();
|
||||
@@ -382,8 +384,8 @@ public class EhEngine {
|
||||
}
|
||||
|
||||
public static RateGalleryParser.Result rateGallery(@Nullable EhClient.Task task,
|
||||
OkHttpClient okHttpClient, long apiUid, String apiKey, long gid,
|
||||
String token, float rating) throws Throwable {
|
||||
OkHttpClient okHttpClient, long apiUid, String apiKey, long gid,
|
||||
String token, float rating) throws Throwable {
|
||||
final JSONObject json = new JSONObject();
|
||||
json.put("method", "rategallery");
|
||||
json.put("apiuid", apiUid);
|
||||
@@ -423,7 +425,7 @@ public class EhEngine {
|
||||
}
|
||||
|
||||
public static GalleryCommentList commentGallery(@Nullable EhClient.Task task,
|
||||
OkHttpClient okHttpClient, String url, String comment, String id) throws Throwable {
|
||||
OkHttpClient okHttpClient, String url, String comment, String id) throws Throwable {
|
||||
FormBody.Builder builder = new FormBody.Builder();
|
||||
if (id == null) {
|
||||
builder.add("commenttext_new", comment);
|
||||
@@ -467,7 +469,7 @@ public class EhEngine {
|
||||
}
|
||||
|
||||
public static String getGalleryToken(@Nullable EhClient.Task task, OkHttpClient okHttpClient,
|
||||
long gid, String gtoken, int page) throws Throwable {
|
||||
long gid, String gtoken, int page) throws Throwable {
|
||||
JSONObject json = new JSONObject()
|
||||
.put("method", "gtoken")
|
||||
.put("pagelist", new JSONArray().put(
|
||||
@@ -504,7 +506,7 @@ public class EhEngine {
|
||||
}
|
||||
|
||||
public static FavoritesParser.Result getFavorites(@Nullable EhClient.Task task, OkHttpClient okHttpClient,
|
||||
String url, boolean callApi) throws Throwable {
|
||||
String url, boolean callApi) throws Throwable {
|
||||
String referer = EhUrl.getReferer();
|
||||
Log.d(TAG, url);
|
||||
Request request = new EhRequestBuilder(url, referer).build();
|
||||
@@ -538,10 +540,10 @@ public class EhEngine {
|
||||
|
||||
/**
|
||||
* @param dstCat -1 for delete, 0 - 9 for cloud favorite, others throw Exception
|
||||
* @param note max 250 characters
|
||||
* @param note max 250 characters
|
||||
*/
|
||||
public static Void addFavorites(@Nullable EhClient.Task task, OkHttpClient okHttpClient,
|
||||
long gid, String token, int dstCat, String note) throws Throwable {
|
||||
long gid, String token, int dstCat, String note) throws Throwable {
|
||||
String catStr;
|
||||
if (dstCat == -1) {
|
||||
catStr = "favdel";
|
||||
@@ -588,7 +590,7 @@ public class EhEngine {
|
||||
}
|
||||
|
||||
public static Void addFavoritesRange(@Nullable EhClient.Task task, OkHttpClient okHttpClient,
|
||||
long[] gidArray, String[] tokenArray, int dstCat) throws Throwable {
|
||||
long[] gidArray, String[] tokenArray, int dstCat) throws Throwable {
|
||||
AssertUtils.assertEquals(gidArray.length, tokenArray.length);
|
||||
for (int i = 0, n = gidArray.length; i < n; i++) {
|
||||
addFavorites(task, okHttpClient, gidArray[i], tokenArray[i], dstCat, null);
|
||||
@@ -597,7 +599,7 @@ public class EhEngine {
|
||||
}
|
||||
|
||||
public static FavoritesParser.Result modifyFavorites(@Nullable EhClient.Task task, OkHttpClient okHttpClient,
|
||||
String url, long[] gidArray, int dstCat, boolean callApi) throws Throwable {
|
||||
String url, long[] gidArray, int dstCat, boolean callApi) throws Throwable {
|
||||
String catStr;
|
||||
if (dstCat == -1) {
|
||||
catStr = "delete";
|
||||
@@ -646,7 +648,7 @@ public class EhEngine {
|
||||
}
|
||||
|
||||
public static Pair<String, String>[] getTorrentList(@Nullable EhClient.Task task, OkHttpClient okHttpClient,
|
||||
String url, long gid, String token) throws Throwable {
|
||||
String url, long gid, String token) throws Throwable {
|
||||
String referer = EhUrl.getGalleryDetailUrl(gid, token);
|
||||
Log.d(TAG, url);
|
||||
Request request = new EhRequestBuilder(url, referer).build();
|
||||
@@ -677,7 +679,7 @@ public class EhEngine {
|
||||
}
|
||||
|
||||
public static Pair<String, Pair<String, String>[]> getArchiveList(@Nullable EhClient.Task task, OkHttpClient okHttpClient,
|
||||
String url, long gid, String token) throws Throwable {
|
||||
String url, long gid, String token) throws Throwable {
|
||||
String referer = EhUrl.getGalleryDetailUrl(gid, token);
|
||||
Log.d(TAG, url);
|
||||
Request request = new EhRequestBuilder(url, referer).build();
|
||||
@@ -708,7 +710,7 @@ public class EhEngine {
|
||||
}
|
||||
|
||||
public static Void downloadArchive(@Nullable EhClient.Task task, OkHttpClient okHttpClient,
|
||||
long gid, String token, String or, String res) throws Throwable {
|
||||
long gid, String token, String or, String res) throws Throwable {
|
||||
if (or == null || or.length() == 0) {
|
||||
throw new EhException("Invalid form param or: " + or);
|
||||
}
|
||||
@@ -755,7 +757,7 @@ public class EhEngine {
|
||||
}
|
||||
|
||||
private static ProfileParser.Result getProfileInternal(@Nullable EhClient.Task task,
|
||||
OkHttpClient okHttpClient, String url, String referer) throws Throwable {
|
||||
OkHttpClient okHttpClient, String url, String referer) throws Throwable {
|
||||
Log.d(TAG, url);
|
||||
Request request = new EhRequestBuilder(url, referer).build();
|
||||
Call call = okHttpClient.newCall(request);
|
||||
@@ -782,7 +784,7 @@ public class EhEngine {
|
||||
}
|
||||
|
||||
public static ProfileParser.Result getProfile(@Nullable EhClient.Task task,
|
||||
OkHttpClient okHttpClient) throws Throwable {
|
||||
OkHttpClient okHttpClient) throws Throwable {
|
||||
String url = EhUrl.URL_FORUMS;
|
||||
Log.d(TAG, url);
|
||||
Request request = new EhRequestBuilder(url, null).build();
|
||||
@@ -810,7 +812,7 @@ public class EhEngine {
|
||||
}
|
||||
|
||||
public static VoteCommentParser.Result voteComment(@Nullable EhClient.Task task, OkHttpClient okHttpClient,
|
||||
long apiUid, String apiKey, long gid, String token, long commentId, int commentVote) throws Throwable {
|
||||
long apiUid, String apiKey, long gid, String token, long commentId, int commentVote) throws Throwable {
|
||||
final JSONObject json = new JSONObject();
|
||||
json.put("method", "votecomment");
|
||||
json.put("apiuid", apiUid);
|
||||
@@ -854,7 +856,7 @@ public class EhEngine {
|
||||
* @param image Must be jpeg
|
||||
*/
|
||||
public static GalleryListParser.Result imageSearch(@Nullable EhClient.Task task, OkHttpClient okHttpClient,
|
||||
File image, boolean uss, boolean osc, boolean se) throws Throwable {
|
||||
File image, boolean uss, boolean osc, boolean se) throws Throwable {
|
||||
MultipartBody.Builder builder = new MultipartBody.Builder();
|
||||
builder.setType(MultipartBody.FORM);
|
||||
builder.addPart(
|
||||
@@ -922,7 +924,7 @@ public class EhEngine {
|
||||
}
|
||||
|
||||
public static GalleryPageParser.Result getGalleryPage(@Nullable EhClient.Task task,
|
||||
OkHttpClient okHttpClient, String url, long gid, String token) throws Throwable {
|
||||
OkHttpClient okHttpClient, String url, long gid, String token) throws Throwable {
|
||||
String referer = EhUrl.getGalleryDetailUrl(gid, token);
|
||||
Log.d(TAG, url);
|
||||
Request request = new EhRequestBuilder(url, referer).build();
|
||||
@@ -950,7 +952,7 @@ public class EhEngine {
|
||||
}
|
||||
|
||||
public static GalleryPageApiParser.Result getGalleryPageApi(@Nullable EhClient.Task task,
|
||||
OkHttpClient okHttpClient, long gid, int index, String pToken, String showKey, String previousPToken) throws Throwable {
|
||||
OkHttpClient okHttpClient, long gid, int index, String pToken, String showKey, String previousPToken) throws Throwable {
|
||||
final JSONObject json = new JSONObject();
|
||||
json.put("method", "showpage");
|
||||
json.put("gid", gid);
|
||||
@@ -966,8 +968,8 @@ public class EhEngine {
|
||||
String origin = EhUrl.getOrigin();
|
||||
Log.d(TAG, url);
|
||||
Request request = new EhRequestBuilder(url, referer, origin)
|
||||
.post(requestBody)
|
||||
.build();
|
||||
.post(requestBody)
|
||||
.build();
|
||||
Call call = okHttpClient.newCall(request);
|
||||
|
||||
// Put call
|
||||
|
||||
@@ -27,27 +27,17 @@ import java.util.List;
|
||||
|
||||
public final class EhFilter {
|
||||
|
||||
private static final String TAG = EhFilter.class.getSimpleName();
|
||||
|
||||
public static final int MODE_TITLE = 0;
|
||||
public static final int MODE_UPLOADER = 1;
|
||||
public static final int MODE_TAG = 2;
|
||||
public static final int MODE_TAG_NAMESPACE = 3;
|
||||
|
||||
private static final String TAG = EhFilter.class.getSimpleName();
|
||||
private static EhFilter sInstance;
|
||||
private final List<Filter> mTitleFilterList = new ArrayList<>();
|
||||
private final List<Filter> mUploaderFilterList = new ArrayList<>();
|
||||
private final List<Filter> mTagFilterList = new ArrayList<>();
|
||||
private final List<Filter> mTagNamespaceFilterList = new ArrayList<>();
|
||||
|
||||
private static EhFilter sInstance;
|
||||
|
||||
public static EhFilter getInstance() {
|
||||
if (sInstance == null) {
|
||||
sInstance = new EhFilter();
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
private EhFilter() {
|
||||
List<Filter> list = EhDB.getAllFilter();
|
||||
for (int i = 0, n = list.size(); i < n; i++) {
|
||||
@@ -75,6 +65,13 @@ public final class EhFilter {
|
||||
}
|
||||
}
|
||||
|
||||
public static EhFilter getInstance() {
|
||||
if (sInstance == null) {
|
||||
sInstance = new EhFilter();
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
public List<Filter> getTitleFilterList() {
|
||||
return mTitleFilterList;
|
||||
}
|
||||
@@ -216,11 +213,7 @@ public final class EhFilter {
|
||||
!tagNamespace.equals(filterNamespace)) {
|
||||
return false;
|
||||
}
|
||||
if (!tagName.equals(filterName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return tagName.equals(filterName);
|
||||
}
|
||||
|
||||
public synchronized boolean filterTag(GalleryInfo info) {
|
||||
@@ -232,7 +225,7 @@ public final class EhFilter {
|
||||
String[] tags = info.simpleTags;
|
||||
List<Filter> filters = mTagFilterList;
|
||||
if (null != tags && filters.size() > 0) {
|
||||
for (String tag: tags) {
|
||||
for (String tag : tags) {
|
||||
for (int i = 0, n = filters.size(); i < n; i++) {
|
||||
if (filters.get(i).enable && matchTag(tag, filters.get(i).text)) {
|
||||
return false;
|
||||
@@ -267,7 +260,7 @@ public final class EhFilter {
|
||||
String[] tags = info.simpleTags;
|
||||
List<Filter> filters = mTagNamespaceFilterList;
|
||||
if (null != tags && filters.size() > 0) {
|
||||
for (String tag: tags) {
|
||||
for (String tag : tags) {
|
||||
for (int i = 0, n = filters.size(); i < n; i++) {
|
||||
if (filters.get(i).enable && matchTagNamespace(tag, filters.get(i).text)) {
|
||||
return false;
|
||||
|
||||
@@ -20,51 +20,49 @@ import com.hippo.ehviewer.Settings;
|
||||
|
||||
public class EhRequest {
|
||||
|
||||
EhClient.Task task;
|
||||
private int mMethod;
|
||||
private Object[] mArgs;
|
||||
private EhClient.Callback mCallback;
|
||||
private EhConfig mEhConfig;
|
||||
|
||||
EhClient.Task task;
|
||||
|
||||
private boolean mCancel = false;
|
||||
|
||||
public int getMethod() {
|
||||
return mMethod;
|
||||
}
|
||||
|
||||
public EhRequest setMethod(int method) {
|
||||
mMethod = method;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Object[] getArgs() {
|
||||
return mArgs;
|
||||
}
|
||||
|
||||
public EhRequest setArgs(Object... args) {
|
||||
mArgs = args;
|
||||
return this;
|
||||
}
|
||||
|
||||
public EhClient.Callback getCallback() {
|
||||
return mCallback;
|
||||
}
|
||||
|
||||
public EhRequest setCallback(EhClient.Callback callback) {
|
||||
mCallback = callback;
|
||||
return this;
|
||||
}
|
||||
|
||||
public EhConfig getEhConfig() {
|
||||
return mEhConfig != null ? mEhConfig : Settings.getEhConfig();
|
||||
}
|
||||
|
||||
public EhRequest setEhConfig(EhConfig ehConfig) {
|
||||
mEhConfig = ehConfig;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getMethod() {
|
||||
return mMethod;
|
||||
}
|
||||
|
||||
public Object[] getArgs() {
|
||||
return mArgs;
|
||||
}
|
||||
|
||||
public EhClient.Callback getCallback() {
|
||||
return mCallback;
|
||||
}
|
||||
|
||||
public EhConfig getEhConfig() {
|
||||
return mEhConfig != null ? mEhConfig : Settings.getEhConfig();
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
if (!mCancel) {
|
||||
mCancel = true;
|
||||
|
||||
@@ -18,7 +18,9 @@ package com.hippo.ehviewer.client;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Base64;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.ehviewer.AppConfig;
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.R;
|
||||
@@ -27,6 +29,7 @@ import com.hippo.util.IoThreadPoolExecutor;
|
||||
import com.hippo.util.TextUrl;
|
||||
import com.hippo.yorozuya.FileUtils;
|
||||
import com.hippo.yorozuya.IOUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
@@ -39,6 +42,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
@@ -49,309 +53,307 @@ import okio.Okio;
|
||||
|
||||
public class EhTagDatabase {
|
||||
|
||||
private final String name;
|
||||
private final byte[] tags;
|
||||
private static final Map<String, String> NAMESPACE_TO_PREFIX = new HashMap<>();
|
||||
private static volatile EhTagDatabase instance;
|
||||
// TODO more lock for different language
|
||||
private static Lock lock = new ReentrantLock();
|
||||
|
||||
public EhTagDatabase(String name, BufferedSource source) throws IOException {
|
||||
this.name = name;
|
||||
int totalBytes = source.readInt();
|
||||
tags = new byte[totalBytes];
|
||||
source.readFully(tags);
|
||||
}
|
||||
static {
|
||||
NAMESPACE_TO_PREFIX.put("artist", "a:");
|
||||
NAMESPACE_TO_PREFIX.put("character", "c:");
|
||||
NAMESPACE_TO_PREFIX.put("female", "f:");
|
||||
NAMESPACE_TO_PREFIX.put("group", "g:");
|
||||
NAMESPACE_TO_PREFIX.put("language", "l:");
|
||||
NAMESPACE_TO_PREFIX.put("male", "m:");
|
||||
NAMESPACE_TO_PREFIX.put("misc", "");
|
||||
NAMESPACE_TO_PREFIX.put("parody", "p:");
|
||||
NAMESPACE_TO_PREFIX.put("reclass", "r:");
|
||||
}
|
||||
|
||||
public String getTranslation(String tag) {
|
||||
return search(tags, tag.getBytes(TextUrl.UTF_8));
|
||||
}
|
||||
private final String name;
|
||||
private final byte[] tags;
|
||||
|
||||
@Nullable
|
||||
private String search(byte[] tags, byte[] tag) {
|
||||
int low = 0;
|
||||
int high = tags.length;
|
||||
while (low < high) {
|
||||
int start = (low + high) / 2;
|
||||
// Look for the starting '\n'
|
||||
while (start > -1 && tags[start] != '\n') {
|
||||
start--;
|
||||
}
|
||||
start++;
|
||||
public EhTagDatabase(String name, BufferedSource source) throws IOException {
|
||||
this.name = name;
|
||||
int totalBytes = source.readInt();
|
||||
tags = new byte[totalBytes];
|
||||
source.readFully(tags);
|
||||
}
|
||||
|
||||
// Look for the middle '\r'.
|
||||
int middle = 1;
|
||||
while (tags[start + middle] != '\r') {
|
||||
middle++;
|
||||
}
|
||||
|
||||
// Look for the ending '\n'
|
||||
int end = middle + 1;
|
||||
while (tags[start + end] != '\n') {
|
||||
end++;
|
||||
}
|
||||
|
||||
int compare;
|
||||
int tagIndex = 0;
|
||||
int curIndex = start;
|
||||
|
||||
for (;;) {
|
||||
int tagByte = tag[tagIndex] & 0xff;
|
||||
int curByte = tags[curIndex] & 0xff;
|
||||
compare = tagByte - curByte;
|
||||
if (compare != 0) {
|
||||
break;
|
||||
@Nullable
|
||||
public static EhTagDatabase getInstance(Context context) {
|
||||
if (isPossible(context)) {
|
||||
return instance;
|
||||
} else {
|
||||
instance = null;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
tagIndex++;
|
||||
curIndex++;
|
||||
if (tagIndex == tag.length && curIndex == start + middle) {
|
||||
break;
|
||||
@Nullable
|
||||
public static String namespaceToPrefix(String namespace) {
|
||||
return NAMESPACE_TO_PREFIX.get(namespace);
|
||||
}
|
||||
|
||||
private static String[] getMetadata(Context context) {
|
||||
String[] metadata = context.getResources().getStringArray(R.array.tag_translation_metadata);
|
||||
if (metadata.length == 4) {
|
||||
return metadata;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
if (tagIndex == tag.length) {
|
||||
compare = -1;
|
||||
break;
|
||||
}
|
||||
if (curIndex == start + middle) {
|
||||
compare = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (compare < 0) {
|
||||
high = start - 1;
|
||||
} else if (compare > 0) {
|
||||
low = start + end + 1;
|
||||
} else {
|
||||
byte[] bytes = Base64.decode(tags, start + middle + 1, end - middle - 1, Base64.DEFAULT);
|
||||
return new String(bytes, TextUrl.UTF_8);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private static final Map<String, String> NAMESPACE_TO_PREFIX = new HashMap<>();
|
||||
|
||||
static {
|
||||
NAMESPACE_TO_PREFIX.put("artist", "a:");
|
||||
NAMESPACE_TO_PREFIX.put("character", "c:");
|
||||
NAMESPACE_TO_PREFIX.put("female", "f:");
|
||||
NAMESPACE_TO_PREFIX.put("group", "g:");
|
||||
NAMESPACE_TO_PREFIX.put("language", "l:");
|
||||
NAMESPACE_TO_PREFIX.put("male", "m:");
|
||||
NAMESPACE_TO_PREFIX.put("misc", "");
|
||||
NAMESPACE_TO_PREFIX.put("parody", "p:");
|
||||
NAMESPACE_TO_PREFIX.put("reclass", "r:");
|
||||
}
|
||||
|
||||
private static volatile EhTagDatabase instance;
|
||||
// TODO more lock for different language
|
||||
private static Lock lock = new ReentrantLock();
|
||||
|
||||
@Nullable
|
||||
public static EhTagDatabase getInstance(Context context) {
|
||||
if (isPossible(context)) {
|
||||
return instance;
|
||||
} else {
|
||||
instance = null;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String namespaceToPrefix(String namespace) {
|
||||
return NAMESPACE_TO_PREFIX.get(namespace);
|
||||
}
|
||||
|
||||
private static String[] getMetadata(Context context) {
|
||||
String[] metadata = context.getResources().getStringArray(R.array.tag_translation_metadata);
|
||||
if (metadata.length == 4) {
|
||||
return metadata;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isPossible(Context context) {
|
||||
return getMetadata(context) != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static byte[] getFileContent(File file, int length) {
|
||||
try (BufferedSource source = Okio.buffer(Okio.source(file))) {
|
||||
byte[] content = new byte[length];
|
||||
source.readFully(content);
|
||||
return content;
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static byte[] getFileSha1(File file) {
|
||||
try (InputStream is = new FileInputStream(file)) {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
||||
int n;
|
||||
byte[] buffer = new byte[4 * 1024];
|
||||
while ((n = is.read(buffer)) != -1) {
|
||||
digest.update(buffer, 0, n);
|
||||
}
|
||||
return digest.digest();
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean equals(byte[] b1, byte[] b2) {
|
||||
if (b1 == null && b2 == null) {
|
||||
return true;
|
||||
}
|
||||
if (b1 == null || b2 == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (b1.length != b2.length) {
|
||||
return false;
|
||||
public static boolean isPossible(Context context) {
|
||||
return getMetadata(context) != null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < b1.length; i++) {
|
||||
if (b1[i] != b2[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean checkData(File sha1File, File dataFile) {
|
||||
byte[] s1 = getFileContent(sha1File, 20);
|
||||
if (s1 == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] s2 = getFileSha1(dataFile);
|
||||
if (s2 == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return equals(s1, s2);
|
||||
}
|
||||
|
||||
private static boolean save(OkHttpClient client, String url, File file) {
|
||||
Request request = new Request.Builder().url(url).build();
|
||||
Call call = client.newCall(request);
|
||||
try (Response response = call.execute()) {
|
||||
if (!response.isSuccessful()) {
|
||||
return false;
|
||||
}
|
||||
ResponseBody body = response.body();
|
||||
if (body == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try (InputStream is = body.byteStream(); OutputStream os = new FileOutputStream(file)) {
|
||||
IOUtils.copy(is, os);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (Throwable t) {
|
||||
ExceptionUtils.throwIfFatal(t);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void update(Context context) {
|
||||
String[] urls = getMetadata(context);
|
||||
if (urls == null || urls.length != 4) {
|
||||
// Clear tags if it's not possible
|
||||
instance = null;
|
||||
return;
|
||||
}
|
||||
|
||||
String sha1Name = urls[0];
|
||||
String sha1Url = urls[1];
|
||||
String dataName = urls[2];
|
||||
String dataUrl = urls[3];
|
||||
|
||||
// Clear tags if name if different
|
||||
EhTagDatabase tmp = instance;
|
||||
if (tmp != null && !tmp.name.equals(dataName)) {
|
||||
instance = null;
|
||||
}
|
||||
|
||||
IoThreadPoolExecutor.getInstance().execute(() -> {
|
||||
if (!lock.tryLock()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
File dir = AppConfig.getFilesDir("tag-translations");
|
||||
if (dir == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check current sha1 and current data
|
||||
File sha1File = new File(dir, sha1Name);
|
||||
File dataFile = new File(dir, dataName);
|
||||
if (!checkData(sha1File, dataFile)) {
|
||||
FileUtils.delete(sha1File);
|
||||
FileUtils.delete(dataFile);
|
||||
}
|
||||
|
||||
// Read current EhTagDatabase
|
||||
if (instance == null && dataFile.exists()) {
|
||||
try (BufferedSource source = Okio.buffer(Okio.source(dataFile))) {
|
||||
instance = new EhTagDatabase(dataName, source);
|
||||
} catch (IOException e) {
|
||||
FileUtils.delete(sha1File);
|
||||
FileUtils.delete(dataFile);
|
||||
}
|
||||
}
|
||||
|
||||
OkHttpClient client = EhApplication.getOkHttpClient(EhApplication.getInstance());
|
||||
|
||||
// Save new sha1
|
||||
File tempSha1File = new File(dir, sha1Name + ".tmp");
|
||||
if (!save(client, sha1Url, tempSha1File)) {
|
||||
FileUtils.delete(tempSha1File);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check new sha1 and current data
|
||||
if (checkData(tempSha1File, dataFile)) {
|
||||
// The data is the same
|
||||
FileUtils.delete(tempSha1File);
|
||||
return;
|
||||
}
|
||||
|
||||
// Save new data
|
||||
File tempDataFile = new File(dir, dataName + ".tmp");
|
||||
if (!save(client, dataUrl, tempDataFile)) {
|
||||
FileUtils.delete(tempDataFile);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check new sha1 and new data
|
||||
if (!checkData(tempSha1File, tempDataFile)) {
|
||||
FileUtils.delete(tempSha1File);
|
||||
FileUtils.delete(tempDataFile);
|
||||
return;
|
||||
}
|
||||
|
||||
// Replace current sha1 and current data with new sha1 and new data
|
||||
FileUtils.delete(sha1File);
|
||||
FileUtils.delete(dataFile);
|
||||
tempSha1File.renameTo(sha1File);
|
||||
tempDataFile.renameTo(dataFile);
|
||||
|
||||
// Read new EhTagDatabase
|
||||
try (BufferedSource source = Okio.buffer(Okio.source(dataFile))) {
|
||||
instance = new EhTagDatabase(dataName, source);
|
||||
@Nullable
|
||||
private static byte[] getFileContent(File file, int length) {
|
||||
try (BufferedSource source = Okio.buffer(Okio.source(file))) {
|
||||
byte[] content = new byte[length];
|
||||
source.readFully(content);
|
||||
return content;
|
||||
} catch (IOException e) {
|
||||
// Ignore
|
||||
return null;
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static byte[] getFileSha1(File file) {
|
||||
try (InputStream is = new FileInputStream(file)) {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
||||
int n;
|
||||
byte[] buffer = new byte[4 * 1024];
|
||||
while ((n = is.read(buffer)) != -1) {
|
||||
digest.update(buffer, 0, n);
|
||||
}
|
||||
return digest.digest();
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean equals(byte[] b1, byte[] b2) {
|
||||
if (b1 == null && b2 == null) {
|
||||
return true;
|
||||
}
|
||||
if (b1 == null || b2 == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (b1.length != b2.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < b1.length; i++) {
|
||||
if (b1[i] != b2[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean checkData(File sha1File, File dataFile) {
|
||||
byte[] s1 = getFileContent(sha1File, 20);
|
||||
if (s1 == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
byte[] s2 = getFileSha1(dataFile);
|
||||
if (s2 == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return equals(s1, s2);
|
||||
}
|
||||
|
||||
private static boolean save(OkHttpClient client, String url, File file) {
|
||||
Request request = new Request.Builder().url(url).build();
|
||||
Call call = client.newCall(request);
|
||||
try (Response response = call.execute()) {
|
||||
if (!response.isSuccessful()) {
|
||||
return false;
|
||||
}
|
||||
ResponseBody body = response.body();
|
||||
if (body == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try (InputStream is = body.byteStream(); OutputStream os = new FileOutputStream(file)) {
|
||||
IOUtils.copy(is, os);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (Throwable t) {
|
||||
ExceptionUtils.throwIfFatal(t);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void update(Context context) {
|
||||
String[] urls = getMetadata(context);
|
||||
if (urls == null || urls.length != 4) {
|
||||
// Clear tags if it's not possible
|
||||
instance = null;
|
||||
return;
|
||||
}
|
||||
|
||||
String sha1Name = urls[0];
|
||||
String sha1Url = urls[1];
|
||||
String dataName = urls[2];
|
||||
String dataUrl = urls[3];
|
||||
|
||||
// Clear tags if name if different
|
||||
EhTagDatabase tmp = instance;
|
||||
if (tmp != null && !tmp.name.equals(dataName)) {
|
||||
instance = null;
|
||||
}
|
||||
|
||||
IoThreadPoolExecutor.getInstance().execute(() -> {
|
||||
if (!lock.tryLock()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
File dir = AppConfig.getFilesDir("tag-translations");
|
||||
if (dir == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check current sha1 and current data
|
||||
File sha1File = new File(dir, sha1Name);
|
||||
File dataFile = new File(dir, dataName);
|
||||
if (!checkData(sha1File, dataFile)) {
|
||||
FileUtils.delete(sha1File);
|
||||
FileUtils.delete(dataFile);
|
||||
}
|
||||
|
||||
// Read current EhTagDatabase
|
||||
if (instance == null && dataFile.exists()) {
|
||||
try (BufferedSource source = Okio.buffer(Okio.source(dataFile))) {
|
||||
instance = new EhTagDatabase(dataName, source);
|
||||
} catch (IOException e) {
|
||||
FileUtils.delete(sha1File);
|
||||
FileUtils.delete(dataFile);
|
||||
}
|
||||
}
|
||||
|
||||
OkHttpClient client = EhApplication.getOkHttpClient(EhApplication.getInstance());
|
||||
|
||||
// Save new sha1
|
||||
File tempSha1File = new File(dir, sha1Name + ".tmp");
|
||||
if (!save(client, sha1Url, tempSha1File)) {
|
||||
FileUtils.delete(tempSha1File);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check new sha1 and current data
|
||||
if (checkData(tempSha1File, dataFile)) {
|
||||
// The data is the same
|
||||
FileUtils.delete(tempSha1File);
|
||||
return;
|
||||
}
|
||||
|
||||
// Save new data
|
||||
File tempDataFile = new File(dir, dataName + ".tmp");
|
||||
if (!save(client, dataUrl, tempDataFile)) {
|
||||
FileUtils.delete(tempDataFile);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check new sha1 and new data
|
||||
if (!checkData(tempSha1File, tempDataFile)) {
|
||||
FileUtils.delete(tempSha1File);
|
||||
FileUtils.delete(tempDataFile);
|
||||
return;
|
||||
}
|
||||
|
||||
// Replace current sha1 and current data with new sha1 and new data
|
||||
FileUtils.delete(sha1File);
|
||||
FileUtils.delete(dataFile);
|
||||
tempSha1File.renameTo(sha1File);
|
||||
tempDataFile.renameTo(dataFile);
|
||||
|
||||
// Read new EhTagDatabase
|
||||
try (BufferedSource source = Okio.buffer(Okio.source(dataFile))) {
|
||||
instance = new EhTagDatabase(dataName, source);
|
||||
} catch (IOException e) {
|
||||
// Ignore
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public String getTranslation(String tag) {
|
||||
return search(tags, tag.getBytes(TextUrl.UTF_8));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String search(byte[] tags, byte[] tag) {
|
||||
int low = 0;
|
||||
int high = tags.length;
|
||||
while (low < high) {
|
||||
int start = (low + high) / 2;
|
||||
// Look for the starting '\n'
|
||||
while (start > -1 && tags[start] != '\n') {
|
||||
start--;
|
||||
}
|
||||
start++;
|
||||
|
||||
// Look for the middle '\r'.
|
||||
int middle = 1;
|
||||
while (tags[start + middle] != '\r') {
|
||||
middle++;
|
||||
}
|
||||
|
||||
// Look for the ending '\n'
|
||||
int end = middle + 1;
|
||||
while (tags[start + end] != '\n') {
|
||||
end++;
|
||||
}
|
||||
|
||||
int compare;
|
||||
int tagIndex = 0;
|
||||
int curIndex = start;
|
||||
|
||||
for (; ; ) {
|
||||
int tagByte = tag[tagIndex] & 0xff;
|
||||
int curByte = tags[curIndex] & 0xff;
|
||||
compare = tagByte - curByte;
|
||||
if (compare != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
tagIndex++;
|
||||
curIndex++;
|
||||
if (tagIndex == tag.length && curIndex == start + middle) {
|
||||
break;
|
||||
}
|
||||
if (tagIndex == tag.length) {
|
||||
compare = -1;
|
||||
break;
|
||||
}
|
||||
if (curIndex == start + middle) {
|
||||
compare = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (compare < 0) {
|
||||
high = start - 1;
|
||||
} else if (compare > 0) {
|
||||
low = start + end + 1;
|
||||
} else {
|
||||
byte[] bytes = Base64.decode(tags, start + middle + 1, end - middle - 1, Base64.DEFAULT);
|
||||
return new String(bytes, TextUrl.UTF_8);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -17,10 +17,13 @@
|
||||
package com.hippo.ehviewer.client;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.hippo.ehviewer.Settings;
|
||||
import com.hippo.network.UrlBuilder;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import okhttp3.HttpUrl;
|
||||
|
||||
public class EhUrl {
|
||||
@@ -207,7 +210,7 @@ public class EhUrl {
|
||||
public static String getThumbUrlPrefix() {
|
||||
switch (Settings.getGallerySite()) {
|
||||
default:
|
||||
//case SITE_E:
|
||||
//case SITE_E:
|
||||
return URL_PREFIX_THUMB_E;
|
||||
//case SITE_EX:
|
||||
// return URL_PREFIX_THUMB_EX;
|
||||
|
||||
@@ -19,7 +19,9 @@ package com.hippo.ehviewer.client;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder;
|
||||
import com.hippo.ehviewer.client.parser.GalleryDetailUrlParser;
|
||||
import com.hippo.ehviewer.client.parser.GalleryListUrlParser;
|
||||
|
||||
@@ -19,10 +19,13 @@ package com.hippo.ehviewer.client;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.Settings;
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class EhUtils {
|
||||
@@ -64,20 +67,20 @@ public class EhUtils {
|
||||
EhConfig.ASIAN_PORN,
|
||||
EhConfig.NON_H,
|
||||
EhConfig.WESTERN,
|
||||
UNKNOWN };
|
||||
UNKNOWN};
|
||||
|
||||
private static final String[][] CATEGORY_STRINGS = {
|
||||
new String[] { "misc" },
|
||||
new String[] { "doujinshi" },
|
||||
new String[] { "manga" },
|
||||
new String[] { "artistcg", "Artist CG Sets", "Artist CG" },
|
||||
new String[] { "gamecg", "Game CG Sets", "Game CG" },
|
||||
new String[] { "imageset", "Image Sets", "Image Set" },
|
||||
new String[] { "cosplay" },
|
||||
new String[] { "asianporn", "Asian Porn" },
|
||||
new String[] { "non-h" },
|
||||
new String[] { "western" },
|
||||
new String[] { "unknown" }
|
||||
new String[]{"misc"},
|
||||
new String[]{"doujinshi"},
|
||||
new String[]{"manga"},
|
||||
new String[]{"artistcg", "Artist CG Sets", "Artist CG"},
|
||||
new String[]{"gamecg", "Game CG Sets", "Game CG"},
|
||||
new String[]{"imageset", "Image Sets", "Image Set"},
|
||||
new String[]{"cosplay"},
|
||||
new String[]{"asianporn", "Asian Porn"},
|
||||
new String[]{"non-h"},
|
||||
new String[]{"western"},
|
||||
new String[]{"unknown"}
|
||||
};
|
||||
|
||||
public static int getCategory(String type) {
|
||||
|
||||
@@ -29,36 +29,56 @@ import java.net.URLEncoder;
|
||||
|
||||
public class FavListUrlBuilder implements Parcelable {
|
||||
|
||||
private static final String TAG = FavListUrlBuilder.class.getSimpleName();
|
||||
public static final int FAV_CAT_ALL = -1;
|
||||
public static final int FAV_CAT_LOCAL = -2;
|
||||
public static final Parcelable.Creator<FavListUrlBuilder> CREATOR = new Parcelable.Creator<FavListUrlBuilder>() {
|
||||
|
||||
@Override
|
||||
public FavListUrlBuilder createFromParcel(Parcel source) {
|
||||
return new FavListUrlBuilder(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FavListUrlBuilder[] newArray(int size) {
|
||||
return new FavListUrlBuilder[size];
|
||||
}
|
||||
};
|
||||
private static final String TAG = FavListUrlBuilder.class.getSimpleName();
|
||||
private int mIndex;
|
||||
private String mKeyword;
|
||||
private int mFavCat = FAV_CAT_ALL;
|
||||
|
||||
public FavListUrlBuilder() {
|
||||
}
|
||||
|
||||
protected FavListUrlBuilder(Parcel in) {
|
||||
this.mIndex = in.readInt();
|
||||
this.mKeyword = in.readString();
|
||||
this.mFavCat = in.readInt();
|
||||
}
|
||||
|
||||
public static boolean isValidFavCat(int favCat) {
|
||||
return favCat >= 0 && favCat <= 9;
|
||||
}
|
||||
|
||||
public void setIndex(int index) {
|
||||
mIndex = index;
|
||||
}
|
||||
|
||||
public void setKeyword(String keyword) {
|
||||
mKeyword = keyword;
|
||||
}
|
||||
|
||||
public void setFavCat(int favCat) {
|
||||
mFavCat = favCat;
|
||||
}
|
||||
|
||||
public String getKeyword() {
|
||||
return mKeyword;
|
||||
}
|
||||
|
||||
public void setKeyword(String keyword) {
|
||||
mKeyword = keyword;
|
||||
}
|
||||
|
||||
public int getFavCat() {
|
||||
return mFavCat;
|
||||
}
|
||||
|
||||
public static boolean isValidFavCat(int favCat) {
|
||||
return favCat >= 0 && favCat <= 9;
|
||||
public void setFavCat(int favCat) {
|
||||
mFavCat = favCat;
|
||||
}
|
||||
|
||||
public boolean isLocalFavCat() {
|
||||
@@ -102,26 +122,4 @@ public class FavListUrlBuilder implements Parcelable {
|
||||
dest.writeString(this.mKeyword);
|
||||
dest.writeInt(this.mFavCat);
|
||||
}
|
||||
|
||||
public FavListUrlBuilder() {
|
||||
}
|
||||
|
||||
protected FavListUrlBuilder(Parcel in) {
|
||||
this.mIndex = in.readInt();
|
||||
this.mKeyword = in.readString();
|
||||
this.mFavCat = in.readInt();
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<FavListUrlBuilder> CREATOR = new Parcelable.Creator<FavListUrlBuilder>() {
|
||||
|
||||
@Override
|
||||
public FavListUrlBuilder createFromParcel(Parcel source) {
|
||||
return new FavListUrlBuilder(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FavListUrlBuilder[] newArray(int size) {
|
||||
return new FavListUrlBuilder[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -18,10 +18,22 @@ package com.hippo.ehviewer.client.data;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class GalleryApiInfo implements Parcelable {
|
||||
|
||||
public static final Parcelable.Creator<GalleryApiInfo> CREATOR = new Parcelable.Creator<GalleryApiInfo>() {
|
||||
@Override
|
||||
public GalleryApiInfo createFromParcel(Parcel source) {
|
||||
return new GalleryApiInfo(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GalleryApiInfo[] newArray(int size) {
|
||||
return new GalleryApiInfo[size];
|
||||
}
|
||||
};
|
||||
public long gid;
|
||||
public String token;
|
||||
public String archiverKey;
|
||||
@@ -39,6 +51,27 @@ public class GalleryApiInfo implements Parcelable {
|
||||
@Nullable
|
||||
public String[] tags;
|
||||
|
||||
public GalleryApiInfo() {
|
||||
}
|
||||
|
||||
protected GalleryApiInfo(Parcel in) {
|
||||
this.gid = in.readLong();
|
||||
this.token = in.readString();
|
||||
this.archiverKey = in.readString();
|
||||
this.title = in.readString();
|
||||
this.titleJpn = in.readString();
|
||||
this.category = in.readInt();
|
||||
this.thumb = in.readString();
|
||||
this.uploader = in.readString();
|
||||
this.posted = in.readLong();
|
||||
this.filecount = in.readInt();
|
||||
this.filesize = in.readLong();
|
||||
this.expunged = in.readByte() != 0;
|
||||
this.rating = in.readFloat();
|
||||
this.torrentcount = in.readInt();
|
||||
this.tags = in.createStringArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
@@ -62,37 +95,4 @@ public class GalleryApiInfo implements Parcelable {
|
||||
dest.writeInt(this.torrentcount);
|
||||
dest.writeStringArray(this.tags);
|
||||
}
|
||||
|
||||
public GalleryApiInfo() {
|
||||
}
|
||||
|
||||
protected GalleryApiInfo(Parcel in) {
|
||||
this.gid = in.readLong();
|
||||
this.token = in.readString();
|
||||
this.archiverKey = in.readString();
|
||||
this.title = in.readString();
|
||||
this.titleJpn = in.readString();
|
||||
this.category = in.readInt();
|
||||
this.thumb = in.readString();
|
||||
this.uploader = in.readString();
|
||||
this.posted = in.readLong();
|
||||
this.filecount = in.readInt();
|
||||
this.filesize = in.readLong();
|
||||
this.expunged = in.readByte() != 0;
|
||||
this.rating = in.readFloat();
|
||||
this.torrentcount = in.readInt();
|
||||
this.tags = in.createStringArray();
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<GalleryApiInfo> CREATOR = new Parcelable.Creator<GalleryApiInfo>() {
|
||||
@Override
|
||||
public GalleryApiInfo createFromParcel(Parcel source) {
|
||||
return new GalleryApiInfo(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GalleryApiInfo[] newArray(int size) {
|
||||
return new GalleryApiInfo[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -21,6 +21,17 @@ import android.os.Parcelable;
|
||||
|
||||
public class GalleryComment implements Parcelable {
|
||||
|
||||
public static final Creator<GalleryComment> CREATOR = new Creator<GalleryComment>() {
|
||||
@Override
|
||||
public GalleryComment createFromParcel(Parcel source) {
|
||||
return new GalleryComment(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GalleryComment[] newArray(int size) {
|
||||
return new GalleryComment[size];
|
||||
}
|
||||
};
|
||||
// 0 for uploader comment. can't vote
|
||||
public long id;
|
||||
public int score;
|
||||
@@ -35,6 +46,24 @@ public class GalleryComment implements Parcelable {
|
||||
public String comment;
|
||||
public long lastEdited;
|
||||
|
||||
public GalleryComment() {
|
||||
}
|
||||
|
||||
protected GalleryComment(Parcel in) {
|
||||
this.id = in.readLong();
|
||||
this.score = in.readInt();
|
||||
this.editable = in.readByte() != 0;
|
||||
this.voteUpAble = in.readByte() != 0;
|
||||
this.voteUpEd = in.readByte() != 0;
|
||||
this.voteDownAble = in.readByte() != 0;
|
||||
this.voteDownEd = in.readByte() != 0;
|
||||
this.voteState = in.readString();
|
||||
this.time = in.readLong();
|
||||
this.user = in.readString();
|
||||
this.comment = in.readString();
|
||||
this.lastEdited = in.readLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
@@ -55,34 +84,4 @@ public class GalleryComment implements Parcelable {
|
||||
dest.writeString(this.comment);
|
||||
dest.writeLong(this.lastEdited);
|
||||
}
|
||||
|
||||
public GalleryComment() {
|
||||
}
|
||||
|
||||
protected GalleryComment(Parcel in) {
|
||||
this.id = in.readLong();
|
||||
this.score = in.readInt();
|
||||
this.editable = in.readByte() != 0;
|
||||
this.voteUpAble = in.readByte() != 0;
|
||||
this.voteUpEd = in.readByte() != 0;
|
||||
this.voteDownAble = in.readByte() != 0;
|
||||
this.voteDownEd = in.readByte() != 0;
|
||||
this.voteState = in.readString();
|
||||
this.time = in.readLong();
|
||||
this.user = in.readString();
|
||||
this.comment = in.readString();
|
||||
this.lastEdited = in.readLong();
|
||||
}
|
||||
|
||||
public static final Creator<GalleryComment> CREATOR = new Creator<GalleryComment>() {
|
||||
@Override
|
||||
public GalleryComment createFromParcel(Parcel source) {
|
||||
return new GalleryComment(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GalleryComment[] newArray(int size) {
|
||||
return new GalleryComment[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -18,48 +18,48 @@ package com.hippo.ehviewer.client.data;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class GalleryCommentList implements Parcelable {
|
||||
|
||||
public GalleryComment[] comments;
|
||||
public boolean hasMore;
|
||||
public static final Creator<GalleryCommentList> CREATOR = new Creator<GalleryCommentList>() {
|
||||
@Override
|
||||
public GalleryCommentList createFromParcel(Parcel in) {
|
||||
return new GalleryCommentList(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
@Override
|
||||
public GalleryCommentList[] newArray(int size) {
|
||||
return new GalleryCommentList[size];
|
||||
}
|
||||
};
|
||||
public GalleryComment[] comments;
|
||||
public boolean hasMore;
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeParcelableArray(comments, flags);
|
||||
dest.writeByte(hasMore ? (byte) 1 : (byte) 0);
|
||||
}
|
||||
|
||||
public GalleryCommentList(GalleryComment[] comments, boolean hasMore) {
|
||||
this.comments = comments;
|
||||
this.hasMore = hasMore;
|
||||
}
|
||||
|
||||
protected GalleryCommentList(Parcel in) {
|
||||
Parcelable[] array = in.readParcelableArray(getClass().getClassLoader());
|
||||
if (array != null) {
|
||||
comments = Arrays.copyOf(array, array.length, GalleryComment[].class);
|
||||
} else {
|
||||
comments = null;
|
||||
public GalleryCommentList(GalleryComment[] comments, boolean hasMore) {
|
||||
this.comments = comments;
|
||||
this.hasMore = hasMore;
|
||||
}
|
||||
hasMore = in.readByte() != 0;
|
||||
}
|
||||
|
||||
public static final Creator<GalleryCommentList> CREATOR = new Creator<GalleryCommentList>() {
|
||||
@Override
|
||||
public GalleryCommentList createFromParcel(Parcel in) {
|
||||
return new GalleryCommentList(in);
|
||||
protected GalleryCommentList(Parcel in) {
|
||||
Parcelable[] array = in.readParcelableArray(getClass().getClassLoader());
|
||||
if (array != null) {
|
||||
comments = Arrays.copyOf(array, array.length, GalleryComment[].class);
|
||||
} else {
|
||||
comments = null;
|
||||
}
|
||||
hasMore = in.readByte() != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GalleryCommentList[] newArray(int size) {
|
||||
return new GalleryCommentList[size];
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeParcelableArray(comments, flags);
|
||||
dest.writeByte(hasMore ? (byte) 1 : (byte) 0);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -23,6 +23,17 @@ import java.util.Arrays;
|
||||
|
||||
public class GalleryDetail extends GalleryInfo {
|
||||
|
||||
public static final Creator<GalleryDetail> CREATOR = new Creator<GalleryDetail>() {
|
||||
@Override
|
||||
public GalleryDetail createFromParcel(Parcel source) {
|
||||
return new GalleryDetail(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GalleryDetail[] newArray(int size) {
|
||||
return new GalleryDetail[size];
|
||||
}
|
||||
};
|
||||
public long apiUid = -1L;
|
||||
public String apiKey;
|
||||
public int torrentCount;
|
||||
@@ -41,31 +52,6 @@ public class GalleryDetail extends GalleryInfo {
|
||||
public int previewPages;
|
||||
public PreviewSet previewSet;
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeInt(this.torrentCount);
|
||||
dest.writeString(this.torrentUrl);
|
||||
dest.writeString(this.archiveUrl);
|
||||
dest.writeString(this.parent);
|
||||
dest.writeString(this.visible);
|
||||
dest.writeString(this.language);
|
||||
dest.writeString(this.size);
|
||||
dest.writeInt(this.pages);
|
||||
dest.writeInt(this.favoriteCount);
|
||||
dest.writeByte(isFavorited ? (byte) 1 : (byte) 0);
|
||||
dest.writeInt(this.ratingCount);
|
||||
dest.writeParcelableArray(this.tags, flags);
|
||||
dest.writeParcelable(this.comments, flags);
|
||||
dest.writeInt(this.previewPages);
|
||||
dest.writeParcelable(previewSet, flags);
|
||||
}
|
||||
|
||||
public GalleryDetail() {
|
||||
}
|
||||
|
||||
@@ -93,15 +79,28 @@ public class GalleryDetail extends GalleryInfo {
|
||||
this.previewSet = in.readParcelable(PreviewSet.class.getClassLoader());
|
||||
}
|
||||
|
||||
public static final Creator<GalleryDetail> CREATOR = new Creator<GalleryDetail>() {
|
||||
@Override
|
||||
public GalleryDetail createFromParcel(Parcel source) {
|
||||
return new GalleryDetail(source);
|
||||
}
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GalleryDetail[] newArray(int size) {
|
||||
return new GalleryDetail[size];
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeInt(this.torrentCount);
|
||||
dest.writeString(this.torrentUrl);
|
||||
dest.writeString(this.archiveUrl);
|
||||
dest.writeString(this.parent);
|
||||
dest.writeString(this.visible);
|
||||
dest.writeString(this.language);
|
||||
dest.writeString(this.size);
|
||||
dest.writeInt(this.pages);
|
||||
dest.writeInt(this.favoriteCount);
|
||||
dest.writeByte(isFavorited ? (byte) 1 : (byte) 0);
|
||||
dest.writeInt(this.ratingCount);
|
||||
dest.writeParcelableArray(this.tags, flags);
|
||||
dest.writeParcelable(this.comments, flags);
|
||||
dest.writeInt(this.previewPages);
|
||||
dest.writeParcelable(previewSet, flags);
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,9 @@ package com.hippo.ehviewer.client.data;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class GalleryInfo implements Parcelable {
|
||||
@@ -79,23 +81,34 @@ public class GalleryInfo implements Parcelable {
|
||||
};
|
||||
|
||||
public static final String[] S_LANG_TAGS = {
|
||||
"language:english",
|
||||
"language:chinese",
|
||||
"language:spanish",
|
||||
"language:korean",
|
||||
"language:russian",
|
||||
"language:french",
|
||||
"language:portuguese",
|
||||
"language:thai",
|
||||
"language:german",
|
||||
"language:italian",
|
||||
"language:vietnamese",
|
||||
"language:polish",
|
||||
"language:hungarian",
|
||||
"language:dutch",
|
||||
"language:english",
|
||||
"language:chinese",
|
||||
"language:spanish",
|
||||
"language:korean",
|
||||
"language:russian",
|
||||
"language:french",
|
||||
"language:portuguese",
|
||||
"language:thai",
|
||||
"language:german",
|
||||
"language:italian",
|
||||
"language:vietnamese",
|
||||
"language:polish",
|
||||
"language:hungarian",
|
||||
"language:dutch",
|
||||
};
|
||||
public static final Parcelable.Creator<GalleryInfo> CREATOR = new Parcelable.Creator<GalleryInfo>() {
|
||||
|
||||
public long gid ;
|
||||
@Override
|
||||
public GalleryInfo createFromParcel(Parcel source) {
|
||||
return new GalleryInfo(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GalleryInfo[] newArray(int size) {
|
||||
return new GalleryInfo[size];
|
||||
}
|
||||
};
|
||||
public long gid;
|
||||
public String token;
|
||||
public String title;
|
||||
public String titleJpn;
|
||||
@@ -108,22 +121,43 @@ public class GalleryInfo implements Parcelable {
|
||||
@Nullable
|
||||
public String[] simpleTags;
|
||||
public int pages;
|
||||
|
||||
public int thumbWidth;
|
||||
public int thumbHeight;
|
||||
|
||||
public int spanSize;
|
||||
public int spanIndex;
|
||||
public int spanGroupIndex;
|
||||
|
||||
/**
|
||||
* language from title
|
||||
*/
|
||||
public String simpleLanguage;
|
||||
|
||||
public int favoriteSlot = -2;
|
||||
public String favoriteName;
|
||||
|
||||
public GalleryInfo() {
|
||||
}
|
||||
|
||||
protected GalleryInfo(Parcel in) {
|
||||
this.gid = in.readLong();
|
||||
this.token = in.readString();
|
||||
this.title = in.readString();
|
||||
this.titleJpn = in.readString();
|
||||
this.thumb = in.readString();
|
||||
this.category = in.readInt();
|
||||
this.posted = in.readString();
|
||||
this.uploader = in.readString();
|
||||
this.rating = in.readFloat();
|
||||
this.rated = in.readByte() != 0;
|
||||
this.simpleLanguage = in.readString();
|
||||
this.simpleTags = in.createStringArray();
|
||||
this.thumbWidth = in.readInt();
|
||||
this.thumbHeight = in.readInt();
|
||||
this.spanSize = in.readInt();
|
||||
this.spanIndex = in.readInt();
|
||||
this.spanGroupIndex = in.readInt();
|
||||
this.favoriteSlot = in.readInt();
|
||||
this.favoriteName = in.readString();
|
||||
}
|
||||
|
||||
public final void generateSLang() {
|
||||
if (simpleTags != null) {
|
||||
generateSLangFromTags();
|
||||
@@ -181,41 +215,4 @@ public class GalleryInfo implements Parcelable {
|
||||
dest.writeInt(this.favoriteSlot);
|
||||
dest.writeString(this.favoriteName);
|
||||
}
|
||||
|
||||
public GalleryInfo() {}
|
||||
|
||||
protected GalleryInfo(Parcel in) {
|
||||
this.gid = in.readLong();
|
||||
this.token = in.readString();
|
||||
this.title = in.readString();
|
||||
this.titleJpn = in.readString();
|
||||
this.thumb = in.readString();
|
||||
this.category = in.readInt();
|
||||
this.posted = in.readString();
|
||||
this.uploader = in.readString();
|
||||
this.rating = in.readFloat();
|
||||
this.rated = in.readByte() != 0;
|
||||
this.simpleLanguage = in.readString();
|
||||
this.simpleTags = in.createStringArray();
|
||||
this.thumbWidth = in.readInt();
|
||||
this.thumbHeight = in.readInt();
|
||||
this.spanSize = in.readInt();
|
||||
this.spanIndex = in.readInt();
|
||||
this.spanGroupIndex = in.readInt();
|
||||
this.favoriteSlot = in.readInt();
|
||||
this.favoriteName = in.readString();
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<GalleryInfo> CREATOR = new Parcelable.Creator<GalleryInfo>() {
|
||||
|
||||
@Override
|
||||
public GalleryInfo createFromParcel(Parcel source) {
|
||||
return new GalleryInfo(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GalleryInfo[] newArray(int size) {
|
||||
return new GalleryInfo[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -23,6 +23,17 @@ import com.hippo.widget.LoadImageView;
|
||||
|
||||
public class GalleryPreview implements Parcelable {
|
||||
|
||||
public static final Parcelable.Creator<GalleryPreview> CREATOR = new Parcelable.Creator<GalleryPreview>() {
|
||||
@Override
|
||||
public GalleryPreview createFromParcel(Parcel source) {
|
||||
return new GalleryPreview(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GalleryPreview[] newArray(int size) {
|
||||
return new GalleryPreview[size];
|
||||
}
|
||||
};
|
||||
String imageKey;
|
||||
String imageUrl;
|
||||
String pageUrl;
|
||||
@@ -32,6 +43,19 @@ public class GalleryPreview implements Parcelable {
|
||||
int clipWidth = Integer.MIN_VALUE;
|
||||
int clipHeight = Integer.MIN_VALUE;
|
||||
|
||||
public GalleryPreview() {
|
||||
}
|
||||
|
||||
protected GalleryPreview(Parcel in) {
|
||||
this.imageUrl = in.readString();
|
||||
this.pageUrl = in.readString();
|
||||
this.position = in.readInt();
|
||||
this.offsetX = in.readInt();
|
||||
this.offsetY = in.readInt();
|
||||
this.clipWidth = in.readInt();
|
||||
this.clipHeight = in.readInt();
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
@@ -56,29 +80,4 @@ public class GalleryPreview implements Parcelable {
|
||||
dest.writeInt(this.clipWidth);
|
||||
dest.writeInt(this.clipHeight);
|
||||
}
|
||||
|
||||
public GalleryPreview() {
|
||||
}
|
||||
|
||||
protected GalleryPreview(Parcel in) {
|
||||
this.imageUrl = in.readString();
|
||||
this.pageUrl = in.readString();
|
||||
this.position = in.readInt();
|
||||
this.offsetX = in.readInt();
|
||||
this.offsetY = in.readInt();
|
||||
this.clipWidth = in.readInt();
|
||||
this.clipHeight = in.readInt();
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<GalleryPreview> CREATOR = new Parcelable.Creator<GalleryPreview>() {
|
||||
@Override
|
||||
public GalleryPreview createFromParcel(Parcel source) {
|
||||
return new GalleryPreview(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GalleryPreview[] newArray(int size) {
|
||||
return new GalleryPreview[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -24,8 +24,28 @@ import java.util.ArrayList;
|
||||
// TODO Add url field?
|
||||
public class GalleryTagGroup implements Parcelable {
|
||||
|
||||
public String groupName;
|
||||
public static final Parcelable.Creator<GalleryTagGroup> CREATOR = new Parcelable.Creator<GalleryTagGroup>() {
|
||||
@Override
|
||||
public GalleryTagGroup createFromParcel(Parcel source) {
|
||||
return new GalleryTagGroup(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GalleryTagGroup[] newArray(int size) {
|
||||
return new GalleryTagGroup[size];
|
||||
}
|
||||
};
|
||||
private final ArrayList<String> mTagList;
|
||||
public String groupName;
|
||||
|
||||
public GalleryTagGroup() {
|
||||
mTagList = new ArrayList<>();
|
||||
}
|
||||
|
||||
protected GalleryTagGroup(Parcel in) {
|
||||
this.groupName = in.readString();
|
||||
this.mTagList = in.createStringArrayList();
|
||||
}
|
||||
|
||||
public void addTag(String tag) {
|
||||
mTagList.add(tag);
|
||||
@@ -49,25 +69,4 @@ public class GalleryTagGroup implements Parcelable {
|
||||
dest.writeString(this.groupName);
|
||||
dest.writeStringList(this.mTagList);
|
||||
}
|
||||
|
||||
public GalleryTagGroup() {
|
||||
mTagList = new ArrayList<>();
|
||||
}
|
||||
|
||||
protected GalleryTagGroup(Parcel in) {
|
||||
this.groupName = in.readString();
|
||||
this.mTagList = in.createStringArrayList();
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<GalleryTagGroup> CREATOR = new Parcelable.Creator<GalleryTagGroup>() {
|
||||
@Override
|
||||
public GalleryTagGroup createFromParcel(Parcel source) {
|
||||
return new GalleryTagGroup(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GalleryTagGroup[] newArray(int size) {
|
||||
return new GalleryTagGroup[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -27,10 +27,33 @@ import java.util.ArrayList;
|
||||
|
||||
public class LargePreviewSet extends PreviewSet {
|
||||
|
||||
public static final Parcelable.Creator<LargePreviewSet> CREATOR = new Parcelable.Creator<LargePreviewSet>() {
|
||||
@Override
|
||||
public LargePreviewSet createFromParcel(Parcel source) {
|
||||
return new LargePreviewSet(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LargePreviewSet[] newArray(int size) {
|
||||
return new LargePreviewSet[size];
|
||||
}
|
||||
};
|
||||
private final IntList mPositionList;
|
||||
private final ArrayList<String> mImageUrlList;
|
||||
private final ArrayList<String> mPageUrlList;
|
||||
|
||||
public LargePreviewSet() {
|
||||
mPositionList = new IntList();
|
||||
mImageUrlList = new ArrayList<>();
|
||||
mPageUrlList = new ArrayList<>();
|
||||
}
|
||||
|
||||
protected LargePreviewSet(Parcel in) {
|
||||
this.mPositionList = in.readParcelable(IntList.class.getClassLoader());
|
||||
this.mImageUrlList = in.createStringArrayList();
|
||||
this.mPageUrlList = in.createStringArrayList();
|
||||
}
|
||||
|
||||
public void addItem(int index, String imageUrl, String pageUrl) {
|
||||
mPositionList.add(index);
|
||||
mImageUrlList.add(imageUrl);
|
||||
@@ -80,28 +103,4 @@ public class LargePreviewSet extends PreviewSet {
|
||||
dest.writeStringList(this.mImageUrlList);
|
||||
dest.writeStringList(this.mPageUrlList);
|
||||
}
|
||||
|
||||
public LargePreviewSet() {
|
||||
mPositionList = new IntList();
|
||||
mImageUrlList = new ArrayList<>();
|
||||
mPageUrlList = new ArrayList<>();
|
||||
}
|
||||
|
||||
protected LargePreviewSet(Parcel in) {
|
||||
this.mPositionList = in.readParcelable(IntList.class.getClassLoader());
|
||||
this.mImageUrlList = in.createStringArrayList();
|
||||
this.mPageUrlList = in.createStringArrayList();
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<LargePreviewSet> CREATOR = new Parcelable.Creator<LargePreviewSet>() {
|
||||
@Override
|
||||
public LargePreviewSet createFromParcel(Parcel source) {
|
||||
return new LargePreviewSet(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LargePreviewSet[] newArray(int size) {
|
||||
return new LargePreviewSet[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -19,8 +19,10 @@ package com.hippo.ehviewer.client.data;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.ehviewer.client.EhConfig;
|
||||
import com.hippo.ehviewer.client.EhUrl;
|
||||
import com.hippo.ehviewer.client.EhUtils;
|
||||
@@ -29,6 +31,7 @@ import com.hippo.ehviewer.widget.AdvanceSearchTable;
|
||||
import com.hippo.network.UrlBuilder;
|
||||
import com.hippo.yorozuya.NumberUtils;
|
||||
import com.hippo.yorozuya.StringUtils;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@@ -37,10 +40,6 @@ import java.net.URLEncoder;
|
||||
|
||||
public class ListUrlBuilder implements Cloneable, Parcelable {
|
||||
|
||||
@IntDef({MODE_NORMAL, MODE_UPLOADER, MODE_TAG, MODE_WHATS_HOT, MODE_IMAGE_SEARCH, MODE_SUBSCRIPTION})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
private @interface Mode {}
|
||||
|
||||
// Mode
|
||||
public static final int MODE_NORMAL = 0x0;
|
||||
public static final int MODE_UPLOADER = 0x1;
|
||||
@@ -48,10 +47,19 @@ public class ListUrlBuilder implements Cloneable, Parcelable {
|
||||
public static final int MODE_WHATS_HOT = 0x3;
|
||||
public static final int MODE_IMAGE_SEARCH = 0x4;
|
||||
public static final int MODE_SUBSCRIPTION = 0x5;
|
||||
|
||||
public static final int DEFAULT_ADVANCE = AdvanceSearchTable.SNAME | AdvanceSearchTable.STAGS;
|
||||
public static final int DEFAULT_MIN_RATING = 2;
|
||||
public static final Creator<ListUrlBuilder> CREATOR = new Creator<ListUrlBuilder>() {
|
||||
@Override
|
||||
public ListUrlBuilder createFromParcel(Parcel source) {
|
||||
return new ListUrlBuilder(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListUrlBuilder[] newArray(int size) {
|
||||
return new ListUrlBuilder[size];
|
||||
}
|
||||
};
|
||||
@Mode
|
||||
private int mMode = MODE_NORMAL;
|
||||
|
||||
@@ -70,6 +78,25 @@ public class ListUrlBuilder implements Cloneable, Parcelable {
|
||||
private boolean mOnlySearchCovers;
|
||||
private boolean mShowExpunged;
|
||||
|
||||
public ListUrlBuilder() {
|
||||
}
|
||||
|
||||
@SuppressWarnings("WrongConstant")
|
||||
protected ListUrlBuilder(Parcel in) {
|
||||
this.mMode = in.readInt();
|
||||
this.mPageIndex = in.readInt();
|
||||
this.mCategory = in.readInt();
|
||||
this.mKeyword = in.readString();
|
||||
this.mAdvanceSearch = in.readInt();
|
||||
this.mMinRating = in.readInt();
|
||||
this.mPageFrom = in.readInt();
|
||||
this.mPageTo = in.readInt();
|
||||
this.mImagePath = in.readString();
|
||||
this.mUseSimilarityScan = in.readByte() != 0;
|
||||
this.mOnlySearchCovers = in.readByte() != 0;
|
||||
this.mShowExpunged = in.readByte() != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make this ListUrlBuilder point to homepage
|
||||
*/
|
||||
@@ -197,6 +224,7 @@ public class ListUrlBuilder implements Cloneable, Parcelable {
|
||||
|
||||
/**
|
||||
* Make them the same
|
||||
*
|
||||
* @param lub The template
|
||||
*/
|
||||
public void set(ListUrlBuilder lub) {
|
||||
@@ -263,11 +291,7 @@ public class ListUrlBuilder implements Cloneable, Parcelable {
|
||||
if (q.pageFrom != mPageFrom) {
|
||||
return false;
|
||||
}
|
||||
if (q.pageTo != mPageTo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return q.pageTo == mPageTo;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -497,17 +521,23 @@ public class ListUrlBuilder implements Cloneable, Parcelable {
|
||||
// Advance search
|
||||
if (mAdvanceSearch != -1) {
|
||||
ub.addQuery("advsearch", "1");
|
||||
if((mAdvanceSearch & AdvanceSearchTable.SNAME) != 0) ub.addQuery("f_sname", "on");
|
||||
if((mAdvanceSearch & AdvanceSearchTable.STAGS) != 0) ub.addQuery("f_stags", "on");
|
||||
if((mAdvanceSearch & AdvanceSearchTable.SDESC) != 0) ub.addQuery("f_sdesc", "on");
|
||||
if((mAdvanceSearch & AdvanceSearchTable.STORR) != 0) ub.addQuery("f_storr", "on");
|
||||
if((mAdvanceSearch & AdvanceSearchTable.STO) != 0) ub.addQuery("f_sto", "on");
|
||||
if((mAdvanceSearch & AdvanceSearchTable.SDT1) != 0) ub.addQuery("f_sdt1", "on");
|
||||
if((mAdvanceSearch & AdvanceSearchTable.SDT2) != 0) ub.addQuery("f_sdt2", "on");
|
||||
if((mAdvanceSearch & AdvanceSearchTable.SH) != 0) ub.addQuery("f_sh", "on");
|
||||
if((mAdvanceSearch & AdvanceSearchTable.SFL) != 0) ub.addQuery("f_sfl", "on");
|
||||
if((mAdvanceSearch & AdvanceSearchTable.SFU) != 0) ub.addQuery("f_sfu", "on");
|
||||
if((mAdvanceSearch & AdvanceSearchTable.SFT) != 0) ub.addQuery("f_sft", "on");
|
||||
if ((mAdvanceSearch & AdvanceSearchTable.SNAME) != 0)
|
||||
ub.addQuery("f_sname", "on");
|
||||
if ((mAdvanceSearch & AdvanceSearchTable.STAGS) != 0)
|
||||
ub.addQuery("f_stags", "on");
|
||||
if ((mAdvanceSearch & AdvanceSearchTable.SDESC) != 0)
|
||||
ub.addQuery("f_sdesc", "on");
|
||||
if ((mAdvanceSearch & AdvanceSearchTable.STORR) != 0)
|
||||
ub.addQuery("f_storr", "on");
|
||||
if ((mAdvanceSearch & AdvanceSearchTable.STO) != 0) ub.addQuery("f_sto", "on");
|
||||
if ((mAdvanceSearch & AdvanceSearchTable.SDT1) != 0)
|
||||
ub.addQuery("f_sdt1", "on");
|
||||
if ((mAdvanceSearch & AdvanceSearchTable.SDT2) != 0)
|
||||
ub.addQuery("f_sdt2", "on");
|
||||
if ((mAdvanceSearch & AdvanceSearchTable.SH) != 0) ub.addQuery("f_sh", "on");
|
||||
if ((mAdvanceSearch & AdvanceSearchTable.SFL) != 0) ub.addQuery("f_sfl", "on");
|
||||
if ((mAdvanceSearch & AdvanceSearchTable.SFU) != 0) ub.addQuery("f_sfu", "on");
|
||||
if ((mAdvanceSearch & AdvanceSearchTable.SFT) != 0) ub.addQuery("f_sft", "on");
|
||||
// Set min star
|
||||
if (mMinRating != -1) {
|
||||
ub.addQuery("f_sr", "on");
|
||||
@@ -576,34 +606,8 @@ public class ListUrlBuilder implements Cloneable, Parcelable {
|
||||
dest.writeByte(mShowExpunged ? (byte) 1 : (byte) 0);
|
||||
}
|
||||
|
||||
public ListUrlBuilder() {
|
||||
@IntDef({MODE_NORMAL, MODE_UPLOADER, MODE_TAG, MODE_WHATS_HOT, MODE_IMAGE_SEARCH, MODE_SUBSCRIPTION})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
private @interface Mode {
|
||||
}
|
||||
|
||||
@SuppressWarnings("WrongConstant")
|
||||
protected ListUrlBuilder(Parcel in) {
|
||||
this.mMode = in.readInt();
|
||||
this.mPageIndex = in.readInt();
|
||||
this.mCategory = in.readInt();
|
||||
this.mKeyword = in.readString();
|
||||
this.mAdvanceSearch = in.readInt();
|
||||
this.mMinRating = in.readInt();
|
||||
this.mPageFrom = in.readInt();
|
||||
this.mPageTo = in.readInt();
|
||||
this.mImagePath = in.readString();
|
||||
this.mUseSimilarityScan = in.readByte() != 0;
|
||||
this.mOnlySearchCovers = in.readByte() != 0;
|
||||
this.mShowExpunged = in.readByte() != 0;
|
||||
}
|
||||
|
||||
public static final Creator<ListUrlBuilder> CREATOR = new Creator<ListUrlBuilder>() {
|
||||
@Override
|
||||
public ListUrlBuilder createFromParcel(Parcel source) {
|
||||
return new ListUrlBuilder(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListUrlBuilder[] newArray(int size) {
|
||||
return new ListUrlBuilder[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -25,6 +25,17 @@ import java.util.ArrayList;
|
||||
|
||||
public class NormalPreviewSet extends PreviewSet {
|
||||
|
||||
public static final Creator<NormalPreviewSet> CREATOR = new Creator<NormalPreviewSet>() {
|
||||
@Override
|
||||
public NormalPreviewSet createFromParcel(Parcel source) {
|
||||
return new NormalPreviewSet(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NormalPreviewSet[] newArray(int size) {
|
||||
return new NormalPreviewSet[size];
|
||||
}
|
||||
};
|
||||
private IntList mPositionList = new IntList();
|
||||
private ArrayList<String> mImageKeyList = new ArrayList<>();
|
||||
private ArrayList<String> mImageUrlList = new ArrayList<>();
|
||||
@@ -34,6 +45,20 @@ public class NormalPreviewSet extends PreviewSet {
|
||||
private IntList mClipHeightList = new IntList();
|
||||
private ArrayList<String> mPageUrlList = new ArrayList<>();
|
||||
|
||||
public NormalPreviewSet() {
|
||||
}
|
||||
|
||||
protected NormalPreviewSet(Parcel in) {
|
||||
this.mPositionList = in.readParcelable(IntList.class.getClassLoader());
|
||||
this.mImageKeyList = in.createStringArrayList();
|
||||
this.mImageUrlList = in.createStringArrayList();
|
||||
this.mOffsetXList = in.readParcelable(IntList.class.getClassLoader());
|
||||
this.mOffsetYList = in.readParcelable(IntList.class.getClassLoader());
|
||||
this.mClipWidthList = in.readParcelable(IntList.class.getClassLoader());
|
||||
this.mClipHeightList = in.readParcelable(IntList.class.getClassLoader());
|
||||
this.mPageUrlList = in.createStringArrayList();
|
||||
}
|
||||
|
||||
private String getImageKey(String imageUrl) {
|
||||
int index = imageUrl.indexOf('/');
|
||||
if (index >= 0) {
|
||||
@@ -44,7 +69,7 @@ public class NormalPreviewSet extends PreviewSet {
|
||||
}
|
||||
|
||||
public void addItem(int position, String imageUrl, int xOffset, int yOffset, int width,
|
||||
int height, String pageUrl) {
|
||||
int height, String pageUrl) {
|
||||
mPositionList.add(position);
|
||||
mImageKeyList.add(getImageKey(imageUrl));
|
||||
mImageUrlList.add(imageUrl);
|
||||
@@ -107,30 +132,4 @@ public class NormalPreviewSet extends PreviewSet {
|
||||
dest.writeParcelable(this.mClipHeightList, flags);
|
||||
dest.writeStringList(this.mPageUrlList);
|
||||
}
|
||||
|
||||
public NormalPreviewSet() {
|
||||
}
|
||||
|
||||
protected NormalPreviewSet(Parcel in) {
|
||||
this.mPositionList = in.readParcelable(IntList.class.getClassLoader());
|
||||
this.mImageKeyList = in.createStringArrayList();
|
||||
this.mImageUrlList = in.createStringArrayList();
|
||||
this.mOffsetXList = in.readParcelable(IntList.class.getClassLoader());
|
||||
this.mOffsetYList = in.readParcelable(IntList.class.getClassLoader());
|
||||
this.mClipWidthList = in.readParcelable(IntList.class.getClassLoader());
|
||||
this.mClipHeightList = in.readParcelable(IntList.class.getClassLoader());
|
||||
this.mPageUrlList = in.createStringArrayList();
|
||||
}
|
||||
|
||||
public static final Creator<NormalPreviewSet> CREATOR = new Creator<NormalPreviewSet>() {
|
||||
@Override
|
||||
public NormalPreviewSet createFromParcel(Parcel source) {
|
||||
return new NormalPreviewSet(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NormalPreviewSet[] newArray(int size) {
|
||||
return new NormalPreviewSet[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -24,21 +24,15 @@ import com.hippo.ehviewer.client.exception.ParseException;
|
||||
import com.hippo.util.ExceptionUtils;
|
||||
import com.hippo.util.JsoupUtils;
|
||||
import com.hippo.yorozuya.AssertUtils;
|
||||
import java.util.List;
|
||||
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.select.Elements;
|
||||
|
||||
public class FavoritesParser {
|
||||
import java.util.List;
|
||||
|
||||
public static class Result {
|
||||
public String[] catArray; // Size 10
|
||||
public int[] countArray; // Size 10
|
||||
public int pages;
|
||||
public int nextPage;
|
||||
public List<GalleryInfo> galleryInfoList;
|
||||
}
|
||||
public class FavoritesParser {
|
||||
|
||||
public static Result parse(String body) throws Exception {
|
||||
if (body.contains("This page requires you to log on.</p>")) {
|
||||
@@ -77,4 +71,12 @@ public class FavoritesParser {
|
||||
|
||||
return re;
|
||||
}
|
||||
|
||||
public static class Result {
|
||||
public String[] catArray; // Size 10
|
||||
public int[] countArray; // Size 10
|
||||
public int pages;
|
||||
public int nextPage;
|
||||
public List<GalleryInfo> galleryInfoList;
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ package com.hippo.ehviewer.client.parser;
|
||||
import com.hippo.ehviewer.client.EhUrl;
|
||||
import com.hippo.ehviewer.client.exception.ParseException;
|
||||
import com.hippo.util.ExceptionUtils;
|
||||
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
|
||||
@@ -18,8 +18,8 @@ package com.hippo.ehviewer.client.parser;
|
||||
|
||||
import com.hippo.ehviewer.client.EhUtils;
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo;
|
||||
|
||||
import com.hippo.yorozuya.NumberUtils;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.hippo.ehviewer.client.parser;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.ehviewer.Settings;
|
||||
import com.hippo.ehviewer.client.EhUrl;
|
||||
import com.hippo.ehviewer.client.EhUtils;
|
||||
@@ -37,6 +38,15 @@ import com.hippo.util.JsoupUtils;
|
||||
import com.hippo.util.MutableBoolean;
|
||||
import com.hippo.yorozuya.NumberUtils;
|
||||
import com.hippo.yorozuya.StringUtils;
|
||||
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.nodes.Node;
|
||||
import org.jsoup.select.Elements;
|
||||
import org.jsoup.select.NodeTraversor;
|
||||
import org.jsoup.select.NodeVisitor;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
@@ -47,13 +57,6 @@ import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.nodes.Node;
|
||||
import org.jsoup.select.Elements;
|
||||
import org.jsoup.select.NodeTraversor;
|
||||
import org.jsoup.select.NodeVisitor;
|
||||
|
||||
public class GalleryDetailParser {
|
||||
|
||||
@@ -74,16 +77,15 @@ public class GalleryDetailParser {
|
||||
private static final GalleryCommentList EMPTY_GALLERY_COMMENT_ARRAY = new GalleryCommentList(new GalleryComment[0], false);
|
||||
|
||||
private static final DateFormat WEB_COMMENT_DATE_FORMAT = new SimpleDateFormat("dd MMMMM yyyy, HH:mm z", Locale.US);
|
||||
|
||||
static {
|
||||
WEB_COMMENT_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
}
|
||||
|
||||
private static final String OFFENSIVE_STRING =
|
||||
"<p>(And if you choose to ignore this warning, you lose all rights to complain about it in the future.)</p>";
|
||||
private static final String PINING_STRING =
|
||||
"<p>This gallery is pining for the fjords.</p>";
|
||||
|
||||
static {
|
||||
WEB_COMMENT_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
}
|
||||
|
||||
public static GalleryDetail parse(String body) throws EhException {
|
||||
if (body.contains(OFFENSIVE_STRING)) {
|
||||
throw new OffensiveException();
|
||||
@@ -491,7 +493,8 @@ public class GalleryDetailParser {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tail(Node node, int depth) { }
|
||||
public void tail(Node node, int depth) {
|
||||
}
|
||||
}, chd);
|
||||
|
||||
return new GalleryCommentList(list.toArray(new GalleryComment[list.size()]), hasMore.value);
|
||||
@@ -663,8 +666,8 @@ public class GalleryDetailParser {
|
||||
continue;
|
||||
}
|
||||
String imageUrl = ParserUtils.trim(m.group(3));
|
||||
int xOffset = ParserUtils.parseInt(m.group(4), 0);
|
||||
int yOffset = 0;
|
||||
int xOffset = ParserUtils.parseInt(m.group(4), 0);
|
||||
int yOffset = 0;
|
||||
int width = ParserUtils.parseInt(m.group(1), 0);
|
||||
if (width <= 0) {
|
||||
continue;
|
||||
|
||||
@@ -17,14 +17,15 @@
|
||||
package com.hippo.ehviewer.client.parser;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.ehviewer.client.EhUrl;
|
||||
import com.hippo.yorozuya.NumberUtils;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Like http://exhentai.org/g/1234567/a1b2c3d4e5<br>
|
||||
*
|
||||
*/
|
||||
public final class GalleryDetailUrlParser {
|
||||
|
||||
|
||||
@@ -18,7 +18,9 @@ package com.hippo.ehviewer.client.parser;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.hippo.ehviewer.EhDB;
|
||||
import com.hippo.ehviewer.client.EhUtils;
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo;
|
||||
@@ -27,15 +29,17 @@ import com.hippo.ehviewer.client.exception.ParseException;
|
||||
import com.hippo.util.ExceptionUtils;
|
||||
import com.hippo.util.JsoupUtils;
|
||||
import com.hippo.yorozuya.NumberUtils;
|
||||
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.select.Elements;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.select.Elements;
|
||||
|
||||
public class GalleryListParser {
|
||||
|
||||
@@ -47,26 +51,19 @@ public class GalleryListParser {
|
||||
private static final Pattern PATTERN_PAGES = Pattern.compile("(\\d+) page");
|
||||
private static final Pattern PATTERN_NEXT_PAGE = Pattern.compile("page=(\\d+)");
|
||||
|
||||
private static final String[][] FAVORITE_SLOT_RGB = new String[][] {
|
||||
new String[] { "0", "0", "0"},
|
||||
new String[] { "240", "0", "0"},
|
||||
new String[] { "240", "160", "0"},
|
||||
new String[] { "208", "208", "0"},
|
||||
new String[] { "0", "128", "0"},
|
||||
new String[] { "144", "240", "64"},
|
||||
new String[] { "64", "176", "240"},
|
||||
new String[] { "0", "0", "240"},
|
||||
new String[] { "80", "0", "128"},
|
||||
new String[] { "224", "128", "224"},
|
||||
private static final String[][] FAVORITE_SLOT_RGB = new String[][]{
|
||||
new String[]{"0", "0", "0"},
|
||||
new String[]{"240", "0", "0"},
|
||||
new String[]{"240", "160", "0"},
|
||||
new String[]{"208", "208", "0"},
|
||||
new String[]{"0", "128", "0"},
|
||||
new String[]{"144", "240", "64"},
|
||||
new String[]{"64", "176", "240"},
|
||||
new String[]{"0", "0", "240"},
|
||||
new String[]{"80", "0", "128"},
|
||||
new String[]{"224", "128", "224"},
|
||||
};
|
||||
|
||||
public static class Result {
|
||||
public int pages;
|
||||
public int nextPage;
|
||||
public boolean noWatchedTags;
|
||||
public List<GalleryInfo> galleryInfoList;
|
||||
}
|
||||
|
||||
private static int parsePages(Document d, String body) throws ParseException {
|
||||
try {
|
||||
Elements es = d.getElementsByClass("ptt").first().child(0).child(0).children();
|
||||
@@ -357,4 +354,11 @@ public class GalleryListParser {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static class Result {
|
||||
public int pages;
|
||||
public int nextPage;
|
||||
public boolean noWatchedTags;
|
||||
public List<GalleryInfo> galleryInfoList;
|
||||
}
|
||||
}
|
||||
@@ -17,13 +17,16 @@
|
||||
package com.hippo.ehviewer.client.parser;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.hippo.ehviewer.client.exception.ParseException;
|
||||
import com.hippo.yorozuya.StringUtils;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class GalleryPageApiParser {
|
||||
|
||||
private static final Pattern PATTERN_IMAGE_URL = Pattern.compile("<img[^>]*src=\"([^\"]+)\" style");
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
package com.hippo.ehviewer.client.parser;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.hippo.ehviewer.client.exception.ParseException;
|
||||
import com.hippo.yorozuya.StringUtils;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
package com.hippo.ehviewer.client.parser;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.ehviewer.client.EhUrl;
|
||||
import com.hippo.yorozuya.NumberUtils;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
@@ -18,18 +18,19 @@ package com.hippo.ehviewer.client.parser;
|
||||
|
||||
import com.hippo.ehviewer.client.exception.EhException;
|
||||
import com.hippo.util.ExceptionUtils;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class GalleryTokenApiParser {
|
||||
|
||||
/**
|
||||
* {
|
||||
* "tokenlist": [
|
||||
* {
|
||||
* "gid":618395,
|
||||
* "token":"0439fa3666"
|
||||
* }
|
||||
* ]
|
||||
* "tokenlist": [
|
||||
* {
|
||||
* "gid":618395,
|
||||
* "token":"0439fa3666"
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
*/
|
||||
public static String parse(String body) throws Exception {
|
||||
|
||||
@@ -18,9 +18,11 @@ package com.hippo.ehviewer.client.parser;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.hippo.ehviewer.client.EhUrl;
|
||||
import com.hippo.ehviewer.client.exception.ParseException;
|
||||
import com.hippo.util.ExceptionUtils;
|
||||
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
|
||||
@@ -23,22 +23,21 @@ import org.json.JSONObject;
|
||||
|
||||
public class RateGalleryParser {
|
||||
|
||||
public static class Result {
|
||||
public float rating;
|
||||
public int ratingCount;
|
||||
}
|
||||
|
||||
public static Result parse(String body) throws Exception {
|
||||
try {
|
||||
JSONObject jsonObject = new JSONObject(body);
|
||||
Result result = new Result();
|
||||
result.rating = (float)jsonObject.getDouble("rating_avg");
|
||||
result.rating = (float) jsonObject.getDouble("rating_avg");
|
||||
result.ratingCount = jsonObject.getInt("rating_cnt");
|
||||
return result;
|
||||
} catch (JSONException e) {
|
||||
Exception exception = new ParseException("Can't parse rate gallery", body);
|
||||
exception.initCause(e);
|
||||
Exception exception = new ParseException("Can't parse rate gallery", body, e);
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Result {
|
||||
public float rating;
|
||||
public int ratingCount;
|
||||
}
|
||||
}
|
||||
@@ -21,13 +21,6 @@ import org.json.JSONObject;
|
||||
|
||||
public class VoteCommentParser {
|
||||
|
||||
public static class Result {
|
||||
public long id;
|
||||
public int score;
|
||||
public int vote;
|
||||
public int expectVote;
|
||||
}
|
||||
|
||||
// {"comment_id":1253922,"comment_score":-19,"comment_vote":0}
|
||||
public static Result parse(String body, int vote) throws JSONException {
|
||||
Result result = new Result();
|
||||
@@ -38,4 +31,11 @@ public class VoteCommentParser {
|
||||
result.expectVote = vote;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static class Result {
|
||||
public long id;
|
||||
public int score;
|
||||
public int vote;
|
||||
public int expectVote;
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,10 @@ import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.ehviewer.EhDB;
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo;
|
||||
import com.hippo.ehviewer.dao.DownloadInfo;
|
||||
@@ -39,6 +41,7 @@ import com.hippo.yorozuya.SimpleHandler;
|
||||
import com.hippo.yorozuya.collect.LongList;
|
||||
import com.hippo.yorozuya.collect.SparseIJArray;
|
||||
import com.hippo.yorozuya.collect.SparseJLArray;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -52,9 +55,13 @@ import java.util.Map;
|
||||
public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
|
||||
private static final String TAG = DownloadManager.class.getSimpleName();
|
||||
|
||||
private static final Comparator<DownloadInfo> DATE_DESC_COMPARATOR = new Comparator<DownloadInfo>() {
|
||||
@Override
|
||||
public int compare(DownloadInfo lhs, DownloadInfo rhs) {
|
||||
return lhs.time - rhs.time > 0 ? -1 : 1;
|
||||
}
|
||||
};
|
||||
private final Context mContext;
|
||||
|
||||
// All download info list
|
||||
private final LinkedList<DownloadInfo> mAllInfoList;
|
||||
// All download info map
|
||||
@@ -67,20 +74,16 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
private final LinkedList<DownloadInfo> mDefaultInfoList;
|
||||
// Store download info wait to start
|
||||
private final LinkedList<DownloadInfo> mWaitList;
|
||||
|
||||
private final SpeedReminder mSpeedReminder;
|
||||
|
||||
private final List<DownloadInfoListener> mDownloadInfoListeners;
|
||||
private final ConcurrentPool<NotifyTask> mNotifyTaskPool = new ConcurrentPool<>(5);
|
||||
@Nullable
|
||||
private DownloadListener mDownloadListener;
|
||||
private final List<DownloadInfoListener> mDownloadInfoListeners;
|
||||
|
||||
@Nullable
|
||||
private DownloadInfo mCurrentTask;
|
||||
@Nullable
|
||||
private SpiderQueen mCurrentSpider;
|
||||
|
||||
private final ConcurrentPool<NotifyTask> mNotifyTaskPool = new ConcurrentPool<>(5);
|
||||
|
||||
public DownloadManager(Context context) {
|
||||
mContext = context;
|
||||
|
||||
@@ -145,7 +148,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (DownloadLabel raw: mLabelList) {
|
||||
for (DownloadLabel raw : mLabelList) {
|
||||
if (label.equals(raw.getLabel())) {
|
||||
return true;
|
||||
}
|
||||
@@ -230,7 +233,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
// Notify state update
|
||||
List<DownloadInfo> list = getInfoListForLabel(info.label);
|
||||
if (list != null) {
|
||||
for (DownloadInfoListener l: mDownloadInfoListeners) {
|
||||
for (DownloadInfoListener l : mDownloadInfoListeners) {
|
||||
l.onUpdate(info, list);
|
||||
}
|
||||
}
|
||||
@@ -256,7 +259,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
// Notify state update
|
||||
List<DownloadInfo> list = getInfoListForLabel(info.label);
|
||||
if (list != null) {
|
||||
for (DownloadInfoListener l: mDownloadInfoListeners) {
|
||||
for (DownloadInfoListener l : mDownloadInfoListeners) {
|
||||
l.onUpdate(info, list);
|
||||
}
|
||||
}
|
||||
@@ -289,7 +292,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
EhDB.putDownloadInfo(info);
|
||||
|
||||
// Notify
|
||||
for (DownloadInfoListener l: mDownloadInfoListeners) {
|
||||
for (DownloadInfoListener l : mDownloadInfoListeners) {
|
||||
l.onAdd(info, list, list.size() - 1);
|
||||
}
|
||||
// Make sure download is running
|
||||
@@ -326,7 +329,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
|
||||
if (update) {
|
||||
// Notify Listener
|
||||
for (DownloadInfoListener l: mDownloadInfoListeners) {
|
||||
for (DownloadInfoListener l : mDownloadInfoListeners) {
|
||||
l.onUpdateAll();
|
||||
}
|
||||
// Ensure download
|
||||
@@ -339,7 +342,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
// Start all STATE_NONE and STATE_FAILED item
|
||||
LinkedList<DownloadInfo> allInfoList = mAllInfoList;
|
||||
LinkedList<DownloadInfo> waitList = mWaitList;
|
||||
for (DownloadInfo info: allInfoList) {
|
||||
for (DownloadInfo info : allInfoList) {
|
||||
if (info.state == DownloadInfo.STATE_NONE || info.state == DownloadInfo.STATE_FAILED) {
|
||||
update = true;
|
||||
// Set state DownloadInfo.STATE_WAIT
|
||||
@@ -353,7 +356,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
|
||||
if (update) {
|
||||
// Notify Listener
|
||||
for (DownloadInfoListener l: mDownloadInfoListeners) {
|
||||
for (DownloadInfoListener l : mDownloadInfoListeners) {
|
||||
l.onUpdateAll();
|
||||
}
|
||||
// Ensure download
|
||||
@@ -362,7 +365,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
}
|
||||
|
||||
public void addDownload(List<DownloadInfo> downloadInfoList) {
|
||||
for (DownloadInfo info: downloadInfoList) {
|
||||
for (DownloadInfo info : downloadInfoList) {
|
||||
if (containDownloadInfo(info.gid)) {
|
||||
// Contain
|
||||
return;
|
||||
@@ -401,13 +404,13 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
Collections.sort(mAllInfoList, DATE_DESC_COMPARATOR);
|
||||
|
||||
// Notify
|
||||
for (DownloadInfoListener l: mDownloadInfoListeners) {
|
||||
for (DownloadInfoListener l : mDownloadInfoListeners) {
|
||||
l.onReload();
|
||||
}
|
||||
}
|
||||
|
||||
public void addDownloadLabel(List<DownloadLabel> downloadLabelList) {
|
||||
for (DownloadLabel label: downloadLabelList) {
|
||||
for (DownloadLabel label : downloadLabelList) {
|
||||
String labelString = label.getLabel();
|
||||
if (!containLabel(labelString)) {
|
||||
mMap.put(labelString, new LinkedList<DownloadInfo>());
|
||||
@@ -444,19 +447,18 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
EhDB.putDownloadInfo(info);
|
||||
|
||||
// Notify
|
||||
for (DownloadInfoListener l: mDownloadInfoListeners) {
|
||||
for (DownloadInfoListener l : mDownloadInfoListeners) {
|
||||
l.onAdd(info, list, list.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void stopDownload(long gid) {
|
||||
DownloadInfo info = stopDownloadInternal(gid);
|
||||
if (info != null) {
|
||||
// Update listener
|
||||
List<DownloadInfo> list = getInfoListForLabel(info.label);
|
||||
if (list != null) {
|
||||
for (DownloadInfoListener l: mDownloadInfoListeners) {
|
||||
for (DownloadInfoListener l : mDownloadInfoListeners) {
|
||||
l.onUpdate(info, list);
|
||||
}
|
||||
}
|
||||
@@ -471,7 +473,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
// Update listener
|
||||
List<DownloadInfo> list = getInfoListForLabel(info.label);
|
||||
if (list != null) {
|
||||
for (DownloadInfoListener l: mDownloadInfoListeners) {
|
||||
for (DownloadInfoListener l : mDownloadInfoListeners) {
|
||||
l.onUpdate(info, list);
|
||||
}
|
||||
}
|
||||
@@ -484,7 +486,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
stopRangeDownloadInternal(gidList);
|
||||
|
||||
// Update listener
|
||||
for (DownloadInfoListener l: mDownloadInfoListeners) {
|
||||
for (DownloadInfoListener l : mDownloadInfoListeners) {
|
||||
l.onUpdateAll();
|
||||
}
|
||||
|
||||
@@ -505,7 +507,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
stopCurrentDownloadInternal();
|
||||
|
||||
// Notify mDownloadInfoListener
|
||||
for (DownloadInfoListener l: mDownloadInfoListeners) {
|
||||
for (DownloadInfoListener l : mDownloadInfoListeners) {
|
||||
l.onUpdateAll();
|
||||
}
|
||||
}
|
||||
@@ -528,7 +530,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
if (index >= 0) {
|
||||
list.remove(info);
|
||||
// Update listener
|
||||
for (DownloadInfoListener l: mDownloadInfoListeners) {
|
||||
for (DownloadInfoListener l : mDownloadInfoListeners) {
|
||||
l.onRemove(info, list, index);
|
||||
}
|
||||
}
|
||||
@@ -565,7 +567,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
}
|
||||
|
||||
// Update listener
|
||||
for (DownloadInfoListener l: mDownloadInfoListeners) {
|
||||
for (DownloadInfoListener l : mDownloadInfoListeners) {
|
||||
l.onReload();
|
||||
}
|
||||
|
||||
@@ -626,7 +628,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
return stopCurrentDownloadInternal();
|
||||
}
|
||||
|
||||
for (Iterator<DownloadInfo> iterator = mWaitList.iterator(); iterator.hasNext();) {
|
||||
for (Iterator<DownloadInfo> iterator = mWaitList.iterator(); iterator.hasNext(); ) {
|
||||
DownloadInfo info = iterator.next();
|
||||
if (info.gid == gid) {
|
||||
// Remove from wait list
|
||||
@@ -686,7 +688,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
}
|
||||
|
||||
// Check all in wait list
|
||||
for (Iterator<DownloadInfo> iterator = mWaitList.iterator(); iterator.hasNext();) {
|
||||
for (Iterator<DownloadInfo> iterator = mWaitList.iterator(); iterator.hasNext(); ) {
|
||||
DownloadInfo info = iterator.next();
|
||||
if (gidList.contains(info.gid)) {
|
||||
// Remove from wait list
|
||||
@@ -715,7 +717,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
return;
|
||||
}
|
||||
|
||||
for (DownloadInfo info: list) {
|
||||
for (DownloadInfo info : list) {
|
||||
if (ObjectUtils.equal(info.label, label)) {
|
||||
continue;
|
||||
}
|
||||
@@ -735,7 +737,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
EhDB.putDownloadInfo(info);
|
||||
}
|
||||
|
||||
for (DownloadInfoListener l: mDownloadInfoListeners) {
|
||||
for (DownloadInfoListener l : mDownloadInfoListeners) {
|
||||
l.onReload();
|
||||
}
|
||||
}
|
||||
@@ -748,7 +750,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
mLabelList.add(EhDB.addDownloadLabel(label));
|
||||
mMap.put(label, new LinkedList<DownloadInfo>());
|
||||
|
||||
for (DownloadInfoListener l: mDownloadInfoListeners) {
|
||||
for (DownloadInfoListener l : mDownloadInfoListeners) {
|
||||
l.onUpdateLabels();
|
||||
}
|
||||
}
|
||||
@@ -758,7 +760,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
mLabelList.add(toPosition, item);
|
||||
EhDB.moveDownloadLabel(fromPosition, toPosition);
|
||||
|
||||
for (DownloadInfoListener l: mDownloadInfoListeners) {
|
||||
for (DownloadInfoListener l : mDownloadInfoListeners) {
|
||||
l.onUpdateLabels();
|
||||
}
|
||||
}
|
||||
@@ -766,7 +768,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
public void renameLabel(@NonNull String from, @NonNull String to) {
|
||||
// Find in label list
|
||||
boolean found = false;
|
||||
for (DownloadLabel raw: mLabelList) {
|
||||
for (DownloadLabel raw : mLabelList) {
|
||||
if (from.equals(raw.getLabel())) {
|
||||
found = true;
|
||||
raw.setLabel(to);
|
||||
@@ -785,7 +787,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
}
|
||||
|
||||
// Update info label
|
||||
for (DownloadInfo info: list) {
|
||||
for (DownloadInfo info : list) {
|
||||
info.label = to;
|
||||
// Update in DB
|
||||
EhDB.putDownloadInfo(info);
|
||||
@@ -794,7 +796,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
mMap.put(to, list);
|
||||
|
||||
// Notify listener
|
||||
for (DownloadInfoListener l: mDownloadInfoListeners) {
|
||||
for (DownloadInfoListener l : mDownloadInfoListeners) {
|
||||
l.onRenameLabel(from, to);
|
||||
}
|
||||
}
|
||||
@@ -802,7 +804,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
public void deleteLabel(@NonNull String label) {
|
||||
// Find in label list and remove
|
||||
boolean found = false;
|
||||
for (Iterator<DownloadLabel> iterator = mLabelList.iterator(); iterator.hasNext();) {
|
||||
for (Iterator<DownloadLabel> iterator = mLabelList.iterator(); iterator.hasNext(); ) {
|
||||
DownloadLabel raw = iterator.next();
|
||||
if (label.equals(raw.getLabel())) {
|
||||
found = true;
|
||||
@@ -821,7 +823,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
}
|
||||
|
||||
// Update info label
|
||||
for (DownloadInfo info: list) {
|
||||
for (DownloadInfo info : list) {
|
||||
info.label = null;
|
||||
// Update in DB
|
||||
EhDB.putDownloadInfo(info);
|
||||
@@ -832,7 +834,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
Collections.sort(mDefaultInfoList, DATE_DESC_COMPARATOR);
|
||||
|
||||
// Notify listener
|
||||
for (DownloadInfoListener l: mDownloadInfoListeners) {
|
||||
for (DownloadInfoListener l : mDownloadInfoListeners) {
|
||||
l.onChange();
|
||||
}
|
||||
}
|
||||
@@ -911,6 +913,80 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
|
||||
public interface DownloadInfoListener {
|
||||
|
||||
/**
|
||||
* Add the special info to the special position
|
||||
*/
|
||||
void onAdd(@NonNull DownloadInfo info, @NonNull List<DownloadInfo> list, int position);
|
||||
|
||||
/**
|
||||
* The special info is changed
|
||||
*/
|
||||
void onUpdate(@NonNull DownloadInfo info, @NonNull List<DownloadInfo> list);
|
||||
|
||||
/**
|
||||
* Maybe all data is changed, but size is the same
|
||||
*/
|
||||
void onUpdateAll();
|
||||
|
||||
/**
|
||||
* Maybe all data is changed, maybe list is changed
|
||||
*/
|
||||
void onReload();
|
||||
|
||||
/**
|
||||
* The list is gone, use default list please
|
||||
*/
|
||||
void onChange();
|
||||
|
||||
/**
|
||||
* Rename label
|
||||
*/
|
||||
void onRenameLabel(String from, String to);
|
||||
|
||||
/**
|
||||
* Remove the special info from the special position
|
||||
*/
|
||||
void onRemove(@NonNull DownloadInfo info, @NonNull List<DownloadInfo> list, int position);
|
||||
|
||||
void onUpdateLabels();
|
||||
}
|
||||
|
||||
public interface DownloadListener {
|
||||
|
||||
/**
|
||||
* Get 509 error
|
||||
*/
|
||||
void onGet509();
|
||||
|
||||
/**
|
||||
* Start download
|
||||
*/
|
||||
void onStart(DownloadInfo info);
|
||||
|
||||
/**
|
||||
* Update download speed
|
||||
*/
|
||||
void onDownload(DownloadInfo info);
|
||||
|
||||
/**
|
||||
* Update page downloaded
|
||||
*/
|
||||
void onGetPage(DownloadInfo info);
|
||||
|
||||
/**
|
||||
* Download done
|
||||
*/
|
||||
void onFinish(DownloadInfo info);
|
||||
|
||||
/**
|
||||
* Download done
|
||||
*/
|
||||
void onCancel(DownloadInfo info);
|
||||
}
|
||||
|
||||
private class NotifyTask implements Runnable {
|
||||
|
||||
public static final int TYPE_ON_GET_PAGES = 0;
|
||||
@@ -985,7 +1061,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
info.total = mPages;
|
||||
List<DownloadInfo> list = getInfoListForLabel(info.label);
|
||||
if (list != null) {
|
||||
for (DownloadInfoListener l: mDownloadInfoListeners) {
|
||||
for (DownloadInfoListener l : mDownloadInfoListeners) {
|
||||
l.onUpdate(info, list);
|
||||
}
|
||||
}
|
||||
@@ -1016,7 +1092,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
}
|
||||
List<DownloadInfo> list = getInfoListForLabel(info.label);
|
||||
if (list != null) {
|
||||
for (DownloadInfoListener l: mDownloadInfoListeners) {
|
||||
for (DownloadInfoListener l : mDownloadInfoListeners) {
|
||||
l.onUpdate(info, list);
|
||||
}
|
||||
}
|
||||
@@ -1034,7 +1110,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
info.total = mTotal;
|
||||
List<DownloadInfo> list = getInfoListForLabel(info.label);
|
||||
if (list != null) {
|
||||
for (DownloadInfoListener l: mDownloadInfoListeners) {
|
||||
for (DownloadInfoListener l : mDownloadInfoListeners) {
|
||||
l.onUpdate(info, list);
|
||||
}
|
||||
}
|
||||
@@ -1078,7 +1154,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
}
|
||||
List<DownloadInfo> list = getInfoListForLabel(info.label);
|
||||
if (list != null) {
|
||||
for (DownloadInfoListener l: mDownloadInfoListeners) {
|
||||
for (DownloadInfoListener l : mDownloadInfoListeners) {
|
||||
l.onUpdate(info, list);
|
||||
}
|
||||
}
|
||||
@@ -1092,16 +1168,13 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SpeedReminder implements Runnable {
|
||||
|
||||
private boolean mStop = true;
|
||||
|
||||
private long mBytesRead;
|
||||
private long oldSpeed = -1;
|
||||
|
||||
private final SparseIJArray mContentLengthMap = new SparseIJArray();
|
||||
private final SparseIJArray mReceivedSizeMap = new SparseIJArray();
|
||||
private boolean mStop = true;
|
||||
private long mBytesRead;
|
||||
private long oldSpeed = -1;
|
||||
|
||||
public void start() {
|
||||
if (mStop) {
|
||||
@@ -1174,7 +1247,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
}
|
||||
List<DownloadInfo> list = getInfoListForLabel(info.label);
|
||||
if (list != null) {
|
||||
for (DownloadInfoListener l: mDownloadInfoListeners) {
|
||||
for (DownloadInfoListener l : mDownloadInfoListeners) {
|
||||
l.onUpdate(info, list);
|
||||
}
|
||||
}
|
||||
@@ -1187,84 +1260,4 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final Comparator<DownloadInfo> DATE_DESC_COMPARATOR = new Comparator<DownloadInfo>() {
|
||||
@Override
|
||||
public int compare(DownloadInfo lhs, DownloadInfo rhs) {
|
||||
return lhs.time - rhs.time > 0 ? -1 : 1;
|
||||
}
|
||||
};
|
||||
|
||||
public interface DownloadInfoListener {
|
||||
|
||||
/**
|
||||
* Add the special info to the special position
|
||||
*/
|
||||
void onAdd(@NonNull DownloadInfo info, @NonNull List<DownloadInfo> list, int position);
|
||||
|
||||
/**
|
||||
* The special info is changed
|
||||
*/
|
||||
void onUpdate(@NonNull DownloadInfo info, @NonNull List<DownloadInfo> list);
|
||||
|
||||
/**
|
||||
* Maybe all data is changed, but size is the same
|
||||
*/
|
||||
void onUpdateAll();
|
||||
|
||||
/**
|
||||
* Maybe all data is changed, maybe list is changed
|
||||
*/
|
||||
void onReload();
|
||||
|
||||
/**
|
||||
* The list is gone, use default list please
|
||||
*/
|
||||
void onChange();
|
||||
|
||||
/**
|
||||
* Rename label
|
||||
*/
|
||||
void onRenameLabel(String from, String to);
|
||||
|
||||
/**
|
||||
* Remove the special info from the special position
|
||||
*/
|
||||
void onRemove(@NonNull DownloadInfo info, @NonNull List<DownloadInfo> list, int position);
|
||||
|
||||
void onUpdateLabels();
|
||||
}
|
||||
|
||||
public interface DownloadListener {
|
||||
|
||||
/**
|
||||
* Get 509 error
|
||||
*/
|
||||
void onGet509();
|
||||
|
||||
/**
|
||||
* Start download
|
||||
*/
|
||||
void onStart(DownloadInfo info);
|
||||
|
||||
/**
|
||||
* Update download speed
|
||||
*/
|
||||
void onDownload(DownloadInfo info);
|
||||
|
||||
/**
|
||||
* Update page downloaded
|
||||
*/
|
||||
void onGetPage(DownloadInfo info);
|
||||
|
||||
/**
|
||||
* Download done
|
||||
*/
|
||||
void onFinish(DownloadInfo info);
|
||||
|
||||
/**
|
||||
* Download done
|
||||
*/
|
||||
void onCancel(DownloadInfo info);
|
||||
}
|
||||
}
|
||||
@@ -28,9 +28,11 @@ import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.client.EhUtils;
|
||||
@@ -45,6 +47,7 @@ import com.hippo.yorozuya.SimpleHandler;
|
||||
import com.hippo.yorozuya.collect.LongList;
|
||||
import com.hippo.yorozuya.collect.SparseJBArray;
|
||||
import com.hippo.yorozuya.collect.SparseJLArray;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@@ -70,7 +73,11 @@ public class DownloadService extends Service implements DownloadManager.Download
|
||||
private static final int ID_DOWNLOADING = 1;
|
||||
private static final int ID_DOWNLOADED = 2;
|
||||
private static final int ID_509 = 3;
|
||||
|
||||
private final static SparseJBArray sItemStateArray = new SparseJBArray();
|
||||
private final static SparseJLArray<String> sItemTitleArray = new SparseJLArray<>();
|
||||
private static int sFailedCount;
|
||||
private static int sFinishedCount;
|
||||
private static int sDownloadedCount;
|
||||
@Nullable
|
||||
private NotificationManager mNotifyManager;
|
||||
@Nullable
|
||||
@@ -81,15 +88,6 @@ public class DownloadService extends Service implements DownloadManager.Download
|
||||
private NotificationDelay mDownloadingDelay;
|
||||
private NotificationDelay mDownloadedDelay;
|
||||
private NotificationDelay m509Delay;
|
||||
|
||||
|
||||
private final static SparseJBArray sItemStateArray = new SparseJBArray();
|
||||
private final static SparseJLArray<String> sItemTitleArray = new SparseJLArray<>();
|
||||
|
||||
private static int sFailedCount;
|
||||
private static int sFinishedCount;
|
||||
private static int sDownloadedCount;
|
||||
|
||||
private String CHANNEL_ID;
|
||||
|
||||
public static void clear() {
|
||||
@@ -104,9 +102,9 @@ public class DownloadService extends Service implements DownloadManager.Download
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
CHANNEL_ID = getPackageName()+".download";
|
||||
CHANNEL_ID = getPackageName() + ".download";
|
||||
mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
mNotifyManager.createNotificationChannel(new NotificationChannel(CHANNEL_ID, getString(R.string.download_service),
|
||||
NotificationManager.IMPORTANCE_LOW));
|
||||
}
|
||||
@@ -433,7 +431,7 @@ public class DownloadService extends Service implements DownloadManager.Download
|
||||
continue;
|
||||
}
|
||||
style.addLine(getString(fin ? R.string.stat_download_done_line_succeeded :
|
||||
R.string.stat_download_done_line_failed, title));
|
||||
R.string.stat_download_done_line_failed, title));
|
||||
}
|
||||
} else {
|
||||
style = null;
|
||||
@@ -473,29 +471,21 @@ public class DownloadService extends Service implements DownloadManager.Download
|
||||
// Avoid frequent notification
|
||||
private static class NotificationDelay implements Runnable {
|
||||
|
||||
@IntDef({OPS_NOTIFY, OPS_CANCEL, OPS_START_FOREGROUND})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
private @interface Ops {}
|
||||
|
||||
private static final int OPS_NOTIFY = 0;
|
||||
private static final int OPS_CANCEL = 1;
|
||||
private static final int OPS_START_FOREGROUND = 2;
|
||||
|
||||
private static final long DELAY = 1000; // 1s
|
||||
|
||||
private Service mService;
|
||||
private final NotificationManager mNotifyManager;
|
||||
private final NotificationCompat.Builder mBuilder;
|
||||
private final int mId;
|
||||
|
||||
private Service mService;
|
||||
private long mLastTime;
|
||||
private boolean mPosted;
|
||||
// false for show, true for cancel
|
||||
@Ops
|
||||
private int mOps;
|
||||
|
||||
public NotificationDelay(Service service, NotificationManager notifyManager,
|
||||
NotificationCompat.Builder builder, int id) {
|
||||
NotificationCompat.Builder builder, int id) {
|
||||
mService = service;
|
||||
mNotifyManager = notifyManager;
|
||||
mBuilder = builder;
|
||||
@@ -577,5 +567,10 @@ public class DownloadService extends Service implements DownloadManager.Download
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@IntDef({OPS_NOTIFY, OPS_CANCEL, OPS_START_FOREGROUND})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
private @interface Ops {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import com.hippo.a7zip.PropID;
|
||||
import com.hippo.a7zip.PropType;
|
||||
import com.hippo.a7zip.SequentialOutStream;
|
||||
import com.hippo.unifile.UniRandomAccessFile;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
@@ -31,126 +32,126 @@ import java.util.List;
|
||||
|
||||
class A7ZipArchive implements Closeable {
|
||||
|
||||
private InArchive archive;
|
||||
|
||||
private A7ZipArchive(InArchive archive) {
|
||||
this.archive = archive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
archive.close();
|
||||
}
|
||||
|
||||
private static boolean isSupportedFilename(String name) {
|
||||
for (String extension : GalleryProvider2.SUPPORT_IMAGE_EXTENSIONS) {
|
||||
if (name.endsWith(extension)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
List<A7ZipArchiveEntry> getArchiveEntries() {
|
||||
List<A7ZipArchiveEntry> entries = new ArrayList<>();
|
||||
|
||||
for (int i = 0, n = archive.getNumberOfEntries(); i < n; i++) {
|
||||
if (!archive.getEntryBooleanProperty(i, PropID.ENCRYPTED)
|
||||
&& !archive.getEntryBooleanProperty(i, PropID.IS_DIR)
|
||||
&& !archive.getEntryBooleanProperty(i, PropID.IS_VOLUME)
|
||||
&& !archive.getEntryBooleanProperty(i, PropID.SOLID)) {
|
||||
String path = archive.getEntryPath(i);
|
||||
if (isSupportedFilename(path.toLowerCase())) {
|
||||
entries.add(new A7ZipArchiveEntry(archive, i, path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
static A7ZipArchive create(UniRandomAccessFile file) throws ArchiveException {
|
||||
InStream store = new UniRandomAccessFileInStream(file);
|
||||
InArchive archive = InArchive.open(store);
|
||||
if ((archive.getArchivePropertyType(PropID.ENCRYPTED) == PropType.BOOL && archive.getArchiveBooleanProperty(PropID.ENCRYPTED))
|
||||
|| (archive.getArchivePropertyType(PropID.SOLID) == PropType.BOOL && archive.getArchiveBooleanProperty(PropID.SOLID))
|
||||
|| (archive.getArchivePropertyType(PropID.IS_VOLUME) == PropType.BOOL && archive.getArchiveBooleanProperty(PropID.IS_VOLUME))) {
|
||||
throw new ArchiveException("Unsupported archive");
|
||||
}
|
||||
return new A7ZipArchive(archive);
|
||||
}
|
||||
|
||||
static class A7ZipArchiveEntry {
|
||||
|
||||
private InArchive archive;
|
||||
private int index;
|
||||
private String path;
|
||||
|
||||
private A7ZipArchiveEntry(InArchive archive, int index, String path) {
|
||||
this.archive = archive;
|
||||
this.index = index;
|
||||
this.path = path;
|
||||
private A7ZipArchive(InArchive archive) {
|
||||
this.archive = archive;
|
||||
}
|
||||
|
||||
String getPath() {
|
||||
return path;
|
||||
private static boolean isSupportedFilename(String name) {
|
||||
for (String extension : GalleryProvider2.SUPPORT_IMAGE_EXTENSIONS) {
|
||||
if (name.endsWith(extension)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void extract(OutputStream os) throws ArchiveException {
|
||||
archive.extractEntry(index, new OutputStreamSequentialOutStream(os));
|
||||
}
|
||||
}
|
||||
|
||||
private static class UniRandomAccessFileInStream implements InStream {
|
||||
|
||||
private UniRandomAccessFile file;
|
||||
|
||||
public UniRandomAccessFileInStream(UniRandomAccessFile file) {
|
||||
this.file = file;
|
||||
static A7ZipArchive create(UniRandomAccessFile file) throws ArchiveException {
|
||||
InStream store = new UniRandomAccessFileInStream(file);
|
||||
InArchive archive = InArchive.open(store);
|
||||
if ((archive.getArchivePropertyType(PropID.ENCRYPTED) == PropType.BOOL && archive.getArchiveBooleanProperty(PropID.ENCRYPTED))
|
||||
|| (archive.getArchivePropertyType(PropID.SOLID) == PropType.BOOL && archive.getArchiveBooleanProperty(PropID.SOLID))
|
||||
|| (archive.getArchivePropertyType(PropID.IS_VOLUME) == PropType.BOOL && archive.getArchiveBooleanProperty(PropID.IS_VOLUME))) {
|
||||
throw new ArchiveException("Unsupported archive");
|
||||
}
|
||||
return new A7ZipArchive(archive);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seek(long pos) throws IOException {
|
||||
file.seek(pos);
|
||||
public void close() {
|
||||
archive.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long tell() throws IOException {
|
||||
return file.getFilePointer();
|
||||
List<A7ZipArchiveEntry> getArchiveEntries() {
|
||||
List<A7ZipArchiveEntry> entries = new ArrayList<>();
|
||||
|
||||
for (int i = 0, n = archive.getNumberOfEntries(); i < n; i++) {
|
||||
if (!archive.getEntryBooleanProperty(i, PropID.ENCRYPTED)
|
||||
&& !archive.getEntryBooleanProperty(i, PropID.IS_DIR)
|
||||
&& !archive.getEntryBooleanProperty(i, PropID.IS_VOLUME)
|
||||
&& !archive.getEntryBooleanProperty(i, PropID.SOLID)) {
|
||||
String path = archive.getEntryPath(i);
|
||||
if (isSupportedFilename(path.toLowerCase())) {
|
||||
entries.add(new A7ZipArchiveEntry(archive, i, path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long size() throws IOException {
|
||||
return file.length();
|
||||
static class A7ZipArchiveEntry {
|
||||
|
||||
private InArchive archive;
|
||||
private int index;
|
||||
private String path;
|
||||
|
||||
private A7ZipArchiveEntry(InArchive archive, int index, String path) {
|
||||
this.archive = archive;
|
||||
this.index = index;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
void extract(OutputStream os) throws ArchiveException {
|
||||
archive.extractEntry(index, new OutputStreamSequentialOutStream(os));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
return file.read(b, off, len);
|
||||
private static class UniRandomAccessFileInStream implements InStream {
|
||||
|
||||
private UniRandomAccessFile file;
|
||||
|
||||
public UniRandomAccessFileInStream(UniRandomAccessFile file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seek(long pos) throws IOException {
|
||||
file.seek(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long tell() throws IOException {
|
||||
return file.getFilePointer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long size() throws IOException {
|
||||
return file.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
return file.read(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
file.close();
|
||||
private static class OutputStreamSequentialOutStream implements SequentialOutStream {
|
||||
|
||||
private OutputStream stream;
|
||||
|
||||
public OutputStreamSequentialOutStream(OutputStream stream) {
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
stream.write(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class OutputStreamSequentialOutStream implements SequentialOutStream {
|
||||
|
||||
private OutputStream stream;
|
||||
|
||||
public OutputStreamSequentialOutStream(OutputStream stream) {
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
stream.write(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,10 @@ package com.hippo.ehviewer.gallery;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Process;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.a7zip.ArchiveException;
|
||||
import com.hippo.ehviewer.GetText;
|
||||
import com.hippo.ehviewer.R;
|
||||
@@ -30,6 +32,7 @@ import com.hippo.unifile.UniFile;
|
||||
import com.hippo.unifile.UniRandomAccessFile;
|
||||
import com.hippo.util.NaturalComparator;
|
||||
import com.hippo.yorozuya.thread.PriorityThread;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
@@ -43,239 +46,235 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class ArchiveGalleryProvider extends GalleryProvider2 {
|
||||
|
||||
private static final AtomicInteger sIdGenerator = new AtomicInteger();
|
||||
private static final AtomicInteger sIdGenerator = new AtomicInteger();
|
||||
private static Comparator<A7ZipArchive.A7ZipArchiveEntry> naturalComparator = new Comparator<A7ZipArchive.A7ZipArchiveEntry>() {
|
||||
private NaturalComparator comparator = new NaturalComparator();
|
||||
|
||||
private final UniFile file;
|
||||
|
||||
private Thread archiveThread;
|
||||
private Thread decodeThread;
|
||||
|
||||
private volatile int size = STATE_WAIT;
|
||||
private String error;
|
||||
|
||||
private final Stack<Integer> requests = new Stack<>();
|
||||
private final AtomicInteger extractingIndex = new AtomicInteger(GalleryPageView.INVALID_INDEX);
|
||||
private final LinkedHashMap<Integer, InputStream> streams = new LinkedHashMap<>();
|
||||
private final AtomicInteger decodingIndex = new AtomicInteger(GalleryPageView.INVALID_INDEX);
|
||||
|
||||
public ArchiveGalleryProvider(Context context, Uri uri) {
|
||||
file = UniFile.fromUri(context, uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
super.start();
|
||||
|
||||
int id = sIdGenerator.incrementAndGet();
|
||||
|
||||
archiveThread = new PriorityThread(
|
||||
new ArchiveTask(), "ArchiveTask" + '-' + id, Process.THREAD_PRIORITY_BACKGROUND);
|
||||
archiveThread.start();
|
||||
|
||||
decodeThread = new PriorityThread(
|
||||
new DecodeTask(), "DecodeTask" + '-' + id, Process.THREAD_PRIORITY_BACKGROUND);
|
||||
decodeThread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
super.stop();
|
||||
|
||||
if (archiveThread != null) {
|
||||
archiveThread.interrupt();
|
||||
archiveThread = null;
|
||||
}
|
||||
if (decodeThread != null) {
|
||||
decodeThread.interrupt();
|
||||
decodeThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRequest(int index) {
|
||||
boolean inDecodeTask;
|
||||
synchronized (streams) {
|
||||
inDecodeTask = streams.keySet().contains(index) || index == decodingIndex.get();
|
||||
}
|
||||
|
||||
synchronized (requests) {
|
||||
boolean inArchiveTask = requests.contains(index) || index == extractingIndex.get();
|
||||
if (!inArchiveTask && !inDecodeTask) {
|
||||
requests.add(index);
|
||||
requests.notify();
|
||||
}
|
||||
}
|
||||
notifyPageWait(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onForceRequest(int index) {
|
||||
onRequest(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCancelRequest(int index) {
|
||||
synchronized (requests) {
|
||||
requests.remove(Integer.valueOf(index));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getImageFilename(int index) {
|
||||
// TODO
|
||||
return Integer.toString(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean save(int index, @NonNull UniFile file) {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public UniFile save(int index, @NonNull UniFile dir, @NonNull String filename) {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
private class ArchiveTask implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
UniRandomAccessFile uraf = null;
|
||||
if (file != null) {
|
||||
try {
|
||||
uraf = file.createRandomAccessFile("r");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
@Override
|
||||
public int compare(A7ZipArchive.A7ZipArchiveEntry o1, A7ZipArchive.A7ZipArchiveEntry o2) {
|
||||
return comparator.compare(o1.getPath(), o2.getPath());
|
||||
}
|
||||
}
|
||||
if (uraf == null) {
|
||||
size = STATE_ERROR;
|
||||
error = GetText.getString(R.string.error_reading_failed);
|
||||
notifyDataChanged();
|
||||
return;
|
||||
}
|
||||
};
|
||||
private final UniFile file;
|
||||
private final Stack<Integer> requests = new Stack<>();
|
||||
private final AtomicInteger extractingIndex = new AtomicInteger(GalleryPageView.INVALID_INDEX);
|
||||
private final LinkedHashMap<Integer, InputStream> streams = new LinkedHashMap<>();
|
||||
private final AtomicInteger decodingIndex = new AtomicInteger(GalleryPageView.INVALID_INDEX);
|
||||
private Thread archiveThread;
|
||||
private Thread decodeThread;
|
||||
private volatile int size = STATE_WAIT;
|
||||
private String error;
|
||||
|
||||
A7ZipArchive archive = null;
|
||||
try {
|
||||
archive = A7ZipArchive.create(uraf);
|
||||
} catch (ArchiveException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (archive == null) {
|
||||
size = STATE_ERROR;
|
||||
error = GetText.getString(R.string.error_invalid_archive);
|
||||
notifyDataChanged();
|
||||
return;
|
||||
}
|
||||
public ArchiveGalleryProvider(Context context, Uri uri) {
|
||||
file = UniFile.fromUri(context, uri);
|
||||
}
|
||||
|
||||
List<A7ZipArchive.A7ZipArchiveEntry> entries = archive.getArchiveEntries();
|
||||
Collections.sort(entries, naturalComparator);
|
||||
@Override
|
||||
public void start() {
|
||||
super.start();
|
||||
|
||||
// Update size and notify changed
|
||||
size = entries.size();
|
||||
notifyDataChanged();
|
||||
int id = sIdGenerator.incrementAndGet();
|
||||
|
||||
archiveThread = new PriorityThread(
|
||||
new ArchiveTask(), "ArchiveTask" + '-' + id, Process.THREAD_PRIORITY_BACKGROUND);
|
||||
archiveThread.start();
|
||||
|
||||
decodeThread = new PriorityThread(
|
||||
new DecodeTask(), "DecodeTask" + '-' + id, Process.THREAD_PRIORITY_BACKGROUND);
|
||||
decodeThread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
super.stop();
|
||||
|
||||
if (archiveThread != null) {
|
||||
archiveThread.interrupt();
|
||||
archiveThread = null;
|
||||
}
|
||||
if (decodeThread != null) {
|
||||
decodeThread.interrupt();
|
||||
decodeThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRequest(int index) {
|
||||
boolean inDecodeTask;
|
||||
synchronized (streams) {
|
||||
inDecodeTask = streams.containsKey(index) || index == decodingIndex.get();
|
||||
}
|
||||
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
int index;
|
||||
synchronized (requests) {
|
||||
if (requests.isEmpty()) {
|
||||
try {
|
||||
requests.wait();
|
||||
} catch (InterruptedException e) {
|
||||
// Interrupted
|
||||
break;
|
||||
boolean inArchiveTask = requests.contains(index) || index == extractingIndex.get();
|
||||
if (!inArchiveTask && !inDecodeTask) {
|
||||
requests.add(index);
|
||||
requests.notify();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
index = requests.pop();
|
||||
extractingIndex.lazySet(index);
|
||||
}
|
||||
|
||||
// Check index valid
|
||||
if (index < 0 || index >= entries.size()) {
|
||||
extractingIndex.lazySet(GalleryPageView.INVALID_INDEX);
|
||||
notifyPageFailed(index, GetText.getString(R.string.error_out_of_range));
|
||||
continue;
|
||||
}
|
||||
|
||||
Pipe pipe = new Pipe(4 * 1024);
|
||||
|
||||
synchronized (streams) {
|
||||
if (streams.get(index) != null) {
|
||||
continue;
|
||||
}
|
||||
streams.put(index, pipe.getInputStream());
|
||||
streams.notify();
|
||||
}
|
||||
|
||||
try {
|
||||
entries.get(index).extract(pipe.getOutputStream());
|
||||
} catch (ArchiveException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
extractingIndex.lazySet(GalleryPageView.INVALID_INDEX);
|
||||
}
|
||||
}
|
||||
notifyPageWait(index);
|
||||
}
|
||||
}
|
||||
|
||||
private class DecodeTask implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
int index;
|
||||
InputStream stream;
|
||||
synchronized (streams) {
|
||||
if (streams.isEmpty()) {
|
||||
try {
|
||||
streams.wait();
|
||||
} catch (InterruptedException e) {
|
||||
// Interrupted
|
||||
break;
|
||||
protected void onForceRequest(int index) {
|
||||
onRequest(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCancelRequest(int index) {
|
||||
synchronized (requests) {
|
||||
requests.remove(Integer.valueOf(index));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String getImageFilename(int index) {
|
||||
// TODO
|
||||
return Integer.toString(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean save(int index, @NonNull UniFile file) {
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public UniFile save(int index, @NonNull UniFile dir, @NonNull String filename) {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
private class ArchiveTask implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
UniRandomAccessFile uraf = null;
|
||||
if (file != null) {
|
||||
try {
|
||||
uraf = file.createRandomAccessFile("r");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (uraf == null) {
|
||||
size = STATE_ERROR;
|
||||
error = GetText.getString(R.string.error_reading_failed);
|
||||
notifyDataChanged();
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
Iterator<Map.Entry<Integer, InputStream>> iterator = streams.entrySet().iterator();
|
||||
Map.Entry<Integer, InputStream> entry = iterator.next();
|
||||
iterator.remove();
|
||||
index = entry.getKey();
|
||||
stream = entry.getValue();
|
||||
decodingIndex.lazySet(index);
|
||||
A7ZipArchive archive = null;
|
||||
try {
|
||||
archive = A7ZipArchive.create(uraf);
|
||||
} catch (ArchiveException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (archive == null) {
|
||||
size = STATE_ERROR;
|
||||
error = GetText.getString(R.string.error_invalid_archive);
|
||||
notifyDataChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
List<A7ZipArchive.A7ZipArchiveEntry> entries = archive.getArchiveEntries();
|
||||
Collections.sort(entries, naturalComparator);
|
||||
|
||||
// Update size and notify changed
|
||||
size = entries.size();
|
||||
notifyDataChanged();
|
||||
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
int index;
|
||||
synchronized (requests) {
|
||||
if (requests.isEmpty()) {
|
||||
try {
|
||||
requests.wait();
|
||||
} catch (InterruptedException e) {
|
||||
// Interrupted
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
index = requests.pop();
|
||||
extractingIndex.lazySet(index);
|
||||
}
|
||||
|
||||
// Check index valid
|
||||
if (index < 0 || index >= entries.size()) {
|
||||
extractingIndex.lazySet(GalleryPageView.INVALID_INDEX);
|
||||
notifyPageFailed(index, GetText.getString(R.string.error_out_of_range));
|
||||
continue;
|
||||
}
|
||||
|
||||
Pipe pipe = new Pipe(4 * 1024);
|
||||
|
||||
synchronized (streams) {
|
||||
if (streams.get(index) != null) {
|
||||
continue;
|
||||
}
|
||||
streams.put(index, pipe.getInputStream());
|
||||
streams.notify();
|
||||
}
|
||||
|
||||
try {
|
||||
entries.get(index).extract(pipe.getOutputStream());
|
||||
} catch (ArchiveException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
extractingIndex.lazySet(GalleryPageView.INVALID_INDEX);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Image image = Image.decode(stream, true);
|
||||
if (image != null) {
|
||||
notifyPageSucceed(index, image);
|
||||
} else {
|
||||
notifyPageFailed(index, GetText.getString(R.string.error_decoding_failed));
|
||||
}
|
||||
} finally {
|
||||
decodingIndex.lazySet(GalleryPageView.INVALID_INDEX);
|
||||
private class DecodeTask implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
int index;
|
||||
InputStream stream;
|
||||
synchronized (streams) {
|
||||
if (streams.isEmpty()) {
|
||||
try {
|
||||
streams.wait();
|
||||
} catch (InterruptedException e) {
|
||||
// Interrupted
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
Iterator<Map.Entry<Integer, InputStream>> iterator = streams.entrySet().iterator();
|
||||
Map.Entry<Integer, InputStream> entry = iterator.next();
|
||||
iterator.remove();
|
||||
index = entry.getKey();
|
||||
stream = entry.getValue();
|
||||
decodingIndex.lazySet(index);
|
||||
}
|
||||
|
||||
try {
|
||||
Image image = Image.decode(stream, true);
|
||||
if (image != null) {
|
||||
notifyPageSucceed(index, image);
|
||||
} else {
|
||||
notifyPageFailed(index, GetText.getString(R.string.error_decoding_failed));
|
||||
}
|
||||
} finally {
|
||||
decodingIndex.lazySet(GalleryPageView.INVALID_INDEX);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Comparator<A7ZipArchive.A7ZipArchiveEntry> naturalComparator = new Comparator<A7ZipArchive.A7ZipArchiveEntry>() {
|
||||
private NaturalComparator comparator = new NaturalComparator();
|
||||
@Override
|
||||
public int compare(A7ZipArchive.A7ZipArchiveEntry o1, A7ZipArchive.A7ZipArchiveEntry o2) {
|
||||
return comparator.compare(o1.getPath(), o2.getPath());
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -18,8 +18,10 @@ package com.hippo.ehviewer.gallery;
|
||||
|
||||
import android.os.Process;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.ehviewer.GetText;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.glgallery.GalleryPageView;
|
||||
@@ -31,6 +33,7 @@ import com.hippo.yorozuya.FileUtils;
|
||||
import com.hippo.yorozuya.IOUtils;
|
||||
import com.hippo.yorozuya.StringUtils;
|
||||
import com.hippo.yorozuya.thread.PriorityThread;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
@@ -44,7 +47,16 @@ public class DirGalleryProvider extends GalleryProvider2 implements Runnable {
|
||||
|
||||
private static final String TAG = DirGalleryProvider.class.getSimpleName();
|
||||
private static final AtomicInteger sIdGenerator = new AtomicInteger();
|
||||
private static FilenameFilter imageFilter =
|
||||
(dir, name) -> StringUtils.endsWith(name.toLowerCase(), SUPPORT_IMAGE_EXTENSIONS);
|
||||
private static Comparator<UniFile> naturalComparator = new Comparator<UniFile>() {
|
||||
private NaturalComparator comparator = new NaturalComparator();
|
||||
|
||||
@Override
|
||||
public int compare(UniFile o1, UniFile o2) {
|
||||
return comparator.compare(o1.getName(), o2.getName());
|
||||
}
|
||||
};
|
||||
private final UniFile mDir;
|
||||
private final Stack<Integer> mRequests = new Stack<>();
|
||||
private final AtomicInteger mDecodingIndex = new AtomicInteger(GalleryPageView.INVALID_INDEX);
|
||||
@@ -242,15 +254,4 @@ public class DirGalleryProvider extends GalleryProvider2 implements Runnable {
|
||||
|
||||
Log.i(TAG, "ImageDecoder end");
|
||||
}
|
||||
|
||||
private static FilenameFilter imageFilter =
|
||||
(dir, name) -> StringUtils.endsWith(name.toLowerCase(), SUPPORT_IMAGE_EXTENSIONS);
|
||||
|
||||
private static Comparator<UniFile> naturalComparator = new Comparator<UniFile>() {
|
||||
private NaturalComparator comparator = new NaturalComparator();
|
||||
@Override
|
||||
public int compare(UniFile o1, UniFile o2) {
|
||||
return comparator.compare(o1.getName(), o2.getName());
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -17,14 +17,17 @@
|
||||
package com.hippo.ehviewer.gallery;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo;
|
||||
import com.hippo.ehviewer.spider.SpiderQueen;
|
||||
import com.hippo.glgallery.GalleryProvider;
|
||||
import com.hippo.image.Image;
|
||||
import com.hippo.unifile.UniFile;
|
||||
import com.hippo.yorozuya.SimpleHandler;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class EhGalleryProvider extends GalleryProvider2 implements SpiderQueen.OnSpiderListener {
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.hippo.ehviewer.gallery;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.glgallery.GalleryProvider;
|
||||
import com.hippo.unifile.UniFile;
|
||||
|
||||
@@ -35,7 +36,8 @@ public abstract class GalleryProvider2 extends GalleryProvider {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void putStartPage(int page) {}
|
||||
public void putStartPage(int page) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return without extension
|
||||
|
||||
@@ -17,146 +17,147 @@
|
||||
package com.hippo.ehviewer.gallery;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
class Pipe {
|
||||
|
||||
private final int capacity;
|
||||
private final byte[] buffer;
|
||||
private final int capacity;
|
||||
private final byte[] buffer;
|
||||
|
||||
private int head = 0;
|
||||
private int tail = 0;
|
||||
private boolean full = false;
|
||||
private int head = 0;
|
||||
private int tail = 0;
|
||||
private boolean full = false;
|
||||
|
||||
private boolean inClosed = false;
|
||||
private boolean outClosed = false;
|
||||
private boolean inClosed = false;
|
||||
private boolean outClosed = false;
|
||||
|
||||
private InputStream inputStream = new InputStream() {
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
synchronized (Pipe.this) {
|
||||
byte[] bytes = new byte[1];
|
||||
if (read(bytes, 0, 1) != -1) {
|
||||
return bytes[0];
|
||||
} else {
|
||||
return -1;
|
||||
private InputStream inputStream = new InputStream() {
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
synchronized (Pipe.this) {
|
||||
byte[] bytes = new byte[1];
|
||||
if (read(bytes, 0, 1) != -1) {
|
||||
return bytes[0];
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(@NonNull byte[] b, int off, int len) throws IOException {
|
||||
synchronized (Pipe.this) {
|
||||
for (;;) {
|
||||
if (inClosed) {
|
||||
throw new IOException("The InputStream is closed");
|
||||
}
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
@Override
|
||||
public int read(@NonNull byte[] b, int off, int len) throws IOException {
|
||||
synchronized (Pipe.this) {
|
||||
for (; ; ) {
|
||||
if (inClosed) {
|
||||
throw new IOException("The InputStream is closed");
|
||||
}
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (head == tail && !full) {
|
||||
if (outClosed) {
|
||||
// No bytes available and the OutputStream is closed. So it's the end.
|
||||
return -1;
|
||||
} else {
|
||||
// Wait for OutputStream write bytes
|
||||
try {
|
||||
Pipe.this.wait();
|
||||
} catch (InterruptedException e) {
|
||||
throw new IOException("The thread interrupted", e);
|
||||
}
|
||||
if (head == tail && !full) {
|
||||
if (outClosed) {
|
||||
// No bytes available and the OutputStream is closed. So it's the end.
|
||||
return -1;
|
||||
} else {
|
||||
// Wait for OutputStream write bytes
|
||||
try {
|
||||
Pipe.this.wait();
|
||||
} catch (InterruptedException e) {
|
||||
throw new IOException("The thread interrupted", e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int read = Math.min(len, (head < tail ? tail : capacity) - head);
|
||||
System.arraycopy(buffer, head, b, off, read);
|
||||
head += read;
|
||||
if (head == capacity) {
|
||||
head = 0;
|
||||
}
|
||||
full = false;
|
||||
Pipe.this.notifyAll();
|
||||
return read;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int read = Math.min(len, (head < tail ? tail : capacity) - head);
|
||||
System.arraycopy(buffer, head, b, off, read);
|
||||
head += read;
|
||||
if (head == capacity) {
|
||||
head = 0;
|
||||
}
|
||||
full = false;
|
||||
Pipe.this.notifyAll();
|
||||
return read;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
synchronized (Pipe.this) {
|
||||
inClosed = true;
|
||||
Pipe.this.notifyAll();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private OutputStream outputStream = new OutputStream() {
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
synchronized (Pipe.this) {
|
||||
byte[] bytes = new byte[] { (byte) b};
|
||||
write(bytes, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NonNull byte[] b, int off, int len) throws IOException {
|
||||
synchronized (Pipe.this) {
|
||||
while (len != 0) {
|
||||
if (outClosed) {
|
||||
throw new IOException("The OutputStream is closed");
|
||||
}
|
||||
if (inClosed) {
|
||||
throw new IOException("The InputStream is closed");
|
||||
}
|
||||
|
||||
if (head == tail && full) {
|
||||
// The buffer is full, wait for InputStream read bytes
|
||||
try {
|
||||
Pipe.this.wait();
|
||||
} catch (InterruptedException e) {
|
||||
throw new IOException("The thread interrupted", e);
|
||||
@Override
|
||||
public void close() {
|
||||
synchronized (Pipe.this) {
|
||||
inClosed = true;
|
||||
Pipe.this.notifyAll();
|
||||
}
|
||||
} else {
|
||||
int write = Math.min(len, (head <= tail ? capacity : head) - tail);
|
||||
System.arraycopy(b, off, buffer, tail, write);
|
||||
off += write;
|
||||
len -= write;
|
||||
tail += write;
|
||||
if (tail == capacity) {
|
||||
tail = 0;
|
||||
}
|
||||
if (head == tail) {
|
||||
full = true;
|
||||
}
|
||||
Pipe.this.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private OutputStream outputStream = new OutputStream() {
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
synchronized (Pipe.this) {
|
||||
byte[] bytes = new byte[]{(byte) b};
|
||||
write(bytes, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(@NonNull byte[] b, int off, int len) throws IOException {
|
||||
synchronized (Pipe.this) {
|
||||
while (len != 0) {
|
||||
if (outClosed) {
|
||||
throw new IOException("The OutputStream is closed");
|
||||
}
|
||||
if (inClosed) {
|
||||
throw new IOException("The InputStream is closed");
|
||||
}
|
||||
|
||||
if (head == tail && full) {
|
||||
// The buffer is full, wait for InputStream read bytes
|
||||
try {
|
||||
Pipe.this.wait();
|
||||
} catch (InterruptedException e) {
|
||||
throw new IOException("The thread interrupted", e);
|
||||
}
|
||||
} else {
|
||||
int write = Math.min(len, (head <= tail ? capacity : head) - tail);
|
||||
System.arraycopy(b, off, buffer, tail, write);
|
||||
off += write;
|
||||
len -= write;
|
||||
tail += write;
|
||||
if (tail == capacity) {
|
||||
tail = 0;
|
||||
}
|
||||
if (head == tail) {
|
||||
full = true;
|
||||
}
|
||||
Pipe.this.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
synchronized (Pipe.this) {
|
||||
outClosed = true;
|
||||
Pipe.this.notifyAll();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Pipe(int capacity) {
|
||||
this.capacity = capacity;
|
||||
this.buffer = new byte[capacity];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
synchronized (Pipe.this) {
|
||||
outClosed = true;
|
||||
Pipe.this.notifyAll();
|
||||
}
|
||||
InputStream getInputStream() {
|
||||
return inputStream;
|
||||
}
|
||||
};
|
||||
|
||||
Pipe(int capacity) {
|
||||
this.capacity = capacity;
|
||||
this.buffer = new byte[capacity];
|
||||
}
|
||||
|
||||
InputStream getInputStream() {
|
||||
return inputStream;
|
||||
}
|
||||
|
||||
OutputStream getOutputStream() {
|
||||
return outputStream;
|
||||
}
|
||||
OutputStream getOutputStream() {
|
||||
return outputStream;
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,9 @@ package com.hippo.ehviewer.preference;
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.Settings;
|
||||
@@ -91,7 +93,7 @@ public class CleanRedundancyPreference extends TaskPreference {
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
for (UniFile f: files) {
|
||||
for (UniFile f : files) {
|
||||
if (clearFile(f)) {
|
||||
++count;
|
||||
}
|
||||
@@ -110,7 +112,7 @@ public class CleanRedundancyPreference extends TaskPreference {
|
||||
}
|
||||
|
||||
Toast.makeText(mApplication, 0 == count ?
|
||||
mApplication.getString(R.string.settings_download_clean_redundancy_no_redundancy):
|
||||
mApplication.getString(R.string.settings_download_clean_redundancy_no_redundancy) :
|
||||
mApplication.getString(R.string.settings_download_clean_redundancy_done, count), Toast.LENGTH_SHORT).show();
|
||||
super.onPostExecute(o);
|
||||
}
|
||||
|
||||
+3
-1
@@ -18,7 +18,9 @@ package com.hippo.ehviewer.preference;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.hippo.ehviewer.EhDB;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.preference.MessagePreference;
|
||||
@@ -36,7 +38,7 @@ public class ClearDownloadPathCachePreference extends MessagePreference {
|
||||
}
|
||||
|
||||
public ClearDownloadPathCachePreference(Context context,
|
||||
AttributeSet attrs, int defStyleAttr) {
|
||||
AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context);
|
||||
}
|
||||
|
||||
@@ -18,8 +18,10 @@ package com.hippo.ehviewer.preference;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.Settings;
|
||||
import com.hippo.ehviewer.widget.CategoryTable;
|
||||
|
||||
+3
-3
@@ -20,7 +20,9 @@ import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.Settings;
|
||||
import com.hippo.preference.DialogPreference;
|
||||
@@ -29,8 +31,6 @@ import com.hippo.yorozuya.ViewUtils;
|
||||
|
||||
public class ExcludedTagNamespacesPreference extends DialogPreference {
|
||||
|
||||
private View mTableLayout;
|
||||
|
||||
private static final int[] EXCLUDED_TAG_GROUP_RES_ID = {
|
||||
R.id.tag_group_reclass,
|
||||
R.id.tag_group_language,
|
||||
@@ -41,7 +41,6 @@ public class ExcludedTagNamespacesPreference extends DialogPreference {
|
||||
R.id.tag_group_male,
|
||||
R.id.tag_group_female,
|
||||
};
|
||||
|
||||
private static final int[] EXCLUDED_TAG_GROUP_ID = {
|
||||
0x1,
|
||||
0x2,
|
||||
@@ -52,6 +51,7 @@ public class ExcludedTagNamespacesPreference extends DialogPreference {
|
||||
0x40,
|
||||
0x80
|
||||
};
|
||||
private View mTableLayout;
|
||||
|
||||
public ExcludedTagNamespacesPreference(Context context) {
|
||||
super(context);
|
||||
|
||||
@@ -19,60 +19,63 @@ package com.hippo.ehviewer.preference;
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.hippo.ehviewer.AppConfig;
|
||||
import com.hippo.ehviewer.EhDB;
|
||||
import com.hippo.ehviewer.GetText;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.util.ReadableTime;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class ExportDataPreference extends TaskPreference {
|
||||
|
||||
public ExportDataPreference(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public ExportDataPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public ExportDataPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Task onCreateTask() {
|
||||
return new ExportDataTask(getContext());
|
||||
}
|
||||
|
||||
private static class ExportDataTask extends Task {
|
||||
|
||||
public ExportDataTask(@NonNull Context context) {
|
||||
super(context);
|
||||
public ExportDataPreference(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public ExportDataPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public ExportDataPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Object doInBackground(Void... voids) {
|
||||
File dir = AppConfig.getExternalDataDir();
|
||||
if (dir != null) {
|
||||
File file = new File(dir, ReadableTime.getFilenamableTime(System.currentTimeMillis()) + ".db");
|
||||
if (EhDB.exportDB(getApplication(), file)) {
|
||||
return file;
|
||||
protected Task onCreateTask() {
|
||||
return new ExportDataTask(getContext());
|
||||
}
|
||||
|
||||
private static class ExportDataTask extends Task {
|
||||
|
||||
public ExportDataTask(@NonNull Context context) {
|
||||
super(context);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Object o) {
|
||||
Toast.makeText(getApplication(),
|
||||
(o instanceof File)
|
||||
? GetText.getString(R.string.settings_advanced_export_data_to, ((File) o).getPath())
|
||||
: GetText.getString(R.string.settings_advanced_export_data_failed),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
super.onPostExecute(o);
|
||||
@Override
|
||||
protected Object doInBackground(Void... voids) {
|
||||
File dir = AppConfig.getExternalDataDir();
|
||||
if (dir != null) {
|
||||
File file = new File(dir, ReadableTime.getFilenamableTime(System.currentTimeMillis()) + ".db");
|
||||
if (EhDB.exportDB(getApplication(), file)) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Object o) {
|
||||
Toast.makeText(getApplication(),
|
||||
(o instanceof File)
|
||||
? GetText.getString(R.string.settings_advanced_export_data_to, ((File) o).getPath())
|
||||
: GetText.getString(R.string.settings_advanced_export_data_failed),
|
||||
Toast.LENGTH_SHORT).show();
|
||||
super.onPostExecute(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,15 +21,19 @@ import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.client.EhCookieStore;
|
||||
import com.hippo.ehviewer.client.EhUrl;
|
||||
import com.hippo.preference.MessagePreference;
|
||||
import com.hippo.text.Html;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import okhttp3.Cookie;
|
||||
import okhttp3.HttpUrl;
|
||||
|
||||
|
||||
@@ -25,7 +25,9 @@ import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Spinner;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.EhProxySelector;
|
||||
@@ -71,20 +73,20 @@ public class ProxyPreference extends DialogPreference implements View.OnClickLis
|
||||
|
||||
private void updateSummary(int type, String ip, int port) {
|
||||
if ((type == EhProxySelector.TYPE_HTTP || type == EhProxySelector.TYPE_SOCKS)
|
||||
&& (TextUtils.isEmpty(ip) || !InetValidator.isValidInetPort(port)) ) {
|
||||
&& (TextUtils.isEmpty(ip) || !InetValidator.isValidInetPort(port))) {
|
||||
type = EhProxySelector.TYPE_SYSTEM;
|
||||
}
|
||||
|
||||
if (type == EhProxySelector.TYPE_HTTP || type == EhProxySelector.TYPE_SOCKS) {
|
||||
Context context = getContext();
|
||||
setSummary(context.getString(R.string.settings_advanced_proxy_summary_1,
|
||||
getProxyTypeText(context, type),
|
||||
ip,
|
||||
port));
|
||||
getProxyTypeText(context, type),
|
||||
ip,
|
||||
port));
|
||||
} else {
|
||||
Context context = getContext();
|
||||
setSummary(context.getString(R.string.settings_advanced_proxy_summary_2,
|
||||
getProxyTypeText(context, type)));
|
||||
getProxyTypeText(context, type)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,10 +19,12 @@ package com.hippo.ehviewer.preference;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Parcel;
|
||||
import android.preference.Preference;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.EhDB;
|
||||
import com.hippo.ehviewer.R;
|
||||
@@ -36,11 +38,13 @@ import com.hippo.ehviewer.spider.SpiderQueen;
|
||||
import com.hippo.unifile.UniFile;
|
||||
import com.hippo.util.ExceptionUtils;
|
||||
import com.hippo.yorozuya.IOUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
public class RestoreDownloadPreference extends TaskPreference {
|
||||
@@ -123,7 +127,7 @@ public class RestoreDownloadPreference extends TaskPreference {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (UniFile file: files) {
|
||||
for (UniFile file : files) {
|
||||
RestoreItem restoreItem = getRestoreItem(file);
|
||||
if (null != restoreItem) {
|
||||
restoreItemList.add(restoreItem);
|
||||
@@ -184,27 +188,6 @@ public class RestoreDownloadPreference extends TaskPreference {
|
||||
|
||||
private static class RestoreItem extends GalleryInfo {
|
||||
|
||||
public String dirname;
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeString(this.dirname);
|
||||
}
|
||||
|
||||
public RestoreItem() {
|
||||
}
|
||||
|
||||
protected RestoreItem(Parcel in) {
|
||||
super(in);
|
||||
this.dirname = in.readString();
|
||||
}
|
||||
|
||||
public static final Creator<RestoreItem> CREATOR = new Creator<RestoreItem>() {
|
||||
@Override
|
||||
public RestoreItem createFromParcel(Parcel source) {
|
||||
@@ -216,5 +199,25 @@ public class RestoreDownloadPreference extends TaskPreference {
|
||||
return new RestoreItem[size];
|
||||
}
|
||||
};
|
||||
public String dirname;
|
||||
|
||||
public RestoreItem() {
|
||||
}
|
||||
|
||||
protected RestoreItem(Parcel in) {
|
||||
super(in);
|
||||
this.dirname = in.readString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeString(this.dirname);
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
-32
@@ -19,54 +19,58 @@ package com.hippo.ehviewer.preference;
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.client.EhCookieStore;
|
||||
import com.hippo.ehviewer.client.EhUrl;
|
||||
import com.hippo.preference.ActivityPreference;
|
||||
|
||||
import okhttp3.HttpUrl;
|
||||
|
||||
public class SignInRequiredActivityPreference extends ActivityPreference {
|
||||
|
||||
private View view;
|
||||
private View view;
|
||||
|
||||
public SignInRequiredActivityPreference(Context context) {
|
||||
super(context);
|
||||
}
|
||||
public SignInRequiredActivityPreference(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public SignInRequiredActivityPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
public SignInRequiredActivityPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public SignInRequiredActivityPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
public SignInRequiredActivityPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View onCreateView(ViewGroup parent) {
|
||||
return view = super.onCreateView(parent);
|
||||
}
|
||||
@Override
|
||||
public void onBindViewHolder(PreferenceViewHolder holder) {
|
||||
view = holder.itemView;
|
||||
super.onBindViewHolder(holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onClick() {
|
||||
EhCookieStore store = EhApplication.getEhCookieStore(getContext());
|
||||
HttpUrl e = HttpUrl.get(EhUrl.HOST_E);
|
||||
HttpUrl ex = HttpUrl.get(EhUrl.HOST_EX);
|
||||
@Override
|
||||
protected void onClick() {
|
||||
EhCookieStore store = EhApplication.getEhCookieStore(getContext());
|
||||
HttpUrl e = HttpUrl.get(EhUrl.HOST_E);
|
||||
HttpUrl ex = HttpUrl.get(EhUrl.HOST_EX);
|
||||
|
||||
if (store.contains(e, EhCookieStore.KEY_IPD_MEMBER_ID) ||
|
||||
store.contains(e, EhCookieStore.KEY_IPD_PASS_HASH) ||
|
||||
store.contains(ex, EhCookieStore.KEY_IPD_MEMBER_ID) ||
|
||||
store.contains(ex, EhCookieStore.KEY_IPD_PASS_HASH)) {
|
||||
super.onClick();
|
||||
} else {
|
||||
if (view != null) {
|
||||
Snackbar.make(view, R.string.error_please_login_first, 3000).show();
|
||||
} else {
|
||||
Toast.makeText(getContext(), R.string.error_please_login_first, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
if (store.contains(e, EhCookieStore.KEY_IPD_MEMBER_ID) ||
|
||||
store.contains(e, EhCookieStore.KEY_IPD_PASS_HASH) ||
|
||||
store.contains(ex, EhCookieStore.KEY_IPD_MEMBER_ID) ||
|
||||
store.contains(ex, EhCookieStore.KEY_IPD_PASS_HASH)) {
|
||||
super.onClick();
|
||||
} else {
|
||||
if (view != null) {
|
||||
Snackbar.make(view, R.string.error_please_login_first, 3000).show();
|
||||
} else {
|
||||
Toast.makeText(getContext(), R.string.error_please_login_first, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,9 @@ package com.hippo.ehviewer.preference;
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.client.EhUtils;
|
||||
import com.hippo.preference.MessagePreference;
|
||||
|
||||
@@ -22,11 +22,13 @@ import android.os.AsyncTask;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.customview.view.AbsSavedState;
|
||||
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.preference.DialogPreference;
|
||||
@@ -81,15 +83,6 @@ public abstract class TaskPreference extends DialogPreference {
|
||||
@NonNull
|
||||
protected abstract Task onCreateTask();
|
||||
|
||||
@Override
|
||||
public void onActivityDestroy() {
|
||||
super.onActivityDestroy();
|
||||
|
||||
if (null != mTask) {
|
||||
mTask.setPreference(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Parcelable onSaveInstanceState() {
|
||||
final Parcelable superState = super.onSaveInstanceState();
|
||||
@@ -125,23 +118,6 @@ public abstract class TaskPreference extends DialogPreference {
|
||||
}
|
||||
|
||||
private static class SavedState extends AbsSavedState {
|
||||
int asyncTaskId;
|
||||
|
||||
public SavedState(Parcel source) {
|
||||
super(source, SavedState.class.getClassLoader());
|
||||
asyncTaskId = source.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeInt(asyncTaskId);
|
||||
}
|
||||
|
||||
public SavedState(Parcelable superState) {
|
||||
super(superState);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<SavedState> CREATOR =
|
||||
new Parcelable.Creator<SavedState>() {
|
||||
@Override
|
||||
@@ -154,6 +130,22 @@ public abstract class TaskPreference extends DialogPreference {
|
||||
return new SavedState[size];
|
||||
}
|
||||
};
|
||||
int asyncTaskId;
|
||||
|
||||
public SavedState(Parcel source) {
|
||||
super(source, SavedState.class.getClassLoader());
|
||||
asyncTaskId = source.readInt();
|
||||
}
|
||||
|
||||
public SavedState(Parcelable superState) {
|
||||
super(superState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeInt(asyncTaskId);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class Task extends AsyncTask<Void, Void, Object> {
|
||||
|
||||
@@ -19,7 +19,7 @@ package com.hippo.ehviewer.preference;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.preference.Preference;
|
||||
import androidx.preference.Preference;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
|
||||
@@ -26,16 +26,16 @@ import com.hippo.ehviewer.download.DownloadService;
|
||||
* Created by onlymash on 3/25/18.
|
||||
*/
|
||||
|
||||
public class ShortcutsActivity extends Activity{
|
||||
public class ShortcutsActivity extends Activity {
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
String action = null;
|
||||
Intent intent = getIntent();
|
||||
if (intent != null){
|
||||
if (intent != null) {
|
||||
action = intent.getAction();
|
||||
if (action != null && (action.equals(DownloadService.ACTION_START_ALL) ||
|
||||
action.equals(DownloadService.ACTION_STOP_ALL))){
|
||||
action.equals(DownloadService.ACTION_STOP_ALL))) {
|
||||
startService(new Intent(this, DownloadService.class).setAction(action));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,9 @@ package com.hippo.ehviewer.spider;
|
||||
import android.content.Context;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.beerbelly.SimpleDiskCache;
|
||||
import com.hippo.ehviewer.EhDB;
|
||||
import com.hippo.ehviewer.Settings;
|
||||
@@ -37,6 +39,7 @@ import com.hippo.yorozuya.FileUtils;
|
||||
import com.hippo.yorozuya.IOUtils;
|
||||
import com.hippo.yorozuya.MathUtils;
|
||||
import com.hippo.yorozuya.Utilities;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
@@ -44,33 +47,23 @@ import java.util.Locale;
|
||||
|
||||
public final class SpiderDen {
|
||||
|
||||
@Nullable
|
||||
private final UniFile mDownloadDir;
|
||||
private volatile int mMode = SpiderQueen.MODE_READ;
|
||||
private final long mGid;
|
||||
|
||||
@Nullable
|
||||
private static SimpleDiskCache sCache;
|
||||
@Nullable
|
||||
private final UniFile mDownloadDir;
|
||||
private final long mGid;
|
||||
private volatile int mMode = SpiderQueen.MODE_READ;
|
||||
|
||||
public SpiderDen(GalleryInfo galleryInfo) {
|
||||
mGid = galleryInfo.gid;
|
||||
mDownloadDir = getGalleryDownloadDir(galleryInfo);
|
||||
}
|
||||
|
||||
public static void initialize(Context context) {
|
||||
sCache = new SimpleDiskCache(new File(context.getCacheDir(), "image"),
|
||||
MathUtils.clamp(Settings.getReadCacheSize(), 40, 640) * 1024 * 1024);
|
||||
}
|
||||
|
||||
private static class StartWithFilenameFilter implements FilenameFilter {
|
||||
|
||||
private final String mPrefix;
|
||||
|
||||
public StartWithFilenameFilter(String prefix) {
|
||||
mPrefix = prefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(UniFile dir, String filename) {
|
||||
return filename.startsWith(mPrefix);
|
||||
}
|
||||
}
|
||||
|
||||
public static UniFile getGalleryDownloadDir(GalleryInfo galleryInfo) {
|
||||
UniFile dir = Settings.getDownloadLocation();
|
||||
if (dir != null) {
|
||||
@@ -116,9 +109,23 @@ public final class SpiderDen {
|
||||
}
|
||||
}
|
||||
|
||||
public SpiderDen(GalleryInfo galleryInfo) {
|
||||
mGid = galleryInfo.gid;
|
||||
mDownloadDir = getGalleryDownloadDir(galleryInfo);
|
||||
/**
|
||||
* @param extension with dot
|
||||
*/
|
||||
public static String generateImageFilename(int index, String extension) {
|
||||
return String.format(Locale.US, "%08d%s", index + 1, extension);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static UniFile findImageFile(UniFile dir, int index) {
|
||||
for (String extension : GalleryProvider2.SUPPORT_IMAGE_EXTENSIONS) {
|
||||
String filename = generateImageFilename(index, extension);
|
||||
UniFile file = dir.findFile(filename);
|
||||
if (file != null) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setMode(@SpiderQueen.Mode int mode) {
|
||||
@@ -158,25 +165,6 @@ public final class SpiderDen {
|
||||
return sCache.contain(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param extension with dot
|
||||
*/
|
||||
public static String generateImageFilename(int index, String extension) {
|
||||
return String.format(Locale.US, "%08d%s", index + 1, extension);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static UniFile findImageFile(UniFile dir, int index) {
|
||||
for (String extension : GalleryProvider2.SUPPORT_IMAGE_EXTENSIONS) {
|
||||
String filename = generateImageFilename(index, extension);
|
||||
UniFile file = dir.findFile(filename);
|
||||
if (file != null) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean containInDownloadDir(int index) {
|
||||
UniFile dir = getDownloadDir();
|
||||
if (dir == null) {
|
||||
@@ -334,7 +322,6 @@ public final class SpiderDen {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private InputStreamPipe openCacheInputStreamPipe(int index) {
|
||||
if (sCache == null) {
|
||||
@@ -378,4 +365,18 @@ public final class SpiderDen {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class StartWithFilenameFilter implements FilenameFilter {
|
||||
|
||||
private final String mPrefix;
|
||||
|
||||
public StartWithFilenameFilter(String prefix) {
|
||||
mPrefix = prefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(UniFile dir, String filename) {
|
||||
return filename.startsWith(mPrefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,11 +19,14 @@ package com.hippo.ehviewer.spider;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.unifile.UniFile;
|
||||
import com.hippo.yorozuya.IOUtils;
|
||||
import com.hippo.yorozuya.NumberUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
@@ -31,13 +34,10 @@ import java.io.OutputStreamWriter;
|
||||
|
||||
public class SpiderInfo {
|
||||
|
||||
static final String TOKEN_FAILED = "failed";
|
||||
private static final String TAG = SpiderInfo.class.getSimpleName();
|
||||
|
||||
private static final String VERSION_STR = "VERSION";
|
||||
private static final int VERSION = 2;
|
||||
|
||||
static final String TOKEN_FAILED = "failed";
|
||||
|
||||
public int startPage = 0;
|
||||
public long gid = -1;
|
||||
public String token = null;
|
||||
|
||||
@@ -24,10 +24,12 @@ import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
import com.hippo.beerbelly.SimpleDiskCache;
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.GetText;
|
||||
@@ -61,6 +63,7 @@ import com.hippo.yorozuya.Utilities;
|
||||
import com.hippo.yorozuya.collect.SparseJLArray;
|
||||
import com.hippo.yorozuya.thread.PriorityThread;
|
||||
import com.hippo.yorozuya.thread.PriorityThreadFactory;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -78,6 +81,7 @@ import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
@@ -87,38 +91,23 @@ import okhttp3.ResponseBody;
|
||||
|
||||
public final class SpiderQueen implements Runnable {
|
||||
|
||||
private static final String TAG = SpiderQueen.class.getSimpleName();
|
||||
private static final AtomicInteger sIdGenerator = new AtomicInteger();
|
||||
private static final boolean DEBUG_LOG = false;
|
||||
private static final boolean DEBUG_PTOKEN = true;
|
||||
|
||||
@IntDef({MODE_READ, MODE_DOWNLOAD})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Mode {}
|
||||
|
||||
@IntDef({STATE_NONE, STATE_DOWNLOADING, STATE_FINISHED, STATE_FAILED})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface State {}
|
||||
|
||||
public static final int MODE_READ = 0;
|
||||
public static final int MODE_DOWNLOAD = 1;
|
||||
|
||||
public static final int STATE_NONE = 0;
|
||||
public static final int STATE_DOWNLOADING = 1;
|
||||
public static final int STATE_FINISHED = 2;
|
||||
public static final int STATE_FAILED = 3;
|
||||
|
||||
public static final int DECODE_THREAD_NUM = 1;
|
||||
|
||||
public static final String SPIDER_INFO_FILENAME = ".ehviewer";
|
||||
|
||||
private static final String TAG = SpiderQueen.class.getSimpleName();
|
||||
private static final AtomicInteger sIdGenerator = new AtomicInteger();
|
||||
private static final boolean DEBUG_LOG = false;
|
||||
private static final boolean DEBUG_PTOKEN = true;
|
||||
private static final String[] URL_509_SUFFIX_ARRAY = {
|
||||
"/509.gif",
|
||||
"/509s.gif"
|
||||
};
|
||||
|
||||
private static final SparseJLArray<SpiderQueen> sQueenMap = new SparseJLArray<>();
|
||||
|
||||
@NonNull
|
||||
private final OkHttpClient mHttpClient;
|
||||
@NonNull
|
||||
@@ -127,54 +116,42 @@ public final class SpiderQueen implements Runnable {
|
||||
private final GalleryInfo mGalleryInfo;
|
||||
@NonNull
|
||||
private final SpiderDen mSpiderDen;
|
||||
|
||||
private int mReadReference = 0;
|
||||
private int mDownloadReference = 0;
|
||||
|
||||
// It mQueenThread is null, failed or stopped
|
||||
@Nullable
|
||||
private volatile Thread mQueenThread;
|
||||
private final Object mQueenLock = new Object();
|
||||
|
||||
private final Thread[] mDecodeThreadArray = new Thread[DECODE_THREAD_NUM];
|
||||
private final int[] mDecodeIndexArray = new int[DECODE_THREAD_NUM];
|
||||
private final Queue<Integer> mDecodeRequestQueue = new LinkedList<>();
|
||||
|
||||
private final Object mWorkerLock = new Object();
|
||||
private ThreadPoolExecutor mWorkerPoolExecutor;
|
||||
private int mWorkerCount;
|
||||
|
||||
private final Object mPTokenLock = new Object();
|
||||
private final AtomicReference<SpiderInfo> mSpiderInfo = new AtomicReference<>();
|
||||
private final Queue<Integer> mRequestPTokenQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
private final Object mPageStateLock = new Object();
|
||||
private volatile int[] mPageStateArray;
|
||||
|
||||
// Store request page. The index may be invalid
|
||||
private final Queue<Integer> mRequestPageQueue = new LinkedList<>();
|
||||
// Store preload page. The index may be invalid
|
||||
private final Queue<Integer> mRequestPageQueue2 = new LinkedList<>();
|
||||
// Store force request page. The index may be invalid
|
||||
private final Queue<Integer> mForceRequestPageQueue = new LinkedList<>();
|
||||
// For download, when it go to mPageStateArray.size(), done
|
||||
private volatile int mDownloadPage = -1;
|
||||
|
||||
private final AtomicInteger mDownloadedPages = new AtomicInteger(0);
|
||||
private final AtomicInteger mFinishedPages = new AtomicInteger(0);
|
||||
|
||||
private AtomicReference<String> showKey = new AtomicReference<>();
|
||||
private final Object showKeyLock = new Object();
|
||||
|
||||
// Store page error
|
||||
private final ConcurrentHashMap<Integer, String> mPageErrorMap = new ConcurrentHashMap<>();
|
||||
// Store page download percent
|
||||
private final ConcurrentHashMap<Integer, Float> mPagePercentMap = new ConcurrentHashMap<>();
|
||||
|
||||
private final List<OnSpiderListener> mSpiderListeners = new ArrayList<>();
|
||||
|
||||
private final int mWorkerMaxCount;
|
||||
private final int mPreloadNumber;
|
||||
private int mReadReference = 0;
|
||||
private int mDownloadReference = 0;
|
||||
// It mQueenThread is null, failed or stopped
|
||||
@Nullable
|
||||
private volatile Thread mQueenThread;
|
||||
private ThreadPoolExecutor mWorkerPoolExecutor;
|
||||
private int mWorkerCount;
|
||||
private volatile int[] mPageStateArray;
|
||||
// For download, when it go to mPageStateArray.size(), done
|
||||
private volatile int mDownloadPage = -1;
|
||||
private AtomicReference<String> showKey = new AtomicReference<>();
|
||||
|
||||
private SpiderQueen(EhApplication application, @NonNull GalleryInfo galleryInfo) {
|
||||
mHttpClient = EhApplication.getOkHttpClient(application);
|
||||
@@ -194,6 +171,49 @@ public final class SpiderQueen implements Runnable {
|
||||
new PriorityThreadFactory(SpiderWorker.class.getSimpleName(), Process.THREAD_PRIORITY_BACKGROUND));
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public static SpiderQueen obtainSpiderQueen(@NonNull Context context,
|
||||
@NonNull GalleryInfo galleryInfo, @Mode int mode) {
|
||||
OSUtils.checkMainLoop();
|
||||
|
||||
SpiderQueen queen = sQueenMap.get(galleryInfo.gid);
|
||||
if (queen == null) {
|
||||
EhApplication application = (EhApplication) context.getApplicationContext();
|
||||
queen = new SpiderQueen(application, galleryInfo);
|
||||
sQueenMap.put(galleryInfo.gid, queen);
|
||||
// Set mode
|
||||
queen.setMode(mode);
|
||||
queen.start();
|
||||
} else {
|
||||
// Set mode
|
||||
queen.setMode(mode);
|
||||
}
|
||||
return queen;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public static void releaseSpiderQueen(@NonNull SpiderQueen queen, @Mode int mode) {
|
||||
OSUtils.checkMainLoop();
|
||||
|
||||
// Clear mode
|
||||
queen.clearMode(mode);
|
||||
|
||||
if (queen.mReadReference == 0 && queen.mDownloadReference == 0) {
|
||||
// Stop and remove if there is no reference
|
||||
queen.stop();
|
||||
sQueenMap.remove(queen.mGalleryInfo.gid);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean contain(int[] array, int value) {
|
||||
for (int v : array) {
|
||||
if (v == value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addOnSpiderListener(OnSpiderListener listener) {
|
||||
synchronized (mSpiderListeners) {
|
||||
mSpiderListeners.add(listener);
|
||||
@@ -288,40 +308,6 @@ public final class SpiderQueen implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public static SpiderQueen obtainSpiderQueen(@NonNull Context context,
|
||||
@NonNull GalleryInfo galleryInfo, @Mode int mode) {
|
||||
OSUtils.checkMainLoop();
|
||||
|
||||
SpiderQueen queen = sQueenMap.get(galleryInfo.gid);
|
||||
if (queen == null) {
|
||||
EhApplication application = (EhApplication) context.getApplicationContext();
|
||||
queen = new SpiderQueen(application, galleryInfo);
|
||||
sQueenMap.put(galleryInfo.gid, queen);
|
||||
// Set mode
|
||||
queen.setMode(mode);
|
||||
queen.start();
|
||||
} else {
|
||||
// Set mode
|
||||
queen.setMode(mode);
|
||||
}
|
||||
return queen;
|
||||
}
|
||||
|
||||
@UiThread
|
||||
public static void releaseSpiderQueen(@NonNull SpiderQueen queen, @Mode int mode) {
|
||||
OSUtils.checkMainLoop();
|
||||
|
||||
// Clear mode
|
||||
queen.clearMode(mode);
|
||||
|
||||
if (queen.mReadReference == 0 && queen.mDownloadReference == 0) {
|
||||
// Stop and remove if there is no reference
|
||||
queen.stop();
|
||||
sQueenMap.remove(queen.mGalleryInfo.gid);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateMode() {
|
||||
int mode;
|
||||
if (mDownloadReference > 0) {
|
||||
@@ -481,8 +467,7 @@ public final class SpiderQueen implements Runnable {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* String for error<br>
|
||||
* @return String for error<br>
|
||||
* Float for download percent<br>
|
||||
* null for wait
|
||||
*/
|
||||
@@ -496,7 +481,7 @@ public final class SpiderQueen implements Runnable {
|
||||
|
||||
// Fix state for force
|
||||
if ((force && (state == STATE_FINISHED || state == STATE_FAILED)) ||
|
||||
(ignoreError && state == STATE_FAILED)) {
|
||||
(ignoreError && state == STATE_FAILED)) {
|
||||
// Update state to none at once
|
||||
updatePageState(index, STATE_NONE);
|
||||
state = STATE_NONE;
|
||||
@@ -563,15 +548,6 @@ public final class SpiderQueen implements Runnable {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static boolean contain(int[] array, int value) {
|
||||
for (int v: array) {
|
||||
if (v == value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void ensureWorkers() {
|
||||
synchronized (mWorkerLock) {
|
||||
if (null == mWorkerPoolExecutor) {
|
||||
@@ -780,7 +756,7 @@ public final class SpiderQueen implements Runnable {
|
||||
String referer = EhUrl.getReferer();
|
||||
if (DEBUG_PTOKEN) {
|
||||
Log.d(TAG, "index " + index + ", previewIndex " + previewIndex +
|
||||
", previewPerPage " + spiderInfo.previewPerPage+ ", url " + url);
|
||||
", previewPerPage " + spiderInfo.previewPerPage + ", url " + url);
|
||||
}
|
||||
Request request = new EhRequestBuilder(url, referer).build();
|
||||
Response response = mHttpClient.newCall(request).execute();
|
||||
@@ -1008,6 +984,41 @@ public final class SpiderQueen implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
@IntDef({MODE_READ, MODE_DOWNLOAD})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Mode {
|
||||
}
|
||||
|
||||
@IntDef({STATE_NONE, STATE_DOWNLOADING, STATE_FINISHED, STATE_FAILED})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface State {
|
||||
}
|
||||
|
||||
public interface OnSpiderListener {
|
||||
|
||||
void onGetPages(int pages);
|
||||
|
||||
void onGet509(int index);
|
||||
|
||||
/**
|
||||
* @param contentLength -1 for unknown
|
||||
*/
|
||||
void onPageDownload(int index, long contentLength, long receivedSize, int bytesRead);
|
||||
|
||||
void onPageSuccess(int index, int finished, int downloaded, int total);
|
||||
|
||||
void onPageFailure(int index, String error, int finished, int downloaded, int total);
|
||||
|
||||
/**
|
||||
* All workers end
|
||||
*/
|
||||
void onFinish(int finished, int downloaded, int total);
|
||||
|
||||
void onGetImageSuccess(int index, Image image);
|
||||
|
||||
void onGetImageFailure(int index, String error);
|
||||
}
|
||||
|
||||
private class SpiderWorker implements Runnable {
|
||||
|
||||
private final long mGid;
|
||||
@@ -1017,7 +1028,7 @@ public final class SpiderQueen implements Runnable {
|
||||
}
|
||||
|
||||
private String getPageUrl(long gid, int index, String pToken,
|
||||
String oldPageUrl, String skipHathKey) {
|
||||
String oldPageUrl, String skipHathKey) {
|
||||
String pageUrl;
|
||||
if (oldPageUrl != null) {
|
||||
pageUrl = oldPageUrl;
|
||||
@@ -1276,7 +1287,7 @@ public final class SpiderQueen implements Runnable {
|
||||
isPipe.obtain();
|
||||
InputStream inputStream = new BufferedInputStream(isPipe.open());
|
||||
boolean isPlainTxt = true;
|
||||
for (;;) {
|
||||
for (; ; ) {
|
||||
int b = inputStream.read();
|
||||
if (b == -1) {
|
||||
break;
|
||||
@@ -1381,7 +1392,7 @@ public final class SpiderQueen implements Runnable {
|
||||
|
||||
// Check exist for not force request
|
||||
if (!force && mSpiderDen.contain(index)) {
|
||||
updatePageState(index , STATE_FINISHED);
|
||||
updatePageState(index, STATE_FINISHED);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1481,7 +1492,8 @@ public final class SpiderQueen implements Runnable {
|
||||
Log.i(TAG, Thread.currentThread().getName() + ": start");
|
||||
}
|
||||
|
||||
while (mSpiderDen.isReady() && !Thread.currentThread().isInterrupted() && runInternal());
|
||||
while (mSpiderDen.isReady() && !Thread.currentThread().isInterrupted() && runInternal())
|
||||
;
|
||||
|
||||
boolean finish;
|
||||
// Clear in spider worker array
|
||||
@@ -1621,29 +1633,4 @@ public final class SpiderQueen implements Runnable {
|
||||
mPipe.release();
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnSpiderListener {
|
||||
|
||||
void onGetPages(int pages);
|
||||
|
||||
void onGet509(int index);
|
||||
|
||||
/**
|
||||
* @param contentLength -1 for unknown
|
||||
*/
|
||||
void onPageDownload(int index, long contentLength, long receivedSize, int bytesRead);
|
||||
|
||||
void onPageSuccess(int index, int finished, int downloaded, int total);
|
||||
|
||||
void onPageFailure(int index, String error, int finished, int downloaded, int total);
|
||||
|
||||
/**
|
||||
* All workers end
|
||||
*/
|
||||
void onFinish(int finished, int downloaded, int total);
|
||||
|
||||
void onGetImageSuccess(int index, Image image);
|
||||
|
||||
void onGetImageFailure(int index, String error);
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,9 @@ import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.hippo.app.ListCheckBoxDialogBuilder;
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.EhDB;
|
||||
@@ -44,16 +46,19 @@ import com.hippo.util.IoThreadPoolExecutor;
|
||||
import com.hippo.yorozuya.FileUtils;
|
||||
import com.hippo.yorozuya.IOUtils;
|
||||
import com.hippo.yorozuya.collect.LongList;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public final class CommonOperations {
|
||||
|
||||
@@ -68,129 +73,8 @@ public final class CommonOperations {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class UpdateTask extends AsyncTask<Void, Void, JSONObject> {
|
||||
|
||||
private final Activity mActivity;
|
||||
private final OkHttpClient mHttpClient;
|
||||
private final boolean mFeedback;
|
||||
|
||||
public UpdateTask(Activity activity, boolean feedback) {
|
||||
mActivity = activity;
|
||||
mHttpClient = EhApplication.getOkHttpClient(activity);
|
||||
mFeedback = feedback;
|
||||
}
|
||||
|
||||
private JSONObject fetchUpdateInfo(String url) throws IOException, JSONException {
|
||||
Log.d(TAG, url);
|
||||
Request request = new Request.Builder().url(url).build();
|
||||
Response response = mHttpClient.newCall(request).execute();
|
||||
return new JSONObject(response.body().string());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject doInBackground(Void... params) {
|
||||
String url;
|
||||
if (Settings.getBetaUpdateChannel()) {
|
||||
url = "http://www.ehviewer.com/update_beta.json";
|
||||
} else {
|
||||
url = "http://www.ehviewer.com/update.json";
|
||||
}
|
||||
|
||||
try {
|
||||
return fetchUpdateInfo(url);
|
||||
} catch (Throwable e1) {
|
||||
ExceptionUtils.throwIfFatal(e1);
|
||||
|
||||
if (Settings.getBetaUpdateChannel()) {
|
||||
url = "https://raw.githubusercontent.com/seven332/EhViewer/api/update_beta.json";
|
||||
} else {
|
||||
url = "https://raw.githubusercontent.com/seven332/EhViewer/api/update.json";
|
||||
}
|
||||
|
||||
try {
|
||||
return fetchUpdateInfo(url);
|
||||
} catch (Throwable e2) {
|
||||
ExceptionUtils.throwIfFatal(e2);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showUpToDateDialog() {
|
||||
new AlertDialog.Builder(mActivity)
|
||||
.setMessage(R.string.update_to_date)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void showUpdateDialog(String versionName, int versionCode, String size, CharSequence info, final String url) {
|
||||
new AlertDialog.Builder(mActivity)
|
||||
.setTitle(R.string.update)
|
||||
.setMessage(mActivity.getString(R.string.update_plain, versionName, size, info))
|
||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
UrlOpener.openUrl(mActivity, url, false);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.update_ignore, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Settings.putSkipUpdateVersion(versionCode);
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
|
||||
private void handleResult(JSONObject jo) {
|
||||
if (null == jo || mActivity.isFinishing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String versionName;
|
||||
int versionCode;
|
||||
String size;
|
||||
CharSequence info;
|
||||
String url;
|
||||
|
||||
try {
|
||||
PackageManager pm = mActivity.getPackageManager();
|
||||
PackageInfo pi = pm.getPackageInfo(mActivity.getPackageName(), PackageManager.GET_ACTIVITIES);
|
||||
int currentVersionCode = pi.versionCode;
|
||||
versionCode = jo.getInt("version_code");
|
||||
if (currentVersionCode >= versionCode) {
|
||||
// Update to date
|
||||
if (mFeedback) {
|
||||
showUpToDateDialog();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
versionName = jo.getString("version_name");
|
||||
size = FileUtils.humanReadableByteCount(jo.getLong("size"), false);
|
||||
info = Html.fromHtml(jo.getString("info"));
|
||||
url = jo.getString("url");
|
||||
} catch (Throwable e) {
|
||||
ExceptionUtils.throwIfFatal(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mFeedback || versionCode != Settings.getSkipUpdateVersion()) {
|
||||
showUpdateDialog(versionName, versionCode, size, info, url);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(JSONObject jsonObject) {
|
||||
try {
|
||||
handleResult(jsonObject);
|
||||
} finally {
|
||||
UPDATING = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void doAddToFavorites(Activity activity, GalleryInfo galleryInfo,
|
||||
int slot, EhClient.Callback<Void> listener) {
|
||||
int slot, EhClient.Callback<Void> listener) {
|
||||
if (slot == -1) {
|
||||
EhDB.putLocalFavorites(galleryInfo);
|
||||
listener.onSuccess(null);
|
||||
@@ -207,7 +91,7 @@ public final class CommonOperations {
|
||||
}
|
||||
|
||||
public static void addToFavorites(final Activity activity, final GalleryInfo galleryInfo,
|
||||
final EhClient.Callback<Void> listener) {
|
||||
final EhClient.Callback<Void> listener) {
|
||||
int slot = Settings.getDefaultFavSlot();
|
||||
String[] items = new String[11];
|
||||
items[0] = activity.getString(R.string.local_favorites);
|
||||
@@ -220,7 +104,7 @@ public final class CommonOperations {
|
||||
new ListCheckBoxDialogBuilder(activity, items,
|
||||
(builder, dialog, position) -> {
|
||||
int slot1 = position - 1;
|
||||
String newFavoriteName = (slot1 >= 0 && slot1 <= 9) ? items[slot1+1] : null;
|
||||
String newFavoriteName = (slot1 >= 0 && slot1 <= 9) ? items[slot1 + 1] : null;
|
||||
doAddToFavorites(activity, galleryInfo, slot1, new DelegateFavoriteCallback(listener, galleryInfo, newFavoriteName, slot1));
|
||||
if (builder.isChecked()) {
|
||||
Settings.putDefaultFavSlot(slot1);
|
||||
@@ -235,7 +119,7 @@ public final class CommonOperations {
|
||||
}
|
||||
|
||||
public static void removeFromFavorites(Activity activity, GalleryInfo galleryInfo,
|
||||
final EhClient.Callback<Void> listener) {
|
||||
final EhClient.Callback<Void> listener) {
|
||||
EhDB.removeLocalFavorites(galleryInfo.gid);
|
||||
EhClient client = EhApplication.getEhClient(activity);
|
||||
EhRequest request = new EhRequest();
|
||||
@@ -245,40 +129,6 @@ public final class CommonOperations {
|
||||
client.execute(request);
|
||||
}
|
||||
|
||||
private static class DelegateFavoriteCallback implements EhClient.Callback<Void> {
|
||||
|
||||
private final EhClient.Callback<Void> delegate;
|
||||
private final GalleryInfo info;
|
||||
private final String newFavoriteName;
|
||||
private final int slot;
|
||||
|
||||
DelegateFavoriteCallback(EhClient.Callback<Void> delegate, GalleryInfo info,
|
||||
String newFavoriteName, int slot) {
|
||||
this.delegate = delegate;
|
||||
this.info = info;
|
||||
this.newFavoriteName = newFavoriteName;
|
||||
this.slot = slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Void result) {
|
||||
info.favoriteName = newFavoriteName;
|
||||
info.favoriteSlot = slot;
|
||||
delegate.onSuccess(result);
|
||||
EhApplication.getFavouriteStatusRouter().modifyFavourites(info.gid, slot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
delegate.onFailure(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
delegate.onCancel();
|
||||
}
|
||||
}
|
||||
|
||||
public static void startDownload(final MainActivity activity, final GalleryInfo galleryInfo, boolean forceDefault) {
|
||||
startDownload(activity, Collections.singletonList(galleryInfo), forceDefault);
|
||||
}
|
||||
@@ -409,4 +259,159 @@ public final class CommonOperations {
|
||||
noMedia.delete();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class UpdateTask extends AsyncTask<Void, Void, JSONObject> {
|
||||
|
||||
private final Activity mActivity;
|
||||
private final OkHttpClient mHttpClient;
|
||||
private final boolean mFeedback;
|
||||
|
||||
public UpdateTask(Activity activity, boolean feedback) {
|
||||
mActivity = activity;
|
||||
mHttpClient = EhApplication.getOkHttpClient(activity);
|
||||
mFeedback = feedback;
|
||||
}
|
||||
|
||||
private JSONObject fetchUpdateInfo(String url) throws IOException, JSONException {
|
||||
Log.d(TAG, url);
|
||||
Request request = new Request.Builder().url(url).build();
|
||||
Response response = mHttpClient.newCall(request).execute();
|
||||
return new JSONObject(response.body().string());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject doInBackground(Void... params) {
|
||||
String url;
|
||||
if (Settings.getBetaUpdateChannel()) {
|
||||
url = "http://www.ehviewer.com/update_beta.json";
|
||||
} else {
|
||||
url = "http://www.ehviewer.com/update.json";
|
||||
}
|
||||
|
||||
try {
|
||||
return fetchUpdateInfo(url);
|
||||
} catch (Throwable e1) {
|
||||
ExceptionUtils.throwIfFatal(e1);
|
||||
|
||||
if (Settings.getBetaUpdateChannel()) {
|
||||
url = "https://raw.githubusercontent.com/seven332/EhViewer/api/update_beta.json";
|
||||
} else {
|
||||
url = "https://raw.githubusercontent.com/seven332/EhViewer/api/update.json";
|
||||
}
|
||||
|
||||
try {
|
||||
return fetchUpdateInfo(url);
|
||||
} catch (Throwable e2) {
|
||||
ExceptionUtils.throwIfFatal(e2);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showUpToDateDialog() {
|
||||
new AlertDialog.Builder(mActivity)
|
||||
.setMessage(R.string.update_to_date)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private void showUpdateDialog(String versionName, int versionCode, String size, CharSequence info, final String url) {
|
||||
new AlertDialog.Builder(mActivity)
|
||||
.setTitle(R.string.update)
|
||||
.setMessage(mActivity.getString(R.string.update_plain, versionName, size, info))
|
||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
UrlOpener.openUrl(mActivity, url, false);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.update_ignore, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Settings.putSkipUpdateVersion(versionCode);
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
|
||||
private void handleResult(JSONObject jo) {
|
||||
if (null == jo || mActivity.isFinishing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String versionName;
|
||||
int versionCode;
|
||||
String size;
|
||||
CharSequence info;
|
||||
String url;
|
||||
|
||||
try {
|
||||
PackageManager pm = mActivity.getPackageManager();
|
||||
PackageInfo pi = pm.getPackageInfo(mActivity.getPackageName(), PackageManager.GET_ACTIVITIES);
|
||||
int currentVersionCode = pi.versionCode;
|
||||
versionCode = jo.getInt("version_code");
|
||||
if (currentVersionCode >= versionCode) {
|
||||
// Update to date
|
||||
if (mFeedback) {
|
||||
showUpToDateDialog();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
versionName = jo.getString("version_name");
|
||||
size = FileUtils.humanReadableByteCount(jo.getLong("size"), false);
|
||||
info = Html.fromHtml(jo.getString("info"));
|
||||
url = jo.getString("url");
|
||||
} catch (Throwable e) {
|
||||
ExceptionUtils.throwIfFatal(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mFeedback || versionCode != Settings.getSkipUpdateVersion()) {
|
||||
showUpdateDialog(versionName, versionCode, size, info, url);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(JSONObject jsonObject) {
|
||||
try {
|
||||
handleResult(jsonObject);
|
||||
} finally {
|
||||
UPDATING = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class DelegateFavoriteCallback implements EhClient.Callback<Void> {
|
||||
|
||||
private final EhClient.Callback<Void> delegate;
|
||||
private final GalleryInfo info;
|
||||
private final String newFavoriteName;
|
||||
private final int slot;
|
||||
|
||||
DelegateFavoriteCallback(EhClient.Callback<Void> delegate, GalleryInfo info,
|
||||
String newFavoriteName, int slot) {
|
||||
this.delegate = delegate;
|
||||
this.info = info;
|
||||
this.newFavoriteName = newFavoriteName;
|
||||
this.slot = slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Void result) {
|
||||
info.favoriteName = newFavoriteName;
|
||||
info.favoriteSlot = slot;
|
||||
delegate.onSuccess(result);
|
||||
EhApplication.getFavouriteStatusRouter().modifyFavourites(info.gid, slot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
delegate.onFailure(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
delegate.onCancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,16 +23,19 @@ import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.hippo.android.resource.AttrResources;
|
||||
import com.hippo.ehviewer.AppConfig;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ripple.Ripple;
|
||||
import com.hippo.widget.DirExplorer;
|
||||
import com.hippo.yorozuya.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class DirPickerActivity extends ToolbarActivity
|
||||
|
||||
@@ -20,16 +20,17 @@ import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StyleRes;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import com.google.firebase.analytics.FirebaseAnalytics;
|
||||
|
||||
import com.hippo.android.resource.AttrResources;
|
||||
import com.hippo.content.ContextLocalWrapper;
|
||||
import com.hippo.ehviewer.Analytics;
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.Settings;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public abstract class EhActivity extends AppCompatActivity {
|
||||
@@ -45,10 +46,6 @@ public abstract class EhActivity extends AppCompatActivity {
|
||||
|
||||
((EhApplication) getApplication()).registerActivity(this);
|
||||
|
||||
if (Analytics.isEnabled()) {
|
||||
FirebaseAnalytics.getInstance(this);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Settings.getApplyNavBarThemeColor()) {
|
||||
getWindow().setNavigationBarColor(AttrResources.getAttrColor(this, R.attr.colorPrimaryDark));
|
||||
}
|
||||
@@ -64,10 +61,10 @@ public abstract class EhActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if(Settings.getEnabledSecurity()){
|
||||
if (Settings.getEnabledSecurity()) {
|
||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE,
|
||||
WindowManager.LayoutParams.FLAG_SECURE);
|
||||
}else{
|
||||
} else {
|
||||
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.WindowManager;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StyleRes;
|
||||
import com.google.firebase.analytics.FirebaseAnalytics;
|
||||
import com.hippo.android.resource.AttrResources;
|
||||
import com.hippo.app.PrettyPreferenceActivity;
|
||||
import com.hippo.content.ContextLocalWrapper;
|
||||
import com.hippo.ehviewer.Analytics;
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.Settings;
|
||||
import java.util.Locale;
|
||||
|
||||
public abstract class EhPreferenceActivity extends PrettyPreferenceActivity {
|
||||
|
||||
@StyleRes
|
||||
protected abstract int getThemeResId(int theme);
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
setTheme(getThemeResId(Settings.getTheme()));
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
((EhApplication) getApplication()).registerActivity(this);
|
||||
|
||||
if (Analytics.isEnabled()) {
|
||||
FirebaseAnalytics.getInstance(this);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Settings.getApplyNavBarThemeColor()) {
|
||||
getWindow().setNavigationBarColor(AttrResources.getAttrColor(this, R.attr.colorPrimaryDark));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
((EhApplication) getApplication()).unregisterActivity(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if(Settings.getEnabledSecurity()){
|
||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE,
|
||||
WindowManager.LayoutParams.FLAG_SECURE);
|
||||
}else{
|
||||
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context newBase) {
|
||||
Locale locale = null;
|
||||
String language = Settings.getAppLanguage();
|
||||
if (language != null && !language.equals("system")) {
|
||||
String[] split = language.split("-");
|
||||
if (split.length == 1) {
|
||||
locale = new Locale(split[0]);
|
||||
} else if (split.length == 2) {
|
||||
locale = new Locale(split[0], split[1]);
|
||||
} else if (split.length == 3) {
|
||||
locale = new Locale(split[0], split[1], split[2]);
|
||||
}
|
||||
}
|
||||
|
||||
if (locale == null) {
|
||||
locale = Resources.getSystem().getConfiguration().locale;
|
||||
}
|
||||
|
||||
newBase = ContextLocalWrapper.wrap(newBase, locale);
|
||||
super.attachBaseContext(newBase);
|
||||
}
|
||||
}
|
||||
@@ -21,10 +21,12 @@ import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.hippo.android.resource.AttrResources;
|
||||
import com.hippo.easyrecyclerview.EasyRecyclerView;
|
||||
import com.hippo.ehviewer.R;
|
||||
|
||||
@@ -29,10 +29,12 @@ import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
import com.hippo.easyrecyclerview.EasyRecyclerView;
|
||||
import com.hippo.ehviewer.R;
|
||||
@@ -41,6 +43,7 @@ import com.hippo.ehviewer.dao.Filter;
|
||||
import com.hippo.util.DrawableManager;
|
||||
import com.hippo.view.ViewTransition;
|
||||
import com.hippo.yorozuya.ViewUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class FilterActivity extends ToolbarActivity {
|
||||
|
||||
@@ -44,10 +44,12 @@ import android.widget.SeekBar;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.widget.SwitchCompat;
|
||||
|
||||
import com.hippo.android.resource.AttrResources;
|
||||
import com.hippo.ehviewer.AppConfig;
|
||||
import com.hippo.ehviewer.BuildConfig;
|
||||
@@ -78,6 +80,7 @@ import com.hippo.yorozuya.ResourcesUtils;
|
||||
import com.hippo.yorozuya.SimpleAnimatorListener;
|
||||
import com.hippo.yorozuya.SimpleHandler;
|
||||
import com.hippo.yorozuya.ViewUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
@@ -101,14 +104,13 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
|
||||
private static final long HIDE_SLIDER_DELAY = 3000;
|
||||
|
||||
private static final int WRITE_REQUEST_CODE = 43;
|
||||
|
||||
private final ConcurrentPool<NotifyTask> mNotifyTaskPool = new ConcurrentPool<>(3);
|
||||
private String mAction;
|
||||
private String mFilename;
|
||||
private Uri mUri;
|
||||
private GalleryInfo mGalleryInfo;
|
||||
private int mPage;
|
||||
private String mCacheFileName;
|
||||
|
||||
@Nullable
|
||||
private GLRootView mGLRootView;
|
||||
@Nullable
|
||||
@@ -117,11 +119,9 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
|
||||
private GalleryProvider2 mGalleryProvider;
|
||||
@Nullable
|
||||
private GalleryAdapter mGalleryAdapter;
|
||||
|
||||
@Nullable
|
||||
private SystemUiHelper mSystemUiHelper;
|
||||
private boolean mShowSystemUi;
|
||||
|
||||
@Nullable
|
||||
private ColorView mMaskView;
|
||||
@Nullable
|
||||
@@ -132,21 +132,6 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
|
||||
private View mBattery;
|
||||
@Nullable
|
||||
private View mSeekBarPanel;
|
||||
@Nullable
|
||||
private TextView mLeftText;
|
||||
@Nullable
|
||||
private TextView mRightText;
|
||||
@Nullable
|
||||
private ReversibleSeekBar mSeekBar;
|
||||
|
||||
private ObjectAnimator mSeekBarPanelAnimator;
|
||||
|
||||
private int mLayoutMode;
|
||||
private int mSize;
|
||||
private int mCurrentIndex;
|
||||
|
||||
private final ConcurrentPool<NotifyTask> mNotifyTaskPool = new ConcurrentPool<>(3);
|
||||
|
||||
private final ValueAnimator.AnimatorUpdateListener mUpdateSliderListener = new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
@@ -155,14 +140,19 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Nullable
|
||||
private TextView mLeftText;
|
||||
@Nullable
|
||||
private TextView mRightText;
|
||||
@Nullable
|
||||
private ReversibleSeekBar mSeekBar;
|
||||
private ObjectAnimator mSeekBarPanelAnimator;
|
||||
private final SimpleAnimatorListener mShowSliderListener = new SimpleAnimatorListener() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mSeekBarPanelAnimator = null;
|
||||
}
|
||||
};
|
||||
|
||||
private final SimpleAnimatorListener mHideSliderListener = new SimpleAnimatorListener() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
@@ -172,7 +162,6 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final Runnable mHideSliderRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -181,6 +170,9 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
|
||||
}
|
||||
}
|
||||
};
|
||||
private int mLayoutMode;
|
||||
private int mSize;
|
||||
private int mCurrentIndex;
|
||||
|
||||
@Override
|
||||
protected int getThemeResId(int theme) {
|
||||
@@ -233,7 +225,7 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
|
||||
private void onRestore(@NonNull Bundle savedInstanceState) {
|
||||
mAction = savedInstanceState.getString(KEY_ACTION);
|
||||
mFilename = savedInstanceState.getString(KEY_FILENAME);
|
||||
mUri= savedInstanceState.getParcelable(KEY_URI);
|
||||
mUri = savedInstanceState.getParcelable(KEY_URI);
|
||||
mGalleryInfo = savedInstanceState.getParcelable(KEY_GALLERY_INFO);
|
||||
mPage = savedInstanceState.getInt(KEY_PAGE, -1);
|
||||
mCurrentIndex = savedInstanceState.getInt(KEY_CURRENT_INDEX);
|
||||
@@ -735,6 +727,184 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
|
||||
w.setAttributes(lp);
|
||||
}
|
||||
|
||||
private void shareImage(int page) {
|
||||
if (null == mGalleryProvider) {
|
||||
return;
|
||||
}
|
||||
|
||||
File dir = AppConfig.getExternalTempDir();
|
||||
if (null == dir) {
|
||||
Toast.makeText(this, R.string.error_cant_create_temp_file, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
UniFile file;
|
||||
if (null == (file = mGalleryProvider.save(page, UniFile.fromFile(dir), mGalleryProvider.getImageFilename(page)))) {
|
||||
Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
String filename = file.getName();
|
||||
if (filename == null) {
|
||||
Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
|
||||
MimeTypeMap.getFileExtensionFromUrl(filename));
|
||||
if (TextUtils.isEmpty(mimeType)) {
|
||||
mimeType = "image/jpeg";
|
||||
}
|
||||
|
||||
Uri uri = new Uri.Builder()
|
||||
.scheme(ContentResolver.SCHEME_CONTENT)
|
||||
.authority(BuildConfig.FILE_PROVIDER_AUTHORITY)
|
||||
.appendPath("temp")
|
||||
.appendPath(filename)
|
||||
.build();
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_SEND);
|
||||
intent.putExtra(Intent.EXTRA_STREAM, uri);
|
||||
intent.setType(mimeType);
|
||||
|
||||
try {
|
||||
startActivity(Intent.createChooser(intent, getString(R.string.share_image)));
|
||||
} catch (Throwable e) {
|
||||
ExceptionUtils.throwIfFatal(e);
|
||||
Toast.makeText(this, R.string.error_cant_find_activity, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
private void saveImage(int page) {
|
||||
if (null == mGalleryProvider) {
|
||||
return;
|
||||
}
|
||||
|
||||
File dir = AppConfig.getExternalImageDir();
|
||||
if (null == dir) {
|
||||
Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
UniFile file;
|
||||
if (null == (file = mGalleryProvider.save(page, UniFile.fromFile(dir), mGalleryProvider.getImageFilename(page)))) {
|
||||
Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
Toast.makeText(this, getString(R.string.image_saved, file.getUri()), Toast.LENGTH_SHORT).show();
|
||||
|
||||
// Sync media store
|
||||
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, file.getUri()));
|
||||
}
|
||||
|
||||
private void saveImageTo(int page) {
|
||||
if (null == mGalleryProvider) {
|
||||
return;
|
||||
}
|
||||
File dir = getCacheDir();
|
||||
UniFile file;
|
||||
if (null == (file = mGalleryProvider.save(page, UniFile.fromFile(dir), mGalleryProvider.getImageFilename(page)))) {
|
||||
Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
String filename = file.getName();
|
||||
if (filename == null) {
|
||||
Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
mCacheFileName = filename;
|
||||
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
intent.setType("image/*");
|
||||
intent.putExtra(Intent.EXTRA_TITLE, filename);
|
||||
try {
|
||||
startActivityForResult(intent, WRITE_REQUEST_CODE);
|
||||
} catch (Throwable e) {
|
||||
ExceptionUtils.throwIfFatal(e);
|
||||
Toast.makeText(this, R.string.error_cant_find_activity, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
|
||||
if (requestCode == WRITE_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
|
||||
if (resultData != null) {
|
||||
Uri uri = resultData.getData();
|
||||
String filepath = getCacheDir() + "/" + mCacheFileName;
|
||||
File cachefile = new File(filepath);
|
||||
|
||||
InputStream is = null;
|
||||
OutputStream os = null;
|
||||
ContentResolver resolver = getContentResolver();
|
||||
|
||||
try {
|
||||
is = new FileInputStream(cachefile);
|
||||
os = resolver.openOutputStream(uri);
|
||||
IOUtils.copy(is, os);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
IOUtils.closeQuietly(is);
|
||||
IOUtils.closeQuietly(os);
|
||||
}
|
||||
|
||||
cachefile.delete();
|
||||
|
||||
Toast.makeText(this, getString(R.string.image_saved, uri.getPath()), Toast.LENGTH_SHORT).show();
|
||||
// Sync media store
|
||||
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showPageDialog(final int page) {
|
||||
Resources resources = GalleryActivity.this.getResources();
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(GalleryActivity.this);
|
||||
builder.setTitle(resources.getString(R.string.page_menu_title, page + 1));
|
||||
|
||||
final CharSequence[] items;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
items = new CharSequence[]{
|
||||
getString(R.string.page_menu_refresh),
|
||||
getString(R.string.page_menu_share),
|
||||
getString(R.string.page_menu_save),
|
||||
getString(R.string.page_menu_save_to)};
|
||||
} else {
|
||||
items = new CharSequence[]{
|
||||
getString(R.string.page_menu_refresh),
|
||||
getString(R.string.page_menu_share),
|
||||
getString(R.string.page_menu_save)};
|
||||
}
|
||||
pageDialogListener(builder, items, page);
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void pageDialogListener(AlertDialog.Builder builder, CharSequence[] items, int page) {
|
||||
builder.setItems(items, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (mGalleryProvider == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (which) {
|
||||
case 0: // Refresh
|
||||
mGalleryProvider.removeCache(page);
|
||||
mGalleryProvider.forceRequest(page);
|
||||
break;
|
||||
case 1: // Share
|
||||
shareImage(page);
|
||||
break;
|
||||
case 2: // Save
|
||||
saveImage(page);
|
||||
break;
|
||||
case 3: // Save to
|
||||
saveImageTo(page);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class GalleryMenuHelper implements DialogInterface.OnClickListener {
|
||||
|
||||
private final View mView;
|
||||
@@ -880,184 +1050,6 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
|
||||
}
|
||||
}
|
||||
|
||||
private void shareImage(int page) {
|
||||
if (null == mGalleryProvider) {
|
||||
return;
|
||||
}
|
||||
|
||||
File dir = AppConfig.getExternalTempDir();
|
||||
if (null == dir) {
|
||||
Toast.makeText(this, R.string.error_cant_create_temp_file, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
UniFile file;
|
||||
if (null == (file = mGalleryProvider.save(page, UniFile.fromFile(dir), mGalleryProvider.getImageFilename(page)))) {
|
||||
Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
String filename = file.getName();
|
||||
if (filename == null) {
|
||||
Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
|
||||
MimeTypeMap.getFileExtensionFromUrl(filename));
|
||||
if (TextUtils.isEmpty(mimeType)) {
|
||||
mimeType = "image/jpeg";
|
||||
}
|
||||
|
||||
Uri uri = new Uri.Builder()
|
||||
.scheme(ContentResolver.SCHEME_CONTENT)
|
||||
.authority(BuildConfig.FILE_PROVIDER_AUTHORITY)
|
||||
.appendPath("temp")
|
||||
.appendPath(filename)
|
||||
.build();
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Intent.ACTION_SEND);
|
||||
intent.putExtra(Intent.EXTRA_STREAM, uri);
|
||||
intent.setType(mimeType);
|
||||
|
||||
try {
|
||||
startActivity(Intent.createChooser(intent, getString(R.string.share_image)));
|
||||
} catch (Throwable e) {
|
||||
ExceptionUtils.throwIfFatal(e);
|
||||
Toast.makeText(this, R.string.error_cant_find_activity, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
private void saveImage(int page) {
|
||||
if (null == mGalleryProvider) {
|
||||
return;
|
||||
}
|
||||
|
||||
File dir = AppConfig.getExternalImageDir();
|
||||
if (null == dir) {
|
||||
Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
UniFile file;
|
||||
if (null == (file = mGalleryProvider.save(page, UniFile.fromFile(dir), mGalleryProvider.getImageFilename(page)))) {
|
||||
Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
Toast.makeText(this, getString(R.string.image_saved, file.getUri()), Toast.LENGTH_SHORT).show();
|
||||
|
||||
// Sync media store
|
||||
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, file.getUri()));
|
||||
}
|
||||
|
||||
private void saveImageTo(int page) {
|
||||
if (null == mGalleryProvider) {
|
||||
return;
|
||||
}
|
||||
File dir = getCacheDir();
|
||||
UniFile file;
|
||||
if (null == (file = mGalleryProvider.save(page, UniFile.fromFile(dir), mGalleryProvider.getImageFilename(page)))) {
|
||||
Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
String filename = file.getName();
|
||||
if (filename == null) {
|
||||
Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
mCacheFileName = filename;
|
||||
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
intent.setType("image/*");
|
||||
intent.putExtra(Intent.EXTRA_TITLE, filename);
|
||||
try {
|
||||
startActivityForResult(intent, WRITE_REQUEST_CODE);
|
||||
} catch (Throwable e) {
|
||||
ExceptionUtils.throwIfFatal(e);
|
||||
Toast.makeText(this, R.string.error_cant_find_activity, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
|
||||
if (requestCode == WRITE_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
|
||||
if (resultData != null){
|
||||
Uri uri = resultData.getData();
|
||||
String filepath = getCacheDir() + "/" + mCacheFileName;
|
||||
File cachefile = new File(filepath);
|
||||
|
||||
InputStream is = null;
|
||||
OutputStream os = null;
|
||||
ContentResolver resolver = getContentResolver();
|
||||
|
||||
try {
|
||||
is = new FileInputStream(cachefile);
|
||||
os = resolver.openOutputStream(uri);
|
||||
IOUtils.copy(is, os);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
IOUtils.closeQuietly(is);
|
||||
IOUtils.closeQuietly(os);
|
||||
}
|
||||
|
||||
cachefile.delete();
|
||||
|
||||
Toast.makeText(this, getString(R.string.image_saved, uri.getPath()), Toast.LENGTH_SHORT).show();
|
||||
// Sync media store
|
||||
sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showPageDialog(final int page) {
|
||||
Resources resources = GalleryActivity.this.getResources();
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(GalleryActivity.this);
|
||||
builder.setTitle(resources.getString(R.string.page_menu_title, page + 1));
|
||||
|
||||
final CharSequence[] items;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
|
||||
items = new CharSequence[]{
|
||||
getString(R.string.page_menu_refresh),
|
||||
getString(R.string.page_menu_share),
|
||||
getString(R.string.page_menu_save),
|
||||
getString(R.string.page_menu_save_to)};
|
||||
}else {
|
||||
items = new CharSequence[]{
|
||||
getString(R.string.page_menu_refresh),
|
||||
getString(R.string.page_menu_share),
|
||||
getString(R.string.page_menu_save)};
|
||||
}
|
||||
pageDialogListener(builder, items, page);
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void pageDialogListener(AlertDialog.Builder builder, CharSequence[] items, int page){
|
||||
builder.setItems(items, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (mGalleryProvider == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (which) {
|
||||
case 0: // Refresh
|
||||
mGalleryProvider.removeCache(page);
|
||||
mGalleryProvider.forceRequest(page);
|
||||
break;
|
||||
case 1: // Share
|
||||
shareImage(page);
|
||||
break;
|
||||
case 2: // Save
|
||||
saveImage(page);
|
||||
break;
|
||||
case 3: // Save to
|
||||
saveImageTo(page);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class NotifyTask implements Runnable {
|
||||
|
||||
public static final int KEY_LAYOUT_MODE = 0;
|
||||
|
||||
@@ -31,12 +31,14 @@ import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
import com.hippo.android.resource.AttrResources;
|
||||
@@ -47,229 +49,230 @@ import com.hippo.ehviewer.Hosts;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ripple.Ripple;
|
||||
import com.hippo.yorozuya.LayoutUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class HostsActivity extends ToolbarActivity
|
||||
implements EasyRecyclerView.OnItemClickListener, View.OnClickListener {
|
||||
implements EasyRecyclerView.OnItemClickListener, View.OnClickListener {
|
||||
|
||||
private static final String DIALOG_TAG_ADD_HOST = AddHostDialogFragment.class.getName();
|
||||
private static final String DIALOG_TAG_EDIT_HOST = EditHostDialogFragment.class.getName();
|
||||
private static final String DIALOG_TAG_ADD_HOST = AddHostDialogFragment.class.getName();
|
||||
private static final String DIALOG_TAG_EDIT_HOST = EditHostDialogFragment.class.getName();
|
||||
|
||||
private static final String KEY_HOST = "com.hippo.ehviewer.ui.HostsActivity.HOST";
|
||||
private static final String KEY_IP = "com.hippo.ehviewer.ui.HostsActivity.IP";
|
||||
private static final String KEY_HOST = "com.hippo.ehviewer.ui.HostsActivity.HOST";
|
||||
private static final String KEY_IP = "com.hippo.ehviewer.ui.HostsActivity.IP";
|
||||
|
||||
private Hosts hosts;
|
||||
private List<Pair<String, String>> data;
|
||||
private Hosts hosts;
|
||||
private List<Pair<String, String>> data;
|
||||
|
||||
private EasyRecyclerView recyclerView;
|
||||
private View tip;
|
||||
private HostsAdapter adapter;
|
||||
private EasyRecyclerView recyclerView;
|
||||
private View tip;
|
||||
private HostsAdapter adapter;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
hosts = EhApplication.getHosts(this);
|
||||
data = hosts.getAll();
|
||||
hosts = EhApplication.getHosts(this);
|
||||
data = hosts.getAll();
|
||||
|
||||
setContentView(R.layout.activity_hosts);
|
||||
setNavigationIcon(R.drawable.v_arrow_left_dark_x24);
|
||||
recyclerView = findViewById(R.id.recycler_view);
|
||||
tip = findViewById(R.id.tip);
|
||||
FloatingActionButton fab = findViewById(R.id.fab);
|
||||
setContentView(R.layout.activity_hosts);
|
||||
setNavigationIcon(R.drawable.v_arrow_left_dark_x24);
|
||||
recyclerView = findViewById(R.id.recycler_view);
|
||||
tip = findViewById(R.id.tip);
|
||||
FloatingActionButton fab = findViewById(R.id.fab);
|
||||
|
||||
adapter = new HostsAdapter();
|
||||
recyclerView.setAdapter(adapter);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(this, RecyclerView.VERTICAL, false));
|
||||
LinearDividerItemDecoration decoration = new LinearDividerItemDecoration(
|
||||
LinearDividerItemDecoration.VERTICAL,
|
||||
AttrResources.getAttrColor(this, R.attr.dividerColor),
|
||||
LayoutUtils.dp2pix(this, 1));
|
||||
decoration.setShowLastDivider(true);
|
||||
recyclerView.addItemDecoration(decoration);
|
||||
recyclerView.setSelector(Ripple.generateRippleDrawable(this, !AttrResources.getAttrBoolean(this, R.attr.isLightTheme), new ColorDrawable(Color.TRANSPARENT)));
|
||||
recyclerView.setHasFixedSize(true);
|
||||
recyclerView.setOnItemClickListener(this);
|
||||
recyclerView.setPadding(
|
||||
recyclerView.getPaddingLeft(),
|
||||
recyclerView.getPaddingTop(),
|
||||
recyclerView.getPaddingRight(),
|
||||
recyclerView.getPaddingBottom() + getResources().getDimensionPixelOffset(R.dimen.gallery_padding_bottom_fab));
|
||||
adapter = new HostsAdapter();
|
||||
recyclerView.setAdapter(adapter);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(this, RecyclerView.VERTICAL, false));
|
||||
LinearDividerItemDecoration decoration = new LinearDividerItemDecoration(
|
||||
LinearDividerItemDecoration.VERTICAL,
|
||||
AttrResources.getAttrColor(this, R.attr.dividerColor),
|
||||
LayoutUtils.dp2pix(this, 1));
|
||||
decoration.setShowLastDivider(true);
|
||||
recyclerView.addItemDecoration(decoration);
|
||||
recyclerView.setSelector(Ripple.generateRippleDrawable(this, !AttrResources.getAttrBoolean(this, R.attr.isLightTheme), new ColorDrawable(Color.TRANSPARENT)));
|
||||
recyclerView.setHasFixedSize(true);
|
||||
recyclerView.setOnItemClickListener(this);
|
||||
recyclerView.setPadding(
|
||||
recyclerView.getPaddingLeft(),
|
||||
recyclerView.getPaddingTop(),
|
||||
recyclerView.getPaddingRight(),
|
||||
recyclerView.getPaddingBottom() + getResources().getDimensionPixelOffset(R.dimen.gallery_padding_bottom_fab));
|
||||
|
||||
fab.setOnClickListener(this);
|
||||
fab.setOnClickListener(this);
|
||||
|
||||
recyclerView.setVisibility(data.isEmpty() ? View.GONE : View.VISIBLE);
|
||||
tip.setVisibility(data.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
recyclerView.setVisibility(data.isEmpty() ? View.GONE : View.VISIBLE);
|
||||
tip.setVisibility(data.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemClick(EasyRecyclerView easyRecyclerView, View view, int position, long id) {
|
||||
Pair<String, String> pair = data.get(position);
|
||||
Bundle args = new Bundle();
|
||||
args.putString(KEY_HOST, pair.first);
|
||||
args.putString(KEY_IP, pair.second);
|
||||
|
||||
DialogFragment fragment = new EditHostDialogFragment();
|
||||
fragment.setArguments(args);
|
||||
fragment.show(getSupportFragmentManager(), DIALOG_TAG_EDIT_HOST);
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemClick(EasyRecyclerView easyRecyclerView, View view, int position, long id) {
|
||||
Pair<String, String> pair = data.get(position);
|
||||
Bundle args = new Bundle();
|
||||
args.putString(KEY_HOST, pair.first);
|
||||
args.putString(KEY_IP, pair.second);
|
||||
|
||||
DialogFragment fragment = new EditHostDialogFragment();
|
||||
fragment.setArguments(args);
|
||||
fragment.show(getSupportFragmentManager(), DIALOG_TAG_EDIT_HOST);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
new AddHostDialogFragment().show(getSupportFragmentManager(), DIALOG_TAG_ADD_HOST);
|
||||
}
|
||||
|
||||
private void notifyHostsChanges() {
|
||||
data = hosts.getAll();
|
||||
recyclerView.setVisibility(data.isEmpty() ? View.GONE : View.VISIBLE);
|
||||
tip.setVisibility(data.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private class HostsHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public final TextView host;
|
||||
public final TextView ip;
|
||||
|
||||
public HostsHolder(View itemView) {
|
||||
super(itemView);
|
||||
host = itemView.findViewById(R.id.host);
|
||||
ip = itemView.findViewById(R.id.ip);
|
||||
}
|
||||
}
|
||||
|
||||
private class HostsAdapter extends RecyclerView.Adapter<HostsHolder> {
|
||||
|
||||
private final LayoutInflater inflater = getLayoutInflater();
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public HostsHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
return new HostsHolder(inflater.inflate(R.layout.item_hosts, parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull HostsHolder holder, int position) {
|
||||
Pair<String, String> pair = data.get(position);
|
||||
holder.host.setText(pair.first);
|
||||
holder.ip.setText(pair.second);
|
||||
public void onClick(View v) {
|
||||
new AddHostDialogFragment().show(getSupportFragmentManager(), DIALOG_TAG_ADD_HOST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return data.size();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class HostDialogFragment extends DialogFragment {
|
||||
|
||||
private TextView host;
|
||||
private TextView ip;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_hosts, null, false);
|
||||
host = view.findViewById(R.id.host);
|
||||
ip = view.findViewById(R.id.ip);
|
||||
|
||||
Bundle arguments = getArguments();
|
||||
if (savedInstanceState == null && arguments != null) {
|
||||
host.setText(arguments.getString(KEY_HOST));
|
||||
ip.setText(arguments.getString(KEY_IP));
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getContext()).setView(view);
|
||||
onCreateDialogBuilder(builder);
|
||||
AlertDialog dialog = builder.create();
|
||||
dialog.setOnShowListener(d -> onCreateDialog((AlertDialog) d));
|
||||
|
||||
return dialog;
|
||||
private void notifyHostsChanges() {
|
||||
data = hosts.getAll();
|
||||
recyclerView.setVisibility(data.isEmpty() ? View.GONE : View.VISIBLE);
|
||||
tip.setVisibility(data.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
protected abstract void onCreateDialogBuilder(AlertDialog.Builder builder);
|
||||
public abstract static class HostDialogFragment extends DialogFragment {
|
||||
|
||||
protected abstract void onCreateDialog(AlertDialog dialog);
|
||||
private TextView host;
|
||||
private TextView ip;
|
||||
|
||||
protected void put(AlertDialog dialog) {
|
||||
TextView host = dialog.findViewById(R.id.host);
|
||||
TextView ip = dialog.findViewById(R.id.ip);
|
||||
String hostString = host.getText().toString().trim().toLowerCase(Locale.US);
|
||||
String ipString = ip.getText().toString().trim();
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_hosts, null, false);
|
||||
host = view.findViewById(R.id.host);
|
||||
ip = view.findViewById(R.id.ip);
|
||||
|
||||
if (!Hosts.isValidHost(hostString)) {
|
||||
TextInputLayout hostInputLayout = dialog.findViewById(R.id.host_input_layout);
|
||||
hostInputLayout.setError(getContext().getString(R.string.invalid_host));
|
||||
return;
|
||||
}
|
||||
Bundle arguments = getArguments();
|
||||
if (savedInstanceState == null && arguments != null) {
|
||||
host.setText(arguments.getString(KEY_HOST));
|
||||
ip.setText(arguments.getString(KEY_IP));
|
||||
}
|
||||
|
||||
if (!Hosts.isValidIp(ipString)) {
|
||||
TextInputLayout ipInputLayout = dialog.findViewById(R.id.ip_input_layout);
|
||||
ipInputLayout.setError(getContext().getString(R.string.invalid_ip));
|
||||
return;
|
||||
}
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getContext()).setView(view);
|
||||
onCreateDialogBuilder(builder);
|
||||
AlertDialog dialog = builder.create();
|
||||
dialog.setOnShowListener(d -> onCreateDialog((AlertDialog) d));
|
||||
|
||||
HostsActivity activity = (HostsActivity) dialog.getOwnerActivity();
|
||||
activity.hosts.put(hostString, ipString);
|
||||
activity.notifyHostsChanges();
|
||||
return dialog;
|
||||
}
|
||||
|
||||
dialog.dismiss();
|
||||
protected abstract void onCreateDialogBuilder(AlertDialog.Builder builder);
|
||||
|
||||
protected abstract void onCreateDialog(AlertDialog dialog);
|
||||
|
||||
protected void put(AlertDialog dialog) {
|
||||
TextView host = dialog.findViewById(R.id.host);
|
||||
TextView ip = dialog.findViewById(R.id.ip);
|
||||
String hostString = host.getText().toString().trim().toLowerCase(Locale.US);
|
||||
String ipString = ip.getText().toString().trim();
|
||||
|
||||
if (!Hosts.isValidHost(hostString)) {
|
||||
TextInputLayout hostInputLayout = dialog.findViewById(R.id.host_input_layout);
|
||||
hostInputLayout.setError(getContext().getString(R.string.invalid_host));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Hosts.isValidIp(ipString)) {
|
||||
TextInputLayout ipInputLayout = dialog.findViewById(R.id.ip_input_layout);
|
||||
ipInputLayout.setError(getContext().getString(R.string.invalid_ip));
|
||||
return;
|
||||
}
|
||||
|
||||
HostsActivity activity = (HostsActivity) dialog.getOwnerActivity();
|
||||
activity.hosts.put(hostString, ipString);
|
||||
activity.notifyHostsChanges();
|
||||
|
||||
dialog.dismiss();
|
||||
}
|
||||
|
||||
protected void delete(AlertDialog dialog) {
|
||||
TextView host = dialog.findViewById(R.id.host);
|
||||
String hostString = host.getText().toString().trim().toLowerCase(Locale.US);
|
||||
|
||||
HostsActivity activity = (HostsActivity) dialog.getOwnerActivity();
|
||||
activity.hosts.delete(hostString);
|
||||
activity.notifyHostsChanges();
|
||||
|
||||
dialog.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
protected void delete(AlertDialog dialog) {
|
||||
TextView host = dialog.findViewById(R.id.host);
|
||||
String hostString = host.getText().toString().trim().toLowerCase(Locale.US);
|
||||
public static class AddHostDialogFragment extends HostDialogFragment {
|
||||
|
||||
HostsActivity activity = (HostsActivity) dialog.getOwnerActivity();
|
||||
activity.hosts.delete(hostString);
|
||||
activity.notifyHostsChanges();
|
||||
@Override
|
||||
protected void onCreateDialogBuilder(AlertDialog.Builder builder) {
|
||||
builder.setTitle(R.string.add_host);
|
||||
builder.setPositiveButton(R.string.add_host_add, null);
|
||||
}
|
||||
|
||||
dialog.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
public static class AddHostDialogFragment extends HostDialogFragment {
|
||||
|
||||
@Override
|
||||
protected void onCreateDialogBuilder(AlertDialog.Builder builder) {
|
||||
builder.setTitle(R.string.add_host);
|
||||
builder.setPositiveButton(R.string.add_host_add, null);
|
||||
@Override
|
||||
protected void onCreateDialog(AlertDialog dialog) {
|
||||
dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(v -> put(dialog));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreateDialog(AlertDialog dialog) {
|
||||
dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(v -> put(dialog));
|
||||
}
|
||||
}
|
||||
public static class EditHostDialogFragment extends HostDialogFragment {
|
||||
|
||||
public static class EditHostDialogFragment extends HostDialogFragment {
|
||||
@Override
|
||||
protected void onCreateDialogBuilder(AlertDialog.Builder builder) {
|
||||
builder.setTitle(R.string.edit_host);
|
||||
builder.setPositiveButton(R.string.edit_host_confirm, null);
|
||||
builder.setNegativeButton(R.string.edit_host_delete, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreateDialogBuilder(AlertDialog.Builder builder) {
|
||||
builder.setTitle(R.string.edit_host);
|
||||
builder.setPositiveButton(R.string.edit_host_confirm, null);
|
||||
builder.setNegativeButton(R.string.edit_host_delete, null);
|
||||
@Override
|
||||
protected void onCreateDialog(AlertDialog dialog) {
|
||||
dialog.findViewById(R.id.host_input_layout).setEnabled(false);
|
||||
dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(v -> put(dialog));
|
||||
dialog.getButton(DialogInterface.BUTTON_NEGATIVE).setOnClickListener(v -> delete(dialog));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreateDialog(AlertDialog dialog) {
|
||||
dialog.findViewById(R.id.host_input_layout).setEnabled(false);
|
||||
dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(v -> put(dialog));
|
||||
dialog.getButton(DialogInterface.BUTTON_NEGATIVE).setOnClickListener(v -> delete(dialog));
|
||||
private class HostsHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
public final TextView host;
|
||||
public final TextView ip;
|
||||
|
||||
public HostsHolder(View itemView) {
|
||||
super(itemView);
|
||||
host = itemView.findViewById(R.id.host);
|
||||
ip = itemView.findViewById(R.id.ip);
|
||||
}
|
||||
}
|
||||
|
||||
private class HostsAdapter extends RecyclerView.Adapter<HostsHolder> {
|
||||
|
||||
private final LayoutInflater inflater = getLayoutInflater();
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public HostsHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
return new HostsHolder(inflater.inflate(R.layout.item_hosts, parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull HostsHolder holder, int position) {
|
||||
Pair<String, String> pair = data.get(position);
|
||||
holder.host.setText(pair.first);
|
||||
holder.ip.setText(pair.second);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return data.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,9 @@ package com.hippo.ehviewer.ui;
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
|
||||
public class LicenseActivity extends ToolbarActivity {
|
||||
|
||||
@@ -36,14 +36,16 @@ import android.widget.Button;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.IdRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.drawerlayout.widget.DrawerLayout;
|
||||
|
||||
import com.google.android.material.navigation.NavigationView;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.hippo.drawerlayout.DrawerLayout;
|
||||
import com.hippo.ehviewer.AppConfig;
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.R;
|
||||
@@ -85,9 +87,9 @@ import com.hippo.util.BitmapUtils;
|
||||
import com.hippo.util.PermissionRequester;
|
||||
import com.hippo.widget.LoadImageView;
|
||||
import com.hippo.yorozuya.IOUtils;
|
||||
import com.hippo.yorozuya.ResourcesUtils;
|
||||
import com.hippo.yorozuya.SimpleHandler;
|
||||
import com.hippo.yorozuya.ViewUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -103,24 +105,6 @@ public final class MainActivity extends StageActivity
|
||||
private static final String KEY_NAV_CHECKED_ITEM = "nav_checked_item";
|
||||
private static final String KEY_CLIP_TEXT_HASH_CODE = "clip_text_hash_code";
|
||||
|
||||
/*---------------
|
||||
Whole life cycle
|
||||
---------------*/
|
||||
@Nullable
|
||||
private EhDrawerLayout mDrawerLayout;
|
||||
@Nullable
|
||||
private NavigationView mNavView;
|
||||
@Nullable
|
||||
private FrameLayout mRightDrawer;
|
||||
@Nullable
|
||||
private LoadImageView mAvatar;
|
||||
@Nullable
|
||||
private TextView mDisplayName;
|
||||
@Nullable
|
||||
private Button mChangeTheme;
|
||||
|
||||
private int mNavCheckedItem = 0;
|
||||
|
||||
static {
|
||||
registerLaunchMode(SecurityScene.class, SceneFragment.LAUNCH_MODE_SINGLE_TASK);
|
||||
registerLaunchMode(WarningScene.class, SceneFragment.LAUNCH_MODE_SINGLE_TASK);
|
||||
@@ -142,6 +126,23 @@ public final class MainActivity extends StageActivity
|
||||
registerLaunchMode(ProgressScene.class, SceneFragment.LAUNCH_MODE_STANDARD);
|
||||
}
|
||||
|
||||
/*---------------
|
||||
Whole life cycle
|
||||
---------------*/
|
||||
@Nullable
|
||||
private EhDrawerLayout mDrawerLayout;
|
||||
@Nullable
|
||||
private NavigationView mNavView;
|
||||
@Nullable
|
||||
private FrameLayout mRightDrawer;
|
||||
@Nullable
|
||||
private LoadImageView mAvatar;
|
||||
@Nullable
|
||||
private TextView mDisplayName;
|
||||
@Nullable
|
||||
private Button mChangeTheme;
|
||||
private int mNavCheckedItem = 0;
|
||||
|
||||
@Override
|
||||
protected int getThemeResId(int theme) {
|
||||
switch (theme) {
|
||||
@@ -334,7 +335,7 @@ public final class MainActivity extends StageActivity
|
||||
mDisplayName = (TextView) ViewUtils.$$(headerLayout, R.id.display_name);
|
||||
mChangeTheme = (Button) ViewUtils.$$(this, R.id.change_theme);
|
||||
|
||||
mDrawerLayout.setStatusBarColor(ResourcesUtils.getAttrColor(this, R.attr.colorPrimaryDark));
|
||||
//mDrawerLayout.setStatusBarColor(ResourcesUtils.getAttrColor(this, R.attr.colorPrimaryDark));
|
||||
// Pre-L need shadow drawable
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow_left, Gravity.LEFT);
|
||||
@@ -524,7 +525,7 @@ public final class MainActivity extends StageActivity
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode,
|
||||
@NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
@NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
if (requestCode == PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE) {
|
||||
if (grantResults.length == 1 && grantResults[0] != PackageManager.PERMISSION_GRANTED) {
|
||||
Toast.makeText(this, R.string.you_rejected_me, Toast.LENGTH_SHORT).show();
|
||||
@@ -629,15 +630,9 @@ public final class MainActivity extends StageActivity
|
||||
}
|
||||
}
|
||||
|
||||
public void setDrawerGestureBlocker(DrawerLayout.GestureBlocker gestureBlocker) {
|
||||
if (mDrawerLayout != null) {
|
||||
mDrawerLayout.setGestureBlocker(gestureBlocker);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isDrawersVisible() {
|
||||
if (mDrawerLayout != null) {
|
||||
return mDrawerLayout.isDrawersVisible();
|
||||
return mDrawerLayout.isDrawerVisible(mDrawerLayout);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -26,84 +26,87 @@ import android.webkit.CookieManager;
|
||||
import android.webkit.CookieSyncManager;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.client.EhCookieStore;
|
||||
import com.hippo.ehviewer.client.EhUrl;
|
||||
import com.hippo.ehviewer.widget.DialogWebChromeClient;
|
||||
import com.hippo.widget.ProgressView;
|
||||
|
||||
import okhttp3.Cookie;
|
||||
import okhttp3.HttpUrl;
|
||||
|
||||
public class MyTagsActivity extends ToolbarActivity {
|
||||
|
||||
private WebView webView;
|
||||
private ProgressView progress;
|
||||
private String url;
|
||||
private WebView webView;
|
||||
private ProgressView progress;
|
||||
private String url;
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// http://stackoverflow.com/questions/32284642/how-to-handle-an-uncatched-exception
|
||||
CookieManager cookieManager = CookieManager.getInstance();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
cookieManager.flush();
|
||||
cookieManager.removeAllCookies(null);
|
||||
cookieManager.removeSessionCookies(null);
|
||||
} else {
|
||||
CookieSyncManager cookieSyncManager = CookieSyncManager.createInstance(this);
|
||||
cookieSyncManager.startSync();
|
||||
cookieManager.removeAllCookie();
|
||||
cookieManager.removeSessionCookie();
|
||||
cookieSyncManager.stopSync();
|
||||
}
|
||||
|
||||
// Copy cookies from okhttp cookie store to CookieManager
|
||||
url = EhUrl.getMyTagsUrl();
|
||||
EhCookieStore store = EhApplication.getEhCookieStore(this);
|
||||
for (Cookie cookie : store.getCookies(HttpUrl.parse(url))) {
|
||||
cookieManager.setCookie(url, cookie.toString());
|
||||
}
|
||||
|
||||
setContentView(R.layout.activity_my_tags);
|
||||
setNavigationIcon(R.drawable.v_arrow_left_dark_x24);
|
||||
webView = findViewById(R.id.webview);
|
||||
webView.getSettings().setJavaScriptEnabled(true);
|
||||
webView.setWebViewClient(new MyTagsWebViewClient());
|
||||
webView.setWebChromeClient(new DialogWebChromeClient(this));
|
||||
webView.loadUrl(url);
|
||||
progress = findViewById(R.id.progress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
private class MyTagsWebViewClient extends WebViewClient {
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||
// Never load other urls
|
||||
return !url.equals(MyTagsActivity.this.url);
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// http://stackoverflow.com/questions/32284642/how-to-handle-an-uncatched-exception
|
||||
CookieManager cookieManager = CookieManager.getInstance();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
cookieManager.flush();
|
||||
cookieManager.removeAllCookies(null);
|
||||
cookieManager.removeSessionCookies(null);
|
||||
} else {
|
||||
CookieSyncManager cookieSyncManager = CookieSyncManager.createInstance(this);
|
||||
cookieSyncManager.startSync();
|
||||
cookieManager.removeAllCookie();
|
||||
cookieManager.removeSessionCookie();
|
||||
cookieSyncManager.stopSync();
|
||||
}
|
||||
|
||||
// Copy cookies from okhttp cookie store to CookieManager
|
||||
url = EhUrl.getMyTagsUrl();
|
||||
EhCookieStore store = EhApplication.getEhCookieStore(this);
|
||||
for (Cookie cookie : store.getCookies(HttpUrl.parse(url))) {
|
||||
cookieManager.setCookie(url, cookie.toString());
|
||||
}
|
||||
|
||||
setContentView(R.layout.activity_my_tags);
|
||||
setNavigationIcon(R.drawable.v_arrow_left_dark_x24);
|
||||
webView = findViewById(R.id.webview);
|
||||
webView.getSettings().setJavaScriptEnabled(true);
|
||||
webView.setWebViewClient(new MyTagsWebViewClient());
|
||||
webView.setWebChromeClient(new DialogWebChromeClient(this));
|
||||
webView.loadUrl(url);
|
||||
progress = findViewById(R.id.progress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||||
progress.setVisibility(View.VISIBLE);
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageFinished(WebView view, String url) {
|
||||
progress.setVisibility(View.GONE);
|
||||
private class MyTagsWebViewClient extends WebViewClient {
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||
// Never load other urls
|
||||
return !url.equals(MyTagsActivity.this.url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||||
progress.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageFinished(WebView view, String url) {
|
||||
progress.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,8 +23,10 @@ import android.text.TextUtils;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.Settings;
|
||||
import com.hippo.widget.lockpattern.LockPatternUtils;
|
||||
@@ -42,6 +44,16 @@ public class SetSecurityActivity extends ToolbarActivity implements View.OnClick
|
||||
@Nullable
|
||||
private CheckBox mFingerprint;
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
public static boolean hasEnrolledFingerprints(FingerprintManager fingerprintManager) {
|
||||
try {
|
||||
return fingerprintManager.isHardwareDetected()
|
||||
&& fingerprintManager.hasEnrolledFingerprints();
|
||||
} catch (Throwable e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -73,16 +85,6 @@ public class SetSecurityActivity extends ToolbarActivity implements View.OnClick
|
||||
mSet.setOnClickListener(this);
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
public static boolean hasEnrolledFingerprints(FingerprintManager fingerprintManager) {
|
||||
try {
|
||||
return fingerprintManager.isHardwareDetected()
|
||||
&& fingerprintManager.hasEnrolledFingerprints();
|
||||
} catch (Throwable e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
Loaded 100 of 248 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user