Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf3769959f | ||
|
|
10e853f968 | ||
|
|
ac6995be8d | ||
|
|
045d6f15a7 | ||
|
|
2d16f1cb57 | ||
|
|
ebe1d451b0 | ||
|
|
7faa4bea0d | ||
|
|
3785d3130c | ||
|
|
9daa1c27c4 | ||
|
|
67c722ed43 | ||
|
|
3c8f1809aa | ||
|
|
c5a3059e31 | ||
|
|
f305a59880 | ||
|
|
aa18e5f2f1 | ||
|
|
ac25b95eae | ||
|
|
a92fa304f1 | ||
|
|
2f3f3d6480 | ||
|
|
0d108393fc | ||
|
|
8d02f8c6a3 | ||
|
|
87d284ad97 | ||
|
|
c7ffdbec93 | ||
|
|
f9aad6ce9c | ||
|
|
7a64fd4c11 | ||
|
|
909af0cbed | ||
|
|
34db6fc79f | ||
|
|
b025f150c9 | ||
|
|
fea6b38c1e | ||
|
|
e2a03427f9 | ||
|
|
654cb5fdd5 | ||
|
|
333419b716 | ||
|
|
c66b1e4622 | ||
|
|
ac55e2f8fc | ||
|
|
24718e7d25 | ||
|
|
72e1fe9621 | ||
|
|
0c7bf96bf5 | ||
|
|
f1dde6c8bc | ||
|
|
3177a8352f | ||
|
|
3cb024b7f5 | ||
|
|
0a09d16500 | ||
|
|
a79b1ad785 | ||
|
|
4388c53929 | ||
|
|
02f000d37b | ||
|
|
7259e5e315 | ||
|
|
7ce744b5af | ||
|
|
57f21c8d39 | ||
|
|
07f27a8438 | ||
|
|
4f4dc55b4e | ||
|
|
8e9cda8e93 | ||
|
|
d5cfabd73e | ||
|
|
4f1b371e75 |
No files matched your search
@@ -43,17 +43,6 @@ Only Support Android S and above!
|
||||
|
||||

|
||||
|
||||
|
||||
# Build
|
||||
|
||||
本项目使用了 CMake 3.23.1 作为构建工具链之一,而当前版本的 Android Studio SDK Tool 无法满足本项目需求。
|
||||
|
||||
因此,在构建本项目前需要提前配置好 CMake >=3.23.1。
|
||||
|
||||
This Project use CMake 3.23.1, with is highter than Android Studio bundle CMake.
|
||||
|
||||
So you need to pre-install CMake>=3.23.1 before build.
|
||||
|
||||
# Thanks
|
||||
|
||||
本项目受到了诸多开源项目的帮助
|
||||
@@ -76,6 +65,7 @@ Here is the libraries
|
||||
- [TagSoup](http://home.ccil.org/~cowan/tagsoup/)
|
||||
- [libarchive](http://www.libarchive.org/)
|
||||
- [XZ Utils](https://tukaani.org/xz/)
|
||||
- [Zstd](http://www.zstd.net/)
|
||||
|
||||
|
||||
# License
|
||||
|
||||
+6
-12
@@ -35,11 +35,11 @@ android {
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "io.github.nekoinverter.ehviewer"
|
||||
applicationId "moe.tarsin.ehviewer"
|
||||
minSdkVersion 31
|
||||
targetSdkVersion 32
|
||||
versionCode 114523
|
||||
versionName "1.7.27.3"
|
||||
versionCode 114526
|
||||
versionName "1.7.27.6"
|
||||
resConfigs 'zh', 'zh-rCN', 'zh-rHK', 'zh-rTW', 'es', 'ja', 'ko', 'fr', 'de', 'th'
|
||||
}
|
||||
|
||||
@@ -100,18 +100,17 @@ tasks.withType(JavaCompile) {
|
||||
|
||||
dependencies {
|
||||
implementation 'androidx.activity:activity:1.4.0'
|
||||
implementation "androidx.activity:activity-compose:1.4.0"
|
||||
implementation 'androidx.biometric:biometric:1.1.0'
|
||||
implementation 'androidx.activity:activity-compose:1.4.0'
|
||||
implementation 'androidx.biometric:biometric-ktx:1.2.0-alpha04'
|
||||
implementation 'androidx.browser:browser:1.4.0'
|
||||
implementation 'androidx.core:core:1.7.0'
|
||||
implementation 'androidx.dynamicanimation:dynamicanimation:1.1.0-alpha03'
|
||||
implementation 'androidx.fragment:fragment:1.4.1'
|
||||
implementation 'androidx.preference:preference:1.2.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.2.0-alpha01'
|
||||
implementation 'androidx.webkit:webkit:1.4.0'
|
||||
implementation 'com.drakeet.drawer:drawer:1.0.3'
|
||||
implementation 'com.google.android.material:material:1.6.0'
|
||||
implementation 'com.google.android.material:material:1.7.0-alpha02'
|
||||
implementation 'com.getkeepsafe.taptargetview:taptargetview:1.13.3'
|
||||
implementation 'com.github.seven332:beerbelly:0.1.4'
|
||||
implementation 'com.github.seven332:glgallery:0.1.2'
|
||||
@@ -126,13 +125,8 @@ dependencies {
|
||||
implementation 'com.squareup.okhttp3:okhttp:4.9.3'
|
||||
implementation 'com.squareup.okhttp3:okhttp-dnsoverhttps:4.9.3'
|
||||
implementation 'com.takisoft.preferencex:preferencex:1.1.0'
|
||||
implementation 'com.gitlab.NekoInverter:yorozuya:723dc867a9'
|
||||
implementation 'dev.rikka.rikkax.appcompat:appcompat:1.4.1'
|
||||
implementation 'dev.rikka.rikkax.core:core:1.4.0'
|
||||
implementation 'dev.rikka.rikkax.insets:insets:1.2.0'
|
||||
implementation 'dev.rikka.rikkax.layoutinflater:layoutinflater:1.2.0'
|
||||
implementation 'dev.rikka.rikkax.material:material:2.4.0'
|
||||
implementation 'dev.rikka.rikkax.recyclerview:recyclerview-ktx:1.3.1'
|
||||
implementation 'dev.rikka.rikkax.preference:simplemenu-preference:1.0.3'
|
||||
implementation 'org.ccil.cowan.tagsoup:tagsoup:1.2.1'
|
||||
implementation 'org.greenrobot:greendao:3.3.0'
|
||||
|
||||
@@ -14,16 +14,14 @@
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="28"
|
||||
tools:ignore="ScopedStorage" />
|
||||
android:maxSdkVersion="28" />
|
||||
|
||||
<uses-feature
|
||||
android:glEsVersion="0x00020000"
|
||||
@@ -41,8 +39,7 @@
|
||||
android:largeHeap="true"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:theme="@style/AppTheme"
|
||||
tools:ignore="UnusedAttribute">
|
||||
android:theme="@style/AppTheme">
|
||||
|
||||
<activity
|
||||
android:name="com.hippo.ehviewer.ui.MainActivity"
|
||||
|
||||
@@ -86,7 +86,7 @@ public class Settings {
|
||||
****** 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 KEY_SECURITY = "require_unlock";
|
||||
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";
|
||||
@@ -1028,22 +1028,18 @@ public class Settings {
|
||||
return getBoolean(KEY_SAVE_CRASH_LOG, DEFAULT_SAVE_CRASH_LOG);
|
||||
}
|
||||
|
||||
public static String getSecurity() {
|
||||
return getString(KEY_SECURITY, DEFAULT_SECURITY);
|
||||
public static boolean getSecurity() {
|
||||
return getBoolean(KEY_SECURITY, false);
|
||||
}
|
||||
|
||||
public static void putSecurity(String value) {
|
||||
putString(KEY_SECURITY, value);
|
||||
public static void putSecurity(boolean value) {
|
||||
putBoolean(KEY_SECURITY, value);
|
||||
}
|
||||
|
||||
public static boolean getEnableFingerprint() {
|
||||
return getBoolean(KEY_ENABLE_FINGERPRINT, true);
|
||||
}
|
||||
|
||||
public static void putEnableFingerprint(boolean value) {
|
||||
putBoolean(KEY_ENABLE_FINGERPRINT, value);
|
||||
}
|
||||
|
||||
public static int getReadCacheSize() {
|
||||
return getIntFromStr(KEY_READ_CACHE_SIZE, DEFAULT_READ_CACHE_SIZE);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NotificationChannelCompat;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.R;
|
||||
@@ -224,8 +223,6 @@ public class DownloadService extends Service implements DownloadManager.Download
|
||||
.setOngoing(true)
|
||||
.setAutoCancel(false)
|
||||
.setCategory(NotificationCompat.CATEGORY_PROGRESS)
|
||||
.setColor(ContextCompat.getColor(this, R.color.colorPrimary))
|
||||
.setColorized(true)
|
||||
.addAction(R.drawable.v_pause_x24, getString(R.string.stat_download_action_stop_all), piStopAll)
|
||||
.setShowWhen(false)
|
||||
.setChannelId(CHANNEL_ID);
|
||||
@@ -254,7 +251,6 @@ public class DownloadService extends Service implements DownloadManager.Download
|
||||
mDownloadedBuilder = new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID)
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download_done)
|
||||
.setContentTitle(getString(R.string.stat_download_done_title))
|
||||
.setColor(ContextCompat.getColor(this, R.color.colorPrimary))
|
||||
.setDeleteIntent(piClear)
|
||||
.setOngoing(false)
|
||||
.setAutoCancel(true)
|
||||
@@ -273,7 +269,6 @@ public class DownloadService extends Service implements DownloadManager.Download
|
||||
.setContentText(getString(R.string.stat_509_alert_title))
|
||||
.setContentText(getString(R.string.stat_509_alert_text))
|
||||
.setStyle(new NotificationCompat.BigTextStyle().bigText(getString(R.string.stat_509_alert_text)))
|
||||
.setColor(ContextCompat.getColor(this, R.color.colorPrimary))
|
||||
.setAutoCancel(true)
|
||||
.setOngoing(false)
|
||||
.setCategory(NotificationCompat.CATEGORY_ERROR);
|
||||
|
||||
@@ -49,7 +49,6 @@ import android.webkit.MimeTypeMap;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.Switch;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
@@ -66,7 +65,7 @@ import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.core.view.WindowInsetsControllerCompat;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.android.material.switchmaterial.SwitchMaterial;
|
||||
import com.google.android.material.materialswitch.MaterialSwitch;
|
||||
import com.hippo.ehviewer.AppConfig;
|
||||
import com.hippo.ehviewer.BuildConfig;
|
||||
import com.hippo.ehviewer.R;
|
||||
@@ -261,6 +260,16 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
|
||||
mGalleryInfo = intent.getParcelableExtra(KEY_GALLERY_INFO);
|
||||
mPage = intent.getIntExtra(KEY_PAGE, -1);
|
||||
buildProvider();
|
||||
}
|
||||
|
||||
private void onRestore(@NonNull Bundle savedInstanceState) {
|
||||
mAction = savedInstanceState.getString(KEY_ACTION);
|
||||
mFilename = savedInstanceState.getString(KEY_FILENAME);
|
||||
mUri = savedInstanceState.getParcelable(KEY_URI);
|
||||
mGalleryInfo = savedInstanceState.getParcelable(KEY_GALLERY_INFO);
|
||||
mPage = savedInstanceState.getInt(KEY_PAGE, -1);
|
||||
mCurrentIndex = savedInstanceState.getInt(KEY_CURRENT_INDEX);
|
||||
buildProvider();
|
||||
} ActivityResultLauncher<String> requestStoragePermissionLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.RequestPermission(),
|
||||
result -> {
|
||||
@@ -272,16 +281,6 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
|
||||
mSavingPage = -1;
|
||||
});
|
||||
|
||||
private void onRestore(@NonNull Bundle savedInstanceState) {
|
||||
mAction = savedInstanceState.getString(KEY_ACTION);
|
||||
mFilename = savedInstanceState.getString(KEY_FILENAME);
|
||||
mUri = savedInstanceState.getParcelable(KEY_URI);
|
||||
mGalleryInfo = savedInstanceState.getParcelable(KEY_GALLERY_INFO);
|
||||
mPage = savedInstanceState.getInt(KEY_PAGE, -1);
|
||||
mCurrentIndex = savedInstanceState.getInt(KEY_CURRENT_INDEX);
|
||||
buildProvider();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
@@ -1048,15 +1047,15 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan
|
||||
private final Spinner mScaleMode;
|
||||
private final Spinner mStartPosition;
|
||||
private final Spinner mReadTheme;
|
||||
private final SwitchMaterial mKeepScreenOn;
|
||||
private final SwitchMaterial mShowClock;
|
||||
private final SwitchMaterial mShowProgress;
|
||||
private final SwitchMaterial mShowBattery;
|
||||
private final SwitchMaterial mShowPageInterval;
|
||||
private final SwitchMaterial mVolumePage;
|
||||
private final SwitchMaterial mReverseVolumePage;
|
||||
private final SwitchMaterial mReadingFullscreen;
|
||||
private final SwitchMaterial mCustomScreenLightness;
|
||||
private final MaterialSwitch mKeepScreenOn;
|
||||
private final MaterialSwitch mShowClock;
|
||||
private final MaterialSwitch mShowProgress;
|
||||
private final MaterialSwitch mShowBattery;
|
||||
private final MaterialSwitch mShowPageInterval;
|
||||
private final MaterialSwitch mVolumePage;
|
||||
private final MaterialSwitch mReverseVolumePage;
|
||||
private final MaterialSwitch mReadingFullscreen;
|
||||
private final MaterialSwitch mCustomScreenLightness;
|
||||
private final SeekBar mScreenLightness;
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
|
||||
@@ -28,7 +28,6 @@ import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.PersistableBundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Gravity;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
@@ -143,7 +142,7 @@ public final class MainActivity extends StageActivity
|
||||
@NonNull
|
||||
@Override
|
||||
protected Announcer getLaunchAnnouncer() {
|
||||
if (!TextUtils.isEmpty(Settings.getSecurity())) {
|
||||
if (Settings.getSecurity() && SecurityScene.Companion.isAuthenticationSupported(this)) {
|
||||
return new Announcer(SecurityScene.class);
|
||||
} else if (Settings.getShowWarning()) {
|
||||
return new Announcer(WarningScene.class);
|
||||
@@ -161,7 +160,7 @@ public final class MainActivity extends StageActivity
|
||||
// Sometimes scene can't show directly
|
||||
private Announcer processAnnouncer(Announcer announcer) {
|
||||
if (0 == getSceneCount()) {
|
||||
if (!TextUtils.isEmpty(Settings.getSecurity())) {
|
||||
if (Settings.getSecurity() && SecurityScene.Companion.isAuthenticationSupported(this)) {
|
||||
Bundle newArgs = new Bundle();
|
||||
newArgs.putString(SecurityScene.KEY_TARGET_SCENE, announcer.getClazz().getName());
|
||||
newArgs.putBundle(SecurityScene.KEY_TARGET_ARGS, announcer.getArgs());
|
||||
|
||||
@@ -12,7 +12,6 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.hippo.ehviewer.ui.SettingsActivity;
|
||||
import com.takisoft.preferencex.PreferenceFragmentCompat;
|
||||
|
||||
import rikka.recyclerview.RecyclerViewKt;
|
||||
|
||||
public class BasePreferenceFragment extends PreferenceFragmentCompat
|
||||
implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener {
|
||||
@@ -53,11 +52,4 @@ public class BasePreferenceFragment extends PreferenceFragmentCompat
|
||||
public void showTip(CharSequence message, int length) {
|
||||
((SettingsActivity) requireActivity()).showTip(message, length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
|
||||
RecyclerView recyclerView = super.onCreateRecyclerView(inflater, parent, savedInstanceState);
|
||||
RecyclerViewKt.fixEdgeEffect(recyclerView, false, true);
|
||||
return recyclerView;
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package com.hippo.ehviewer.ui.fragment;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.Settings;
|
||||
|
||||
/**
|
||||
* Created by Mo10 on 2018/2/10.
|
||||
*/
|
||||
|
||||
public class PrivacyFragment extends BasePreferenceFragment {
|
||||
private static final String KEY_PATTERN_PROTECTION = "pattern_protection";
|
||||
|
||||
@Override
|
||||
public void onCreatePreferencesFix(@Nullable Bundle savedInstanceState, String rootKey) {
|
||||
addPreferencesFromResource(R.xml.privacy_settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
Preference patternProtection = findPreference(KEY_PATTERN_PROTECTION);
|
||||
patternProtection.setSummary(TextUtils.isEmpty(Settings.getSecurity()) ?
|
||||
R.string.settings_privacy_pattern_protection_not_set :
|
||||
R.string.settings_privacy_pattern_protection_set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFragmentTitle() {
|
||||
return R.string.settings_privacy;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.hippo.ehviewer.ui.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.ui.scene.SecurityScene.Companion.isAuthenticationSupported
|
||||
|
||||
/**
|
||||
* Created by Mo10 on 2018/2/10.
|
||||
*/
|
||||
class PrivacyFragment : BasePreferenceFragment() {
|
||||
override fun onCreatePreferencesFix(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.privacy_settings)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
if (!isAuthenticationSupported(requireContext())) {
|
||||
Settings.putSecurity(false)
|
||||
findPreference<SwitchPreferenceCompat>("require_unlock")?.isEnabled = false
|
||||
findPreference<SwitchPreferenceCompat>("require_unlock")?.isChecked = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun getFragmentTitle(): Int {
|
||||
return R.string.settings_privacy
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
package com.hippo.ehviewer.ui.fragment;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.biometric.BiometricManager;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.Settings;
|
||||
import com.hippo.widget.lockpattern.LockPatternUtils;
|
||||
import com.hippo.widget.lockpattern.LockPatternView;
|
||||
import com.hippo.yorozuya.ViewUtils;
|
||||
|
||||
public class SetSecurityFragment extends BaseFragment implements View.OnClickListener {
|
||||
|
||||
@Nullable
|
||||
private LockPatternView mPatternView;
|
||||
@Nullable
|
||||
private View mCancel;
|
||||
@Nullable
|
||||
private View mSet;
|
||||
@Nullable
|
||||
private CheckBox mFingerprint;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.activity_set_security, container, false);
|
||||
|
||||
mPatternView = (LockPatternView) ViewUtils.$$(view, R.id.pattern_view);
|
||||
mCancel = ViewUtils.$$(view, R.id.cancel);
|
||||
mSet = ViewUtils.$$(view, R.id.set);
|
||||
mFingerprint = (CheckBox) ViewUtils.$$(view, R.id.fingerprint_checkbox);
|
||||
|
||||
String pattern = Settings.getSecurity();
|
||||
if (!TextUtils.isEmpty(pattern)) {
|
||||
mPatternView.setPattern(LockPatternView.DisplayMode.Correct,
|
||||
LockPatternUtils.stringToPattern(pattern));
|
||||
}
|
||||
|
||||
if (BiometricManager.from(requireContext()).canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) == BiometricManager.BIOMETRIC_SUCCESS) {
|
||||
mFingerprint.setVisibility(View.VISIBLE);
|
||||
mFingerprint.setChecked(Settings.getEnableFingerprint());
|
||||
}
|
||||
|
||||
mCancel.setOnClickListener(this);
|
||||
mSet.setOnClickListener(this);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
mPatternView = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (v == mCancel) {
|
||||
requireActivity().onBackPressed();
|
||||
} else if (v == mSet) {
|
||||
if (null != mPatternView && null != mFingerprint) {
|
||||
String security;
|
||||
if (mPatternView.getCellSize() <= 1) {
|
||||
security = "";
|
||||
} else {
|
||||
security = mPatternView.getPatternString();
|
||||
}
|
||||
Settings.putSecurity(security);
|
||||
Settings.putEnableFingerprint(mFingerprint.getVisibility() == View.VISIBLE &&
|
||||
mFingerprint.isChecked() && !security.isEmpty());
|
||||
}
|
||||
requireActivity().onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFragmentTitle() {
|
||||
return R.string.set_pattern_protection;
|
||||
}
|
||||
}
|
||||
@@ -114,7 +114,6 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import rikka.core.res.ResourcesKt;
|
||||
import rikka.recyclerview.RecyclerViewKt;
|
||||
|
||||
@SuppressLint("RtlHardcoded")
|
||||
public class DownloadsScene extends ToolbarScene
|
||||
@@ -378,8 +377,6 @@ public class DownloadsScene extends ToolbarScene
|
||||
mRecyclerView.scrollToPosition(mInitPosition);
|
||||
mInitPosition = -1;
|
||||
}
|
||||
RecyclerViewKt.addEdgeSpacing(mRecyclerView, 4, 4, 4, 4, TypedValue.COMPLEX_UNIT_DIP);
|
||||
RecyclerViewKt.fixEdgeEffect(mRecyclerView, false, true);
|
||||
|
||||
mFastScroller.attachToRecyclerView(mRecyclerView);
|
||||
HandlerDrawable handlerDrawable = new HandlerDrawable();
|
||||
@@ -468,7 +465,6 @@ public class DownloadsScene extends ToolbarScene
|
||||
TapTarget.forBounds(bounds,
|
||||
getString(R.string.guide_download_labels_title),
|
||||
getString(R.string.guide_download_labels_text))
|
||||
.outerCircleColor(R.color.colorPrimary)
|
||||
.transparentTarget(true),
|
||||
new TapTargetView.Listener() {
|
||||
@Override
|
||||
|
||||
@@ -68,6 +68,7 @@ import com.hippo.ehviewer.client.data.FavListUrlBuilder;
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo;
|
||||
import com.hippo.ehviewer.client.parser.FavoritesParser;
|
||||
import com.hippo.ehviewer.ui.CommonOperations;
|
||||
import com.hippo.ehviewer.ui.EhActivity;
|
||||
import com.hippo.ehviewer.ui.MainActivity;
|
||||
import com.hippo.ehviewer.ui.annotation.DrawerLifeCircle;
|
||||
import com.hippo.ehviewer.ui.annotation.ViewLifeCircle;
|
||||
@@ -290,7 +291,13 @@ public class FavoritesScene extends BaseScene implements
|
||||
updateSearchBar();
|
||||
mSearchBarMover = new SearchBarMover(this, mSearchBar, mRecyclerView);
|
||||
|
||||
mActionFabDrawable = new AddDeleteDrawable(context, resources.getColor(R.color.primary_drawable_dark));
|
||||
int colorID;
|
||||
if (EhActivity.isNightMode(context.getResources().getConfiguration())) {
|
||||
colorID = resources.getColor(android.R.color.white, getTheme());
|
||||
} else {
|
||||
colorID = resources.getColor(android.R.color.black, getTheme());
|
||||
}
|
||||
mActionFabDrawable = new AddDeleteDrawable(context, colorID);
|
||||
mFabLayout.getPrimaryFab().setImageDrawable(mActionFabDrawable);
|
||||
mFabLayout.setExpanded(false, false);
|
||||
mFabLayout.setAutoCancel(true);
|
||||
@@ -334,7 +341,6 @@ public class FavoritesScene extends BaseScene implements
|
||||
TapTarget.forBounds(bounds,
|
||||
getString(R.string.guide_collections_title),
|
||||
getString(R.string.guide_collections_text))
|
||||
.outerCircleColor(R.color.colorPrimary)
|
||||
.transparentTarget(true),
|
||||
new TapTargetView.Listener() {
|
||||
@Override
|
||||
|
||||
@@ -94,7 +94,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import rikka.core.res.ResourcesKt;
|
||||
import rikka.recyclerview.RecyclerViewKt;
|
||||
|
||||
public final class GalleryCommentsScene extends ToolbarScene
|
||||
implements View.OnClickListener, SwipeRefreshLayout.OnRefreshListener {
|
||||
@@ -176,7 +175,6 @@ public final class GalleryCommentsScene extends ToolbarScene
|
||||
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.scene_gallery_comments, container, false);
|
||||
mRecyclerView = (EasyRecyclerView) ViewUtils.$$(view, R.id.recycler_view);
|
||||
RecyclerViewKt.fixEdgeEffect(mRecyclerView, false, true);
|
||||
TextView tip = (TextView) ViewUtils.$$(view, R.id.tip);
|
||||
mEditPanel = ViewUtils.$$(view, R.id.edit_panel);
|
||||
mSendImage = (ImageView) ViewUtils.$$(mEditPanel, R.id.send);
|
||||
@@ -223,7 +221,6 @@ public final class GalleryCommentsScene extends ToolbarScene
|
||||
if (itemAnimator instanceof DefaultItemAnimator) {
|
||||
((DefaultItemAnimator) itemAnimator).setSupportsChangeAnimations(false);
|
||||
}
|
||||
RecyclerViewKt.addEdgeSpacing(mRecyclerView, 0, 0, 0, 80, TypedValue.COMPLEX_UNIT_DIP);
|
||||
|
||||
mSendImage.setOnClickListener(this);
|
||||
mFab.setOnClickListener(this);
|
||||
|
||||
@@ -44,7 +44,6 @@ import com.hippo.yorozuya.ViewUtils;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import rikka.core.res.ResourcesKt;
|
||||
import rikka.recyclerview.RecyclerViewKt;
|
||||
|
||||
public final class GalleryInfoScene extends ToolbarScene {
|
||||
|
||||
@@ -174,7 +173,6 @@ public final class GalleryInfoScene extends ToolbarScene {
|
||||
mRecyclerView.addItemDecoration(decoration);
|
||||
mRecyclerView.setClipToPadding(false);
|
||||
mRecyclerView.setHasFixedSize(true);
|
||||
RecyclerViewKt.fixEdgeEffect(mRecyclerView, false, true);
|
||||
return view;
|
||||
}
|
||||
|
||||
|
||||
@@ -96,6 +96,7 @@ import com.hippo.ehviewer.dao.DownloadLabel;
|
||||
import com.hippo.ehviewer.dao.QuickSearch;
|
||||
import com.hippo.ehviewer.download.DownloadManager;
|
||||
import com.hippo.ehviewer.ui.CommonOperations;
|
||||
import com.hippo.ehviewer.ui.EhActivity;
|
||||
import com.hippo.ehviewer.ui.GalleryActivity;
|
||||
import com.hippo.ehviewer.ui.MainActivity;
|
||||
import com.hippo.ehviewer.ui.dialog.SelectItemWithIconAdapter;
|
||||
@@ -613,7 +614,13 @@ public final class GalleryListScene extends BaseScene
|
||||
mFabLayout.setOnExpandListener(this);
|
||||
addAboveSnackView(mFabLayout);
|
||||
|
||||
mActionFabDrawable = new AddDeleteDrawable(context, resources.getColor(R.color.primary_drawable_dark));
|
||||
int colorID;
|
||||
if (EhActivity.isNightMode(context.getResources().getConfiguration())) {
|
||||
colorID = resources.getColor(android.R.color.white, getTheme());
|
||||
} else {
|
||||
colorID = resources.getColor(android.R.color.black, getTheme());
|
||||
}
|
||||
mActionFabDrawable = new AddDeleteDrawable(context, colorID);
|
||||
mFabLayout.getPrimaryFab().setImageDrawable(mActionFabDrawable);
|
||||
|
||||
mSearchFab.setOnClickListener(this);
|
||||
@@ -657,7 +664,6 @@ public final class GalleryListScene extends BaseScene
|
||||
TapTarget.forBounds(bounds,
|
||||
getString(R.string.guide_quick_search_title),
|
||||
getString(R.string.guide_quick_search_text))
|
||||
.outerCircleColor(R.color.colorPrimary)
|
||||
.transparentTarget(true),
|
||||
new TapTargetView.Listener() {
|
||||
@Override
|
||||
|
||||
@@ -86,8 +86,6 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import rikka.recyclerview.RecyclerViewKt;
|
||||
|
||||
public class HistoryScene extends ToolbarScene {
|
||||
|
||||
/*---------------
|
||||
@@ -232,8 +230,6 @@ public class HistoryScene extends ToolbarScene {
|
||||
mRecyclerView.addItemDecoration(decoration);
|
||||
guardManager.attachRecyclerView(mRecyclerView);
|
||||
swipeManager.attachRecyclerView(mRecyclerView);
|
||||
RecyclerViewKt.fixEdgeEffect(mRecyclerView, false, true);
|
||||
RecyclerViewKt.addEdgeSpacing(mRecyclerView, 4, 4, 4, 4, TypedValue.COMPLEX_UNIT_DIP);
|
||||
|
||||
mFastScroller.attachToRecyclerView(mRecyclerView);
|
||||
HandlerDrawable handlerDrawable = new HandlerDrawable();
|
||||
|
||||
@@ -1,193 +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.scene;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorManager;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.biometric.BiometricManager;
|
||||
import androidx.biometric.BiometricPrompt;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.Settings;
|
||||
import com.hippo.ehviewer.ui.MainActivity;
|
||||
import com.hippo.widget.lockpattern.LockPatternUtils;
|
||||
import com.hippo.widget.lockpattern.LockPatternView;
|
||||
import com.hippo.yorozuya.ObjectUtils;
|
||||
import com.hippo.yorozuya.ViewUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class SecurityScene extends SolidScene implements
|
||||
LockPatternView.OnPatternListener {
|
||||
|
||||
private static final int MAX_RETRY_TIMES = 5;
|
||||
private static final String KEY_RETRY_TIMES = "retry_times";
|
||||
|
||||
@Nullable
|
||||
private LockPatternView mPatternView;
|
||||
|
||||
private SensorManager mSensorManager;
|
||||
private Sensor mAccelerometer;
|
||||
private BiometricPrompt.PromptInfo promptInfo;
|
||||
private BiometricPrompt biometricPrompt;
|
||||
private boolean canAuthenticate = false;
|
||||
|
||||
private int mRetryTimes;
|
||||
|
||||
@Override
|
||||
public boolean needShowLeftDrawer() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Context context = requireContext();
|
||||
mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
|
||||
if (null != mSensorManager) {
|
||||
mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
|
||||
}
|
||||
|
||||
if (null == savedInstanceState) {
|
||||
mRetryTimes = MAX_RETRY_TIMES;
|
||||
} else {
|
||||
mRetryTimes = savedInstanceState.getInt(KEY_RETRY_TIMES);
|
||||
}
|
||||
|
||||
canAuthenticate = Settings.getEnableFingerprint() &&
|
||||
BiometricManager.from(requireContext()).canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) == BiometricManager.BIOMETRIC_SUCCESS;
|
||||
biometricPrompt = new BiometricPrompt(this, Executors.newSingleThreadExecutor(), new BiometricPrompt.AuthenticationCallback() {
|
||||
@Override
|
||||
public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) {
|
||||
super.onAuthenticationError(errorCode, errString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
|
||||
startSceneForCheckStep(CHECK_STEP_SECURITY, getArguments());
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailed() {
|
||||
super.onAuthenticationFailed();
|
||||
}
|
||||
});
|
||||
promptInfo = new BiometricPrompt.PromptInfo.Builder()
|
||||
.setTitle(getString(R.string.app_name))
|
||||
.setNegativeButtonText(getString(android.R.string.cancel))
|
||||
.setConfirmationRequired(false)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
mSensorManager = null;
|
||||
mAccelerometer = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
if (canAuthenticate) {
|
||||
startBiometricPrompt();
|
||||
}
|
||||
}
|
||||
|
||||
private void startBiometricPrompt() {
|
||||
biometricPrompt.authenticate(promptInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putInt(KEY_RETRY_TIMES, mRetryTimes);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.scene_security, container, false);
|
||||
if (canAuthenticate) {
|
||||
view.setOnClickListener(v -> startBiometricPrompt());
|
||||
}
|
||||
|
||||
mPatternView = (LockPatternView) ViewUtils.$$(view, R.id.pattern_view);
|
||||
mPatternView.setOnPatternListener(this);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
mPatternView = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPatternStart() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPatternCleared() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPatternCellAdded(List<LockPatternView.Cell> pattern) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPatternDetected(List<LockPatternView.Cell> pattern) {
|
||||
MainActivity activity = getMainActivity();
|
||||
if (null == activity || null == mPatternView) {
|
||||
return;
|
||||
}
|
||||
|
||||
String enteredPatter = LockPatternUtils.patternToString(pattern);
|
||||
String targetPatter = Settings.getSecurity();
|
||||
|
||||
if (ObjectUtils.equal(enteredPatter, targetPatter)) {
|
||||
startSceneForCheckStep(CHECK_STEP_SECURITY, getArguments());
|
||||
finish();
|
||||
} else {
|
||||
mPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
|
||||
mRetryTimes--;
|
||||
if (mRetryTimes <= 0) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2022 tarsin norbin
|
||||
*
|
||||
* 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.scene
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import androidx.biometric.BiometricManager
|
||||
import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
|
||||
import androidx.biometric.BiometricPrompt
|
||||
import androidx.biometric.auth.AuthPromptCallback
|
||||
import androidx.biometric.auth.startClass2BiometricOrCredentialAuthentication
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import com.hippo.ehviewer.R
|
||||
|
||||
class SecurityScene : SolidScene() {
|
||||
override fun needShowLeftDrawer(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
if (isAuthenticationSupported(requireContext()))
|
||||
startAuthentication(getString(R.string.settings_privacy_require_unlock))
|
||||
else
|
||||
startSceneForCheckStep(CHECK_STEP_SECURITY, arguments)
|
||||
}
|
||||
|
||||
private fun startAuthentication(
|
||||
title: String,
|
||||
subtitle: String? = null,
|
||||
confirmationRequired: Boolean = true,
|
||||
) {
|
||||
startClass2BiometricOrCredentialAuthentication(
|
||||
title = title,
|
||||
subtitle = subtitle,
|
||||
confirmationRequired = confirmationRequired,
|
||||
executor = ContextCompat.getMainExecutor(requireContext()),
|
||||
callback = AuthenticationCallback()
|
||||
)
|
||||
}
|
||||
|
||||
inner class AuthenticationCallback : AuthPromptCallback() {
|
||||
override fun onAuthenticationSucceeded(
|
||||
activity: FragmentActivity?,
|
||||
result: BiometricPrompt.AuthenticationResult
|
||||
) {
|
||||
super.onAuthenticationSucceeded(activity, result)
|
||||
startSceneForCheckStep(CHECK_STEP_SECURITY, arguments)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun isAuthenticationSupported(context: Context): Boolean {
|
||||
val authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK or DEVICE_CREDENTIAL
|
||||
return BiometricManager.from(context)
|
||||
.canAuthenticate(authenticators) == BiometricManager.BIOMETRIC_SUCCESS
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,6 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.chip.Chip
|
||||
import com.google.android.material.chip.ChipGroup
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.switchmaterial.SwitchMaterial
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
|
||||
import com.hippo.easyrecyclerview.EasyRecyclerView
|
||||
@@ -59,7 +58,7 @@ class SearchLayout @JvmOverloads constructor(
|
||||
private var mNormalView: View
|
||||
private var mNormalSearchMode: RadioGridGroup
|
||||
private var mNormalSearchModeHelp: ImageView
|
||||
private var mEnableAdvanceSwitch: SwitchMaterial
|
||||
private var mEnableAdvanceSwitch: CheckBox
|
||||
private var mAdvanceView: View
|
||||
private var mTableAdvanceSearch: AdvanceSearchTable
|
||||
private var mImageView: ImageSearchLayout
|
||||
@@ -99,6 +98,11 @@ class SearchLayout @JvmOverloads constructor(
|
||||
mChip.setText(mPair.second)
|
||||
mChip.idt = mPair.first
|
||||
mChip.isChecked = NumberUtils.int2boolean(mPair.first and mCategoryStored)
|
||||
mChip.setOnLongClickListener {
|
||||
mCategoryGroup.clearCheck()
|
||||
mChip.isChecked = true
|
||||
true
|
||||
}
|
||||
mCategoryGroup.addView(mChip)
|
||||
}
|
||||
mCategoryGroup.setOnCheckedStateChangeListener { _, checkedIds ->
|
||||
@@ -113,8 +117,6 @@ class SearchLayout @JvmOverloads constructor(
|
||||
mEnableAdvanceSwitch = mNormalView.findViewById(R.id.search_enable_advance)
|
||||
mNormalSearchModeHelp.setOnClickListener(this)
|
||||
mEnableAdvanceSwitch.setOnCheckedChangeListener(this)
|
||||
mEnableAdvanceSwitch.switchPadding =
|
||||
resources.getDimensionPixelSize(R.dimen.switch_padding)
|
||||
// Create advance view
|
||||
mAdvanceView = mInflater.inflate(R.layout.search_advance, null)
|
||||
mTableAdvanceSearch = mAdvanceView.findViewById(R.id.search_advance_search_table)
|
||||
|
||||
@@ -43,9 +43,14 @@ public class BatteryView extends AppCompatTextView {
|
||||
private BatteryDrawable mDrawable;
|
||||
private boolean mAttached = false;
|
||||
private boolean mIsChargerWorking = false;
|
||||
|
||||
public BatteryView(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public BatteryView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
} private final Runnable mCharger = new Runnable() {
|
||||
|
||||
private int level = 0;
|
||||
@@ -61,10 +66,6 @@ public class BatteryView extends AppCompatTextView {
|
||||
}
|
||||
};
|
||||
|
||||
public BatteryView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public BatteryView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
@@ -101,6 +102,24 @@ public class BatteryView extends AppCompatTextView {
|
||||
getHandler().post(mCharger);
|
||||
mIsChargerWorking = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void stopCharger() {
|
||||
if (mIsChargerWorking) {
|
||||
getHandler().removeCallbacks(mCharger);
|
||||
mIsChargerWorking = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
|
||||
if (!mAttached) {
|
||||
mAttached = true;
|
||||
|
||||
registerReceiver();
|
||||
}
|
||||
} private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
|
||||
|
||||
@Override
|
||||
@@ -132,24 +151,6 @@ public class BatteryView extends AppCompatTextView {
|
||||
}
|
||||
};
|
||||
|
||||
private void stopCharger() {
|
||||
if (mIsChargerWorking) {
|
||||
getHandler().removeCallbacks(mCharger);
|
||||
mIsChargerWorking = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
|
||||
if (!mAttached) {
|
||||
mAttached = true;
|
||||
|
||||
registerReceiver();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
|
||||
@@ -56,8 +56,6 @@ import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import rikka.recyclerview.RecyclerViewKt;
|
||||
|
||||
public class ContentLayout extends FrameLayout {
|
||||
|
||||
private CircularProgressIndicator mProgressView;
|
||||
@@ -124,9 +122,6 @@ public class ContentLayout extends FrameLayout {
|
||||
resources.getColor(R.color.loading_indicator_orange));
|
||||
mBottomProgress.setIndeterminateAnimationType(LinearProgressIndicator.INDETERMINATE_ANIMATION_TYPE_CONTIGUOUS);
|
||||
|
||||
RecyclerViewKt.addEdgeSpacing(mRecyclerView, 4, 4, 4, 4, TypedValue.COMPLEX_UNIT_DIP);
|
||||
RecyclerViewKt.fixEdgeEffect(mRecyclerView, false, true);
|
||||
|
||||
mRecyclerViewOriginBottom = mRecyclerView.getPaddingBottom();
|
||||
mFastScrollerOriginBottom = mFastScroller.getPaddingBottom();
|
||||
}
|
||||
|
||||
@@ -1,93 +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.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.yorozuya.AssertUtils;
|
||||
|
||||
public class MaxSizeContainer extends ViewGroup {
|
||||
|
||||
private int mMaxWidth;
|
||||
private int mMaxHeight;
|
||||
|
||||
public MaxSizeContainer(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context, attrs);
|
||||
}
|
||||
|
||||
public MaxSizeContainer(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context, attrs);
|
||||
}
|
||||
|
||||
private void init(Context context, AttributeSet attrs) {
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MaxSizeContainer);
|
||||
mMaxWidth = a.getDimensionPixelOffset(R.styleable.MaxSizeContainer_maxWidth, -1);
|
||||
mMaxHeight = a.getDimensionPixelOffset(R.styleable.MaxSizeContainer_maxHeight, -1);
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
private int getMeasureSpec(int measureSpec, int max) {
|
||||
if (max < 0) {
|
||||
return measureSpec;
|
||||
}
|
||||
|
||||
int size = MeasureSpec.getSize(measureSpec);
|
||||
int mode = MeasureSpec.getMode(measureSpec);
|
||||
|
||||
switch (mode) {
|
||||
case MeasureSpec.AT_MOST:
|
||||
size = Math.min(size, max);
|
||||
break;
|
||||
case MeasureSpec.UNSPECIFIED:
|
||||
size = max;
|
||||
mode = MeasureSpec.AT_MOST;
|
||||
break;
|
||||
}
|
||||
return MeasureSpec.makeMeasureSpec(size, mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
AssertUtils.assertEquals("getChildCount() must be 1", 1, getChildCount());
|
||||
|
||||
View child = getChildAt(0);
|
||||
if (child.getVisibility() != GONE) {
|
||||
child.measure(getMeasureSpec(widthMeasureSpec, mMaxWidth),
|
||||
getMeasureSpec(heightMeasureSpec, mMaxHeight));
|
||||
setMeasuredDimension(child.getMeasuredWidth(), child.getMeasuredHeight());
|
||||
} else {
|
||||
setMeasuredDimension(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
AssertUtils.assertEquals("getChildCount() must be 1", 1, getChildCount());
|
||||
|
||||
View child = getChildAt(0);
|
||||
if (child.getVisibility() != GONE) {
|
||||
child.layout(0, 0, r - l, b - t);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,8 +66,13 @@ public class TextClock extends AppCompatTextView {
|
||||
onTimeChanged();
|
||||
}
|
||||
};
|
||||
|
||||
public TextClock(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public TextClock(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
} private final Runnable mTicker = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -80,10 +85,6 @@ public class TextClock extends AppCompatTextView {
|
||||
}
|
||||
};
|
||||
|
||||
public TextClock(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public TextClock(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
@@ -98,6 +99,18 @@ public class TextClock extends AppCompatTextView {
|
||||
createTime(mTimeZone);
|
||||
// Wait until onAttachedToWindow() to handle the ticker
|
||||
chooseFormat(false);
|
||||
}
|
||||
|
||||
private void createTime(String timeZone) {
|
||||
if (timeZone != null) {
|
||||
mTime = Calendar.getInstance(TimeZone.getTimeZone(timeZone));
|
||||
} else {
|
||||
mTime = Calendar.getInstance();
|
||||
}
|
||||
}
|
||||
|
||||
public CharSequence getFormat12Hour() {
|
||||
return mFormat12;
|
||||
} private final ContentObserver mFormatChangeObserver = new ContentObserver(new Handler()) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
@@ -112,18 +125,6 @@ public class TextClock extends AppCompatTextView {
|
||||
}
|
||||
};
|
||||
|
||||
private void createTime(String timeZone) {
|
||||
if (timeZone != null) {
|
||||
mTime = Calendar.getInstance(TimeZone.getTimeZone(timeZone));
|
||||
} else {
|
||||
mTime = Calendar.getInstance();
|
||||
}
|
||||
}
|
||||
|
||||
public CharSequence getFormat12Hour() {
|
||||
return mFormat12;
|
||||
}
|
||||
|
||||
public void setFormat12Hour(CharSequence format) {
|
||||
mFormat12 = format;
|
||||
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* 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.widget.lockpattern;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utilities for the lock pattern and its settings.
|
||||
*/
|
||||
public class LockPatternUtils {
|
||||
|
||||
/**
|
||||
* Deserialize a pattern.
|
||||
*
|
||||
* @param string The pattern serialized with {@link #patternToString}
|
||||
* @return The pattern.
|
||||
*/
|
||||
public static List<LockPatternView.Cell> stringToPattern(String string) {
|
||||
List<LockPatternView.Cell> result = new ArrayList<>();
|
||||
|
||||
final byte[] bytes = string.getBytes();
|
||||
for (byte b : bytes) {
|
||||
result.add(LockPatternView.Cell.of(b / 3, b % 3));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize a pattern.
|
||||
*
|
||||
* @param pattern The pattern.
|
||||
* @return The pattern in string form.
|
||||
*/
|
||||
public static String patternToString(List<LockPatternView.Cell> pattern) {
|
||||
if (pattern == null) {
|
||||
return "";
|
||||
}
|
||||
final int patternSize = pattern.size();
|
||||
|
||||
byte[] res = new byte[patternSize];
|
||||
for (int i = 0; i < patternSize; i++) {
|
||||
LockPatternView.Cell cell = pattern.get(i);
|
||||
res[i] = (byte) (cell.getRow() * 3 + cell.getColumn());
|
||||
}
|
||||
return new String(res);
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.yorozuya;
|
||||
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import androidx.interpolator.view.animation.FastOutLinearInInterpolator;
|
||||
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
|
||||
import androidx.interpolator.view.animation.LinearOutSlowInInterpolator;
|
||||
|
||||
public final class AnimationUtils {
|
||||
private AnimationUtils() {
|
||||
}
|
||||
|
||||
public static final Interpolator FAST_SLOW_INTERPOLATOR = new LinearOutSlowInInterpolator();
|
||||
public static final Interpolator SLOW_FAST_INTERPOLATOR = new FastOutLinearInInterpolator();
|
||||
public static final Interpolator SLOW_FAST_SLOW_INTERPOLATOR = new FastOutSlowInInterpolator();
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.yorozuya;
|
||||
|
||||
public class AssertError extends Error {
|
||||
|
||||
public AssertError(String detailMessage) {
|
||||
super(detailMessage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.yorozuya;
|
||||
|
||||
public class AssertException extends Exception {
|
||||
|
||||
public AssertException(String detailMessage) {
|
||||
super(detailMessage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.yorozuya;
|
||||
|
||||
public final class AssertUtils {
|
||||
private AssertUtils() {}
|
||||
|
||||
public static void assertTrue(boolean cond) {
|
||||
assertTrue("Condition has to be true.", cond);
|
||||
}
|
||||
|
||||
public static void assertTrueEx(boolean cond) throws AssertException {
|
||||
assertTrueEx("Condition has to be true.", cond);
|
||||
}
|
||||
|
||||
public static void assertTrue(String message, boolean cond) {
|
||||
if (!cond) {
|
||||
throw new AssertError(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertTrueEx(String message, boolean cond) throws AssertException {
|
||||
if (!cond) {
|
||||
throw new AssertException(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertNull(Object object) {
|
||||
assertNull("Should be null", object);
|
||||
}
|
||||
|
||||
public static void assertNullEx(Object object) throws AssertException {
|
||||
assertNullEx("Should be null", object);
|
||||
}
|
||||
|
||||
public static void assertNull(String message, Object object) {
|
||||
if (object != null) {
|
||||
throw new AssertError(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertNullEx(String message, Object object) throws AssertException {
|
||||
if (object != null) {
|
||||
throw new AssertException(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertNotNull(Object object) {
|
||||
assertNotNull("Should not be null", object);
|
||||
}
|
||||
|
||||
public static void assertNotNullEx(Object object) throws AssertException {
|
||||
assertNotNullEx("Should not be null", object);
|
||||
}
|
||||
|
||||
public static void assertNotNull(String message, Object object) {
|
||||
if (object == null) {
|
||||
throw new AssertError(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertNotNullEx(String message, Object object) throws AssertException {
|
||||
if (object == null) {
|
||||
throw new AssertException(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertEquals(int expected, int actual) {
|
||||
assertEquals("Should be " + expected + ", but it is " + actual, expected, actual);
|
||||
}
|
||||
|
||||
public static void assertEqualsEx(int expected, int actual) throws AssertException {
|
||||
assertEqualsEx("Should be " + expected + ", but it is " + actual, expected, actual);
|
||||
}
|
||||
|
||||
public static void assertEquals(String message, int expected, int actual) {
|
||||
if (expected != actual) {
|
||||
throw new AssertError(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertEqualsEx(String message, int expected, int actual) throws AssertException {
|
||||
if (expected != actual) {
|
||||
throw new AssertException(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertInstanceOf(Object obj, Class clazz) {
|
||||
assertInstanceOf("The object should be instance of " + clazz.getName(), obj, clazz);
|
||||
}
|
||||
|
||||
public static void assertInstanceOfEx(Object obj, Class clazz) throws AssertException {
|
||||
assertInstanceOfEx("The object should be instance of " + clazz.getName(), obj, clazz);
|
||||
}
|
||||
|
||||
public static void assertInstanceOf(String message, Object obj, Class clazz) {
|
||||
if (!clazz.isInstance(obj)) {
|
||||
throw new AssertError(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertInstanceOfEx(String message, Object obj, Class clazz) throws AssertException {
|
||||
if (!clazz.isInstance(obj)) {
|
||||
throw new AssertException(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2015 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.yorozuya;
|
||||
|
||||
public final class ColorUtils {
|
||||
private ColorUtils() {}
|
||||
|
||||
/**
|
||||
* Check is it the color opaque
|
||||
*
|
||||
* @param color the color
|
||||
* @return true if it is opaque
|
||||
*/
|
||||
public static boolean isOpaque(int color) {
|
||||
return color >>> 24 == 0xFF;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.yorozuya;
|
||||
|
||||
public class ConcurrentPool<T> {
|
||||
|
||||
private final T[] mArray;
|
||||
private final int mMaxSize;
|
||||
private int mSize;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ConcurrentPool(int size) {
|
||||
if (size <= 0) {
|
||||
throw new IllegalStateException("Pool size must > 0, it is " + size);
|
||||
}
|
||||
mArray = (T[]) new Object[size];
|
||||
mMaxSize = size;
|
||||
mSize = 0;
|
||||
}
|
||||
|
||||
public synchronized void push(T t) {
|
||||
if (t != null && mSize < mMaxSize) {
|
||||
mArray[mSize++] = t;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized T pop() {
|
||||
if (mSize > 0) {
|
||||
T t = mArray[--mSize];
|
||||
mArray[mSize] = null;
|
||||
return t;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,326 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.yorozuya;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Locale;
|
||||
|
||||
public final class FileUtils {
|
||||
private FileUtils() {}
|
||||
|
||||
public static boolean ensureFile(File file) {
|
||||
return file != null && (!file.exists() || file.isFile());
|
||||
}
|
||||
|
||||
public static boolean ensureDirectory(File file) {
|
||||
if (file != null) {
|
||||
if (file.exists()) {
|
||||
return file.isDirectory();
|
||||
} else {
|
||||
return file.mkdirs();
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert byte to human readable string.<br/>
|
||||
* http://stackoverflow.com/questions/3758606/
|
||||
*
|
||||
* @param bytes the bytes to convert
|
||||
* @param si si units
|
||||
* @return the human readable string
|
||||
*/
|
||||
public static String humanReadableByteCount(long bytes, boolean si) {
|
||||
int unit = si ? 1000 : 1024;
|
||||
if (bytes < unit) return bytes + " B";
|
||||
int exp = (int) (Math.log(bytes) / Math.log(unit));
|
||||
String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp-1) + (si ? "" : "i");
|
||||
return String.format(Locale.US, "%.1f %sB", bytes / Math.pow(unit, exp), pre);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to delete file, dir and it's children
|
||||
*
|
||||
* @param file the file to delete
|
||||
* The dir to deleted
|
||||
*/
|
||||
public static boolean delete(File file) {
|
||||
if (file == null) {
|
||||
return false;
|
||||
}
|
||||
boolean success = true;
|
||||
File[] files = file.listFiles();
|
||||
if (files != null) {
|
||||
for (File f : files) {
|
||||
success &= delete(f);
|
||||
}
|
||||
}
|
||||
/*
|
||||
final File to = new File(file.getAbsolutePath()
|
||||
+ System.currentTimeMillis());
|
||||
if (file.renameTo(to)) {
|
||||
success &= to.delete();
|
||||
} else
|
||||
success &= file.delete();
|
||||
}
|
||||
*/
|
||||
success &= file.delete();
|
||||
return success;
|
||||
}
|
||||
|
||||
public static boolean deleteContent(File file) {
|
||||
if (file == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean success = true;
|
||||
File[] files = file.listFiles();
|
||||
if (files != null) {
|
||||
for (File f : files) {
|
||||
success &= delete(f);
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code null} for get exception
|
||||
*/
|
||||
@Nullable
|
||||
public static String read(File file) {
|
||||
if (file == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = new FileInputStream(file);
|
||||
return IOUtils.readString(is, "utf-8");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} finally {
|
||||
IOUtils.closeQuietly(is);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean write(File file, String str) {
|
||||
if (file == null) {
|
||||
return false;
|
||||
}
|
||||
if (str == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
OutputStream os = null;
|
||||
try {
|
||||
os = new FileOutputStream(file);
|
||||
os.write(str.getBytes("utf-8"));
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
} finally {
|
||||
IOUtils.closeQuietly(os);
|
||||
}
|
||||
}
|
||||
|
||||
private static final char[] FORBIDDEN_FILENAME_CHARACTERS = {
|
||||
'\\',
|
||||
'/',
|
||||
':',
|
||||
'*',
|
||||
'?',
|
||||
'"',
|
||||
'<',
|
||||
'>',
|
||||
'|',
|
||||
};
|
||||
|
||||
public static String sanitizeFilename(@NonNull String filename) {
|
||||
// Remove forbidden_filename_characters
|
||||
filename = StringUtils.remove(filename, FORBIDDEN_FILENAME_CHARACTERS);
|
||||
|
||||
// Ensure utf-8 byte count <= 255
|
||||
int byteCount = 0;
|
||||
int length = 0;
|
||||
for (int len = filename.length(); length < len; length++) {
|
||||
char ch = filename.charAt(length);
|
||||
if (ch <= 0x7F) {
|
||||
byteCount++;
|
||||
} else if (ch <= 0x7FF) {
|
||||
byteCount += 2;
|
||||
} else if (Character.isHighSurrogate(ch)) {
|
||||
byteCount += 4;
|
||||
++length;
|
||||
} else {
|
||||
byteCount += 3;
|
||||
}
|
||||
// Meet max byte count
|
||||
if (byteCount > 255) {
|
||||
filename = filename.substring(0, length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Trim
|
||||
return StringUtils.trim(filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extension from filename
|
||||
*
|
||||
* @param filename the complete filename
|
||||
* @return null for can't find extension, "" empty String for ending with . dot
|
||||
*/
|
||||
public static String getExtensionFromFilename(@Nullable String filename) {
|
||||
if (null == filename) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int index = filename.lastIndexOf('.');
|
||||
if (index != -1) {
|
||||
return filename.substring(index + 1);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name from filename
|
||||
*
|
||||
* @param filename the complete filename
|
||||
* @return null for start with . dot
|
||||
*/
|
||||
public static String getNameFromFilename(@Nullable String filename) {
|
||||
if (null == filename) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int index = filename.lastIndexOf('.');
|
||||
if (index != -1) {
|
||||
String name = filename.substring(0, index);
|
||||
return TextUtils.isEmpty(name) ? null : name;
|
||||
} else {
|
||||
return filename;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a temp file, you need to delete it by you self.
|
||||
*
|
||||
* @param parent The temp file's parent
|
||||
* @param extension The extension of temp file
|
||||
* @return The temp file or null
|
||||
*/
|
||||
@Nullable
|
||||
public static File createTempFile(@Nullable File parent, @Nullable String extension) {
|
||||
if (parent == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
String filename = Long.toString(now + i);
|
||||
if (extension != null) {
|
||||
filename = filename + '.' + extension;
|
||||
}
|
||||
File tempFile = new File(parent, filename);
|
||||
if (!tempFile.exists()) {
|
||||
return tempFile;
|
||||
}
|
||||
}
|
||||
|
||||
// Unbelievable
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a temp dir, you need to delete it by you self.
|
||||
*
|
||||
* @param parent The temp file's parent
|
||||
* @return The temp dir or null
|
||||
*/
|
||||
@Nullable
|
||||
public static File createTempDir(@Nullable File parent) {
|
||||
if (parent == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
String filename = Long.toString(now + i);
|
||||
File tempFile = new File(parent, filename);
|
||||
if (!tempFile.exists() && tempFile.mkdirs()) {
|
||||
return tempFile;
|
||||
}
|
||||
}
|
||||
|
||||
// Unbelievable
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only support file now
|
||||
*/
|
||||
public static boolean rename(@NonNull File from, @NonNull File to) {
|
||||
if (!from.isFile() || to.exists()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean ok = from.renameTo(to);
|
||||
if (ok && !from.exists() && to.isFile()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Copy content
|
||||
InputStream is = null;
|
||||
OutputStream os = null;
|
||||
try {
|
||||
is = new FileInputStream(from);
|
||||
os = new FileOutputStream(to);
|
||||
IOUtils.copy(is, os);
|
||||
ok = true;
|
||||
} catch (IOException e) {
|
||||
IOUtils.closeQuietly(is);
|
||||
IOUtils.closeQuietly(os);
|
||||
ok = false;
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
to.delete();
|
||||
return false;
|
||||
}
|
||||
|
||||
// delete old one
|
||||
from.delete();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2015 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.yorozuya;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Closeable;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public final class IOUtils {
|
||||
private IOUtils() {}
|
||||
|
||||
private static final int EOF = -1;
|
||||
|
||||
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
|
||||
|
||||
/**
|
||||
* Close the closeable stuff. Don't worry about anything.
|
||||
*
|
||||
* @param is the closeable stuff
|
||||
*/
|
||||
public static void closeQuietly(Closeable is) {
|
||||
try {
|
||||
if (is != null) {
|
||||
is.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy bytes from an <code>InputStream</code> to an
|
||||
* <code>OutputStream</code>.
|
||||
*
|
||||
* @param input the InputStream
|
||||
* @param output the OutputStream
|
||||
* @return the number of bytes copied
|
||||
* @throws IOException
|
||||
*/
|
||||
public static long copy(InputStream input, OutputStream output)
|
||||
throws IOException {
|
||||
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
|
||||
long count = 0;
|
||||
int n;
|
||||
while (EOF != (n = input.read(buffer))) {
|
||||
output.write(buffer, 0, n);
|
||||
count += n;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ASCII characters up to but not including the next "\r\n", or
|
||||
* "\n".
|
||||
*
|
||||
* @throws java.io.EOFException if the stream is exhausted before the next
|
||||
* newline character.
|
||||
*/
|
||||
public static String readAsciiLine(final InputStream in) throws IOException {
|
||||
final StringBuilder result = new StringBuilder(80);
|
||||
while (true) {
|
||||
final int c = in.read();
|
||||
if (c == -1) {
|
||||
throw new EOFException();
|
||||
} else if (c == '\n') {
|
||||
break;
|
||||
}
|
||||
|
||||
result.append((char)c);
|
||||
}
|
||||
final int length = result.length();
|
||||
if (length > 0 && result.charAt(length - 1) == '\r') {
|
||||
result.setLength(length - 1);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public static String readString(final InputStream is, String encoding) throws IOException {
|
||||
InputStreamReader reader = new InputStreamReader(is, encoding);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
char[] buffer = new char[DEFAULT_BUFFER_SIZE];
|
||||
int n;
|
||||
while (EOF != (n = reader.read(buffer))) {
|
||||
sb.append(buffer, 0, n);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static byte[] getAllByte(InputStream is) throws IOException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
copy(is, baos);
|
||||
return baos.toByteArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2015 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.yorozuya;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public final class IntIdGenerator {
|
||||
|
||||
public static final int INVALID_ID = -1;
|
||||
|
||||
private final AtomicInteger mId = new AtomicInteger();
|
||||
|
||||
public IntIdGenerator() {}
|
||||
|
||||
public IntIdGenerator(int init) {
|
||||
setNextId(init);
|
||||
}
|
||||
|
||||
@SuppressWarnings("StatementWithEmptyBody")
|
||||
public int nextId() {
|
||||
int id;
|
||||
while ((id = mId.getAndIncrement()) == INVALID_ID);
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setNextId(int id) {
|
||||
checkInValidId(id);
|
||||
mId.set(id);
|
||||
}
|
||||
|
||||
private void checkInValidId(int id) {
|
||||
if (INVALID_ID == id) {
|
||||
throw new IllegalStateException("Can't set INVALID_ID");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2015 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.yorozuya;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public final class LayoutUtils {
|
||||
private LayoutUtils() {}
|
||||
|
||||
/**
|
||||
* dp conversion to pix
|
||||
*
|
||||
* @param context The context
|
||||
* @param dp The value you want to conversion
|
||||
* @return value in pix
|
||||
*/
|
||||
public static int dp2pix(Context context, float dp) {
|
||||
return (int) (dp * context.getResources().getDisplayMetrics().density + 0.5f);
|
||||
}
|
||||
|
||||
/**
|
||||
* pix conversion to dp
|
||||
*
|
||||
* @param context The context
|
||||
* @param pix The value you want to conversion
|
||||
* @return value in dp
|
||||
*/
|
||||
public static float pix2dp(Context context, int pix) {
|
||||
return pix / context.getResources().getDisplayMetrics().density;
|
||||
}
|
||||
|
||||
/**
|
||||
* sp conversion to pix
|
||||
*
|
||||
* @param sp The value you want to conversion
|
||||
* @return value in pix
|
||||
*/
|
||||
public static int sp2pix(Context context, float sp) {
|
||||
return (int) (sp * context.getResources().getDisplayMetrics().scaledDensity + 0.5f);
|
||||
}
|
||||
|
||||
/**
|
||||
* pix conversion to sp
|
||||
*
|
||||
* @param pix The value you want to conversion
|
||||
* @return value in sp
|
||||
*/
|
||||
public static float pix2sp(Context context, float pix) {
|
||||
return pix / context.getResources().getDisplayMetrics().scaledDensity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2015 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.yorozuya;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public final class LongIdGenerator {
|
||||
|
||||
public static final long INVALID_ID = -1L;
|
||||
|
||||
private final AtomicLong mId = new AtomicLong();
|
||||
|
||||
public LongIdGenerator() {}
|
||||
|
||||
public LongIdGenerator(long init) {
|
||||
setNextId(init);
|
||||
}
|
||||
|
||||
@SuppressWarnings("StatementWithEmptyBody")
|
||||
public long nextId() {
|
||||
long id;
|
||||
while ((id = mId.getAndIncrement()) == INVALID_ID);
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setNextId(long id) {
|
||||
checkInValidId(id);
|
||||
mId.set(id);
|
||||
}
|
||||
|
||||
private void checkInValidId(long id) {
|
||||
if (INVALID_ID == id) {
|
||||
throw new IllegalStateException("Can't set INVALID_ID");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
* Copyright 2015 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.yorozuya;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
// Get most code from android.util.MathUtils
|
||||
public final class MathUtils {
|
||||
private MathUtils() {}
|
||||
|
||||
private static final Random sRandom = new Random();
|
||||
private static final float DEG_TO_RAD = 3.1415926f / 180.0f;
|
||||
private static final float RAD_TO_DEG = 180.0f / 3.1415926f;
|
||||
|
||||
public static float abs(float v) {
|
||||
return v > 0 ? v : -v;
|
||||
}
|
||||
|
||||
public static float log(float a) {
|
||||
return (float) Math.log(a);
|
||||
}
|
||||
|
||||
public static float exp(float a) {
|
||||
return (float) Math.exp(a);
|
||||
}
|
||||
|
||||
public static float pow(float a, float b) {
|
||||
return (float) Math.pow(a, b);
|
||||
}
|
||||
|
||||
public static float max(float a, float b) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
public static float max(int a, int b) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
public static float max(float a, float b, float c) {
|
||||
return a > b ? (a > c ? a : c) : (b > c ? b : c);
|
||||
}
|
||||
|
||||
public static float max(int a, int b, int c) {
|
||||
return a > b ? (a > c ? a : c) : (b > c ? b : c);
|
||||
}
|
||||
|
||||
public static float max(float... arg) {
|
||||
int length = arg.length;
|
||||
if (length <= 0) {
|
||||
throw new IllegalArgumentException("Empty argument");
|
||||
} else {
|
||||
float n = arg[0];
|
||||
float m;
|
||||
for (int i = 1; i < length; i++) {
|
||||
m = arg[i];
|
||||
if (m > n)
|
||||
n = m;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
public static int max(int... arg) {
|
||||
int length = arg.length;
|
||||
if (length <= 0) {
|
||||
throw new IllegalArgumentException("Empty argument");
|
||||
} else {
|
||||
int n = arg[0];
|
||||
int m;
|
||||
for (int i = 1; i < length; i++) {
|
||||
m = arg[i];
|
||||
if (m > n)
|
||||
n = m;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
public static float min(float a, float b) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
public static float min(int a, int b) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
public static float min(float a, float b, float c) {
|
||||
return a < b ? (a < c ? a : c) : (b < c ? b : c);
|
||||
}
|
||||
|
||||
public static float min(int a, int b, int c) {
|
||||
return a < b ? (a < c ? a : c) : (b < c ? b : c);
|
||||
}
|
||||
|
||||
public static float min(float... args) {
|
||||
int length = args.length;
|
||||
if (length <= 0) {
|
||||
throw new IllegalArgumentException("Empty argument");
|
||||
} else {
|
||||
float n = args[0];
|
||||
float m;
|
||||
for (int i = 1; i < length; i++) {
|
||||
m = args[i];
|
||||
if (m < n)
|
||||
n = m;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
public static int min(int... args) {
|
||||
int length = args.length;
|
||||
if (length <= 0) {
|
||||
throw new IllegalArgumentException("Empty argument");
|
||||
} else {
|
||||
int n = args[0];
|
||||
int m;
|
||||
for (int i = 1; i < length; i++) {
|
||||
m = args[i];
|
||||
if (m < n)
|
||||
n = m;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
public static float dist(float x1, float y1, float x2, float y2) {
|
||||
final float x = (x2 - x1);
|
||||
final float y = (y2 - y1);
|
||||
return (float) Math.hypot(x, y);
|
||||
}
|
||||
|
||||
public static float dist(float x1, float y1, float z1, float x2, float y2, float z2) {
|
||||
final float x = (x2 - x1);
|
||||
final float y = (y2 - y1);
|
||||
final float z = (z2 - z1);
|
||||
return (float) Math.sqrt(x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
public static boolean near(float x1, float y1, float x2, float y2, float slop) {
|
||||
return dist(x1, y1, x2, y2) < slop;
|
||||
}
|
||||
|
||||
public static boolean near(float x1, float y1, float z1, float x2, float y2, float z2, float slop) {
|
||||
return dist(x1, y1, z1, x2, y2, z2) < slop;
|
||||
}
|
||||
|
||||
public static float mag(float a, float b) {
|
||||
return (float) Math.hypot(a, b);
|
||||
}
|
||||
|
||||
public static float mag(float a, float b, float c) {
|
||||
return (float) Math.sqrt(a * a + b * b + c * c);
|
||||
}
|
||||
|
||||
public static float sq(float v) {
|
||||
return v * v;
|
||||
}
|
||||
|
||||
public static float cross(float v1x, float v1y, float v2x, float v2y) {
|
||||
return v1x * v2y - v1y * v2x;
|
||||
}
|
||||
|
||||
public static float radians(float degrees) {
|
||||
return degrees * DEG_TO_RAD;
|
||||
}
|
||||
|
||||
public static float degrees(float radians) {
|
||||
return radians * RAD_TO_DEG;
|
||||
}
|
||||
|
||||
public static float acos(float value) {
|
||||
return (float) Math.acos(value);
|
||||
}
|
||||
|
||||
public static float asin(float value) {
|
||||
return (float) Math.asin(value);
|
||||
}
|
||||
|
||||
public static float atan(float value) {
|
||||
return (float) Math.atan(value);
|
||||
}
|
||||
|
||||
public static float atan2(float a, float b) {
|
||||
return (float) Math.atan2(a, b);
|
||||
}
|
||||
|
||||
public static float tan(float angle) {
|
||||
return (float) Math.tan(angle);
|
||||
}
|
||||
|
||||
public static int lerp(int start, int stop, float amount) {
|
||||
return start + (int) ((stop - start) * amount);
|
||||
}
|
||||
|
||||
public static float lerp(float start, float stop, float amount) {
|
||||
return start + (stop - start) * amount;
|
||||
}
|
||||
|
||||
public static float delerp(int start, int stop, int value) {
|
||||
if (stop == start) {
|
||||
return 1.0f;
|
||||
} else {
|
||||
return (float) (value - start) / (float) (stop - start);
|
||||
}
|
||||
}
|
||||
|
||||
public static float delerp(float start, float stop, float value) {
|
||||
if (stop == start) {
|
||||
return 1.0f;
|
||||
} else {
|
||||
return (value - start) / (stop - start);
|
||||
}
|
||||
}
|
||||
|
||||
public static float norm(float start, float stop, float value) {
|
||||
return (value - start) / (stop - start);
|
||||
}
|
||||
|
||||
public static float map(float minStart, float minStop, float maxStart, float maxStop, float value) {
|
||||
return maxStart + (maxStart - maxStop) * ((value - minStart) / (minStop - minStart));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the input value x clamped to the range [min, max].
|
||||
*/
|
||||
public static int clamp(int x, int min, int max) {
|
||||
if (x > max) return max;
|
||||
if (x < min) return min;
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the input value x clamped to the range [min, max].
|
||||
*/
|
||||
public static float clamp(float x, float min, float max) {
|
||||
if (x > max) return max;
|
||||
if (x < min) return min;
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the input value x clamped to the range [min, max].
|
||||
*/
|
||||
public static long clamp(long x, long min, long max) {
|
||||
if (x > max) return max;
|
||||
if (x < min) return min;
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next power of two.
|
||||
* Returns the input if it is already power of 2.
|
||||
* Throws IllegalArgumentException if the input is <= 0 or
|
||||
* the answer overflows.
|
||||
*/
|
||||
public static int nextPowerOf2(int n) {
|
||||
if (n <= 0 || n > (1 << 30)) throw new IllegalArgumentException("n is invalid: " + n);
|
||||
n -= 1;
|
||||
n |= n >> 16;
|
||||
n |= n >> 8;
|
||||
n |= n >> 4;
|
||||
n |= n >> 2;
|
||||
n |= n >> 1;
|
||||
return n + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the previous power of two.
|
||||
* Returns the input if it is already power of 2.
|
||||
* Throws IllegalArgumentException if the input is <= 0 or
|
||||
* the answer overflows.
|
||||
*/
|
||||
public static int previousPowerOf2 (int n) {
|
||||
if (n <= 0 || n > (1 << 30)) throw new IllegalArgumentException("n is invalid: " + n);
|
||||
n |= n >> 1;
|
||||
n |= n >> 2;
|
||||
n |= n >> 4;
|
||||
n |= n >> 8;
|
||||
n |= n >> 16;
|
||||
return n - (n >> 1);
|
||||
}
|
||||
|
||||
// http://stackoverflow.com/questions/109023/how-to-count-the-number-of-set-bits-in-a-32-bit-integer
|
||||
public static int hammingWeight(int n) {
|
||||
n = n - ((n >>> 1) & 0x55555555);
|
||||
n = (n & 0x33333333) + ((n >>> 2) & 0x33333333);
|
||||
return (((n + (n >>> 4)) & 0x0F0F0F0F) * 0x01010101) >>> 24;
|
||||
}
|
||||
|
||||
/**
|
||||
* divide and ceil
|
||||
*/
|
||||
public static int ceilDivide(int a, int b) {
|
||||
return (a + b - 1) / b;
|
||||
}
|
||||
|
||||
/**
|
||||
* divide and ceil
|
||||
*/
|
||||
public static long ceilDivide(long a, long b) {
|
||||
return (a + b - 1) / b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get coverage radius of a area
|
||||
*
|
||||
* @param w the width of the area
|
||||
* @param h the height of the area
|
||||
* @param x the x of point in area
|
||||
* @param y the y of point in area
|
||||
* @return the radius
|
||||
*/
|
||||
public static float coverageRadius(float w, float h, float x, float y) {
|
||||
float x2;
|
||||
float y2;
|
||||
if (x > w / 2) {
|
||||
x2 = 0;
|
||||
} else {
|
||||
x2 = w;
|
||||
}
|
||||
if (y > h / 2) {
|
||||
y2 = 0;
|
||||
} else {
|
||||
y2 = h;
|
||||
}
|
||||
return dist(x, y, x2, y2);
|
||||
}
|
||||
|
||||
public static float positiveModulo(float x, float y) {
|
||||
float result = x % y;
|
||||
if (x < 0) {
|
||||
result += y;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static float negativeModulo(float x, float y) {
|
||||
float result = x % y;
|
||||
if (x > 0) {
|
||||
result -= y;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* [0, howbig)
|
||||
*/
|
||||
public static int random(int howbig) {
|
||||
return (int) (sRandom.nextFloat() * howbig);
|
||||
}
|
||||
|
||||
/**
|
||||
* [howsmall, howbig)
|
||||
*/
|
||||
public static int random(int howsmall, int howbig) {
|
||||
if (howsmall >= howbig)
|
||||
return howsmall;
|
||||
return lerp(howsmall, howbig, sRandom.nextFloat());
|
||||
}
|
||||
|
||||
/**
|
||||
* [0, howbig)
|
||||
*/
|
||||
public static float random(float howbig) {
|
||||
return sRandom.nextFloat() * howbig;
|
||||
}
|
||||
|
||||
/**
|
||||
* [howsmall, howbig)
|
||||
*/
|
||||
public static float random(float howsmall, float howbig) {
|
||||
if (howsmall >= howbig)
|
||||
return howsmall;
|
||||
return lerp(howsmall, howbig, sRandom.nextFloat());
|
||||
}
|
||||
|
||||
public static void randomSeed(long seed) {
|
||||
sRandom.setSeed(seed);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.yorozuya;
|
||||
|
||||
import android.util.SparseArray;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public final class Messenger {
|
||||
|
||||
private final IntIdGenerator mIdGenerator;
|
||||
private final SparseArray<List<Receiver>> mReceiverListMap;
|
||||
|
||||
private static Messenger sInstance;
|
||||
|
||||
public static Messenger getInstance() {
|
||||
if (sInstance == null) {
|
||||
sInstance = new Messenger();
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
private Messenger() {
|
||||
mIdGenerator = new IntIdGenerator();
|
||||
mReceiverListMap = new SparseArray<>();
|
||||
}
|
||||
|
||||
public int newId() {
|
||||
return mIdGenerator.nextId();
|
||||
}
|
||||
|
||||
private void notifyInternal(int id, Object obj) {
|
||||
List<Receiver> receiverList = mReceiverListMap.get(id);
|
||||
if (receiverList != null) {
|
||||
List<Receiver> temp = new LinkedList<>(receiverList);
|
||||
for (Receiver r : temp) {
|
||||
r.onReceive(id, obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void notify(final int id, final Object obj) {
|
||||
// Make sure do it in UI thread
|
||||
SimpleHandler.getInstance().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
notifyInternal(id, obj);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void notifyAtOnce(int id, Object obj) {
|
||||
notifyInternal(id, obj);
|
||||
}
|
||||
|
||||
public void register(int id, Receiver receiver) {
|
||||
List<Receiver> receiverList = mReceiverListMap.get(id);
|
||||
if (receiverList == null) {
|
||||
receiverList = new ArrayList<>();
|
||||
mReceiverListMap.put(id, receiverList);
|
||||
}
|
||||
|
||||
receiverList.add(receiver);
|
||||
}
|
||||
|
||||
public void unregister(int id, Receiver receiver) {
|
||||
List<Receiver> receiverList = mReceiverListMap.get(id);
|
||||
if (receiverList != null) {
|
||||
receiverList.remove(receiver);
|
||||
if (receiverList.isEmpty()) {
|
||||
mReceiverListMap.remove(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface Receiver {
|
||||
void onReceive(final int id, final Object obj);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2015 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.yorozuya;
|
||||
|
||||
public final class NumberUtils {
|
||||
private NumberUtils() {}
|
||||
|
||||
/**
|
||||
* 0 for false, Non 0 for true
|
||||
*
|
||||
* @param integer the int
|
||||
* @return the boolean
|
||||
*/
|
||||
public static boolean int2boolean(int integer) {
|
||||
return integer != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* false for 0, true for 1
|
||||
*
|
||||
* @param bool the boolean
|
||||
* @return the int
|
||||
*/
|
||||
public static int boolean2int(boolean bool) {
|
||||
return bool ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not throw NumberFormatException, use default value
|
||||
*
|
||||
* @param str the string to be parsed
|
||||
* @param defaultValue the value to return when get error
|
||||
* @return the value of the string
|
||||
*/
|
||||
public static int parseIntSafely(String str, int defaultValue) {
|
||||
try {
|
||||
return Integer.parseInt(str);
|
||||
} catch (Throwable e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not throw NumberFormatException, use default value
|
||||
*
|
||||
* @param str the string to be parsed
|
||||
* @param defaultValue the value to return when get error
|
||||
* @return the value of the string
|
||||
*/
|
||||
public static long parseLongSafely(String str, long defaultValue) {
|
||||
try {
|
||||
return Long.parseLong(str);
|
||||
} catch (Throwable e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not throw NumberFormatException, use default value
|
||||
*
|
||||
* @param str the string to be parsed
|
||||
* @param defaultValue the value to return when get error
|
||||
* @return the value of the string
|
||||
*/
|
||||
public static float parseFloatSafely(String str, float defaultValue) {
|
||||
try {
|
||||
return Float.parseFloat(str);
|
||||
} catch (Throwable e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2015 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.yorozuya;
|
||||
|
||||
import android.os.Looper;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public final class OSUtils {
|
||||
private OSUtils() {}
|
||||
|
||||
private static final String PROCFS_MEMFILE = "/proc/meminfo";
|
||||
private static final Pattern PROCFS_MEMFILE_FORMAT =
|
||||
Pattern.compile("^([a-zA-Z]*):[ \t]*([0-9]*)[ \t]kB");
|
||||
private static final String MEMTOTAL_STRING = "MemTotal";
|
||||
|
||||
private static long sTotalMem = Long.MIN_VALUE;
|
||||
|
||||
public static void checkMainLoop() {
|
||||
if (Looper.myLooper() != Looper.getMainLooper()) {
|
||||
throw new IllegalStateException("It is in not main loop!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get application allocated memory size
|
||||
*/
|
||||
public static long getAppAllocatedMemory() {
|
||||
return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get application max memory size
|
||||
*/
|
||||
public static long getAppMaxMemory() {
|
||||
return Runtime.getRuntime().maxMemory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get device RAM size
|
||||
*/
|
||||
public static long getTotalMemory() {
|
||||
if (sTotalMem == Long.MIN_VALUE) {
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
reader = new BufferedReader(new FileReader(PROCFS_MEMFILE), 64);
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
Matcher matcher = PROCFS_MEMFILE_FORMAT.matcher(line);
|
||||
if (matcher.find() && MEMTOTAL_STRING.equals(matcher.group(1))) {
|
||||
long mem = NumberUtils.parseLongSafely(matcher.group(2), -1L);
|
||||
if (mem != -1L) {
|
||||
mem *= 1024;
|
||||
}
|
||||
sTotalMem = mem;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
IOUtils.closeQuietly(reader);
|
||||
}
|
||||
if (sTotalMem == Long.MIN_VALUE) {
|
||||
sTotalMem = -1L;
|
||||
}
|
||||
}
|
||||
return sTotalMem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright 2015 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.yorozuya;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public final class ObjectUtils {
|
||||
private ObjectUtils() {}
|
||||
|
||||
/**
|
||||
* Returns true if two possibly-null objects are equal.
|
||||
*/
|
||||
public static boolean equal(@Nullable Object a, @Nullable Object b) {
|
||||
return a == b || (a != null && a.equals(b));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns "null" for null or {@code o.toString()}.
|
||||
*/
|
||||
public static String toString(Object o) {
|
||||
return (o == null) ? "null" : o.toString();
|
||||
}
|
||||
|
||||
private static void dumpObject(Object o, PrintWriter writer, String prefix) {
|
||||
writer.write(prefix);writer.write(o.getClass().getName());writer.write('\n');
|
||||
}
|
||||
|
||||
private static void dumpBoolean(boolean z, PrintWriter writer, String prefix) {
|
||||
writer.write(prefix);writer.write(Boolean.toString(z));writer.write('\n');
|
||||
}
|
||||
|
||||
private static void dumpByte(byte b, PrintWriter writer, String prefix) {
|
||||
writer.write(prefix);writer.write(Byte.toString(b));writer.write('\n');
|
||||
}
|
||||
|
||||
private static void dumpChar(char c, PrintWriter writer, String prefix) {
|
||||
writer.write(prefix);writer.write(Character.toString(c));writer.write('\n');
|
||||
}
|
||||
|
||||
private static void dumpShort(short s, PrintWriter writer, String prefix) {
|
||||
writer.write(prefix);writer.write(Short.toString(s));writer.write('\n');
|
||||
}
|
||||
|
||||
private static void dumpInt(int i, PrintWriter writer, String prefix) {
|
||||
writer.write(prefix);writer.write(Integer.toString(i));writer.write('\n');
|
||||
}
|
||||
|
||||
private static void dumpLong(long j, PrintWriter writer, String prefix) {
|
||||
writer.write(prefix);writer.write(Long.toString(j));writer.write('\n');
|
||||
}
|
||||
|
||||
private static void dumpFloat(float f, PrintWriter writer, String prefix) {
|
||||
writer.write(prefix);writer.write(Float.toString(f));writer.write('\n');
|
||||
}
|
||||
|
||||
private static void dumpDouble(double d, PrintWriter writer, String prefix) {
|
||||
writer.write(prefix);writer.write(Double.toString(d));writer.write('\n');
|
||||
}
|
||||
|
||||
private static void dumpArray(Object array, PrintWriter writer, String prefix, boolean skipFirstPrefix) {
|
||||
if (skipFirstPrefix) {
|
||||
dumpObject(array, writer, "");
|
||||
} else {
|
||||
dumpObject(array, writer, prefix);
|
||||
}
|
||||
|
||||
String newPrefix = prefix + " ";
|
||||
writer.write(newPrefix);writer.write('[');writer.write('\n');
|
||||
|
||||
if (array instanceof Object[]) {
|
||||
Object[] a = (Object[]) array;
|
||||
for (int i = 0, l = a.length; i < l; i++) {
|
||||
dump(a[i], writer, newPrefix, false);
|
||||
}
|
||||
} else if (array instanceof boolean[]) {
|
||||
boolean[] a = (boolean[]) array;
|
||||
for (int i = 0, l = a.length; i < l; i++) {
|
||||
dumpBoolean(a[i], writer, newPrefix);
|
||||
}
|
||||
} else if (array instanceof byte[]) {
|
||||
byte[] a = (byte[]) array;
|
||||
for (int i = 0, l = a.length; i < l; i++) {
|
||||
dumpByte(a[i], writer, newPrefix);
|
||||
}
|
||||
} else if (array instanceof char[]) {
|
||||
char[] a = (char[]) array;
|
||||
for (int i = 0, l = a.length; i < l; i++) {
|
||||
dumpChar(a[i], writer, newPrefix);
|
||||
}
|
||||
} else if (array instanceof short[]) {
|
||||
short[] a = (short[]) array;
|
||||
for (int i = 0, l = a.length; i < l; i++) {
|
||||
dumpShort(a[i], writer, newPrefix);
|
||||
}
|
||||
} else if (array instanceof int[]) {
|
||||
int[] a = (int[]) array;
|
||||
for (int i = 0, l = a.length; i < l; i++) {
|
||||
dumpInt(a[i], writer, newPrefix);
|
||||
}
|
||||
} else if (array instanceof long[]) {
|
||||
long[] a = (long[]) array;
|
||||
for (int i = 0, l = a.length; i < l; i++) {
|
||||
dumpLong(a[i], writer, newPrefix);
|
||||
}
|
||||
} else if (array instanceof float[]) {
|
||||
float[] a = (float[]) array;
|
||||
for (int i = 0, l = a.length; i < l; i++) {
|
||||
dumpFloat(a[i], writer, newPrefix);
|
||||
}
|
||||
} else if (array instanceof double[]) {
|
||||
double[] a = (double[]) array;
|
||||
for (int i = 0, l = a.length; i < l; i++) {
|
||||
dumpDouble(a[i], writer, newPrefix);
|
||||
}
|
||||
} else if (array instanceof List) {
|
||||
for (Object o : (List) array) {
|
||||
dump(o, writer, newPrefix);
|
||||
}
|
||||
} else if (array instanceof Set) {
|
||||
for (Object o : (Set) array) {
|
||||
dump(o, writer, newPrefix);
|
||||
}
|
||||
} else if (array instanceof Map) {
|
||||
Map map = (Map) array;
|
||||
for (Object key : map.keySet()) {
|
||||
Object value = map.get(key);
|
||||
writer.write(newPrefix);writer.write(key.toString());writer.write(": ");dump(value, writer, newPrefix, true);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException(array + " is not array");
|
||||
}
|
||||
|
||||
writer.write(newPrefix);writer.write(']');writer.write('\n');
|
||||
}
|
||||
|
||||
public static void dump(Object o, PrintWriter writer) {
|
||||
dump(o, writer, "", false);
|
||||
}
|
||||
|
||||
public static void dump(Object o, PrintWriter writer, String prefix) {
|
||||
dump(o, writer, prefix, false);
|
||||
}
|
||||
|
||||
public static void dump(Object o, PrintWriter writer, String prefix, boolean skipFirstPrefix) {
|
||||
if (o == null) {
|
||||
if (!skipFirstPrefix) {
|
||||
writer.write(prefix);
|
||||
}
|
||||
writer.write("null\n");
|
||||
} else if (o.getClass().isArray() || o instanceof List || o instanceof Set || o instanceof Map) {
|
||||
dumpArray(o, writer, prefix, skipFirstPrefix);
|
||||
} else if (o.getClass().isPrimitive() || o instanceof Boolean ||
|
||||
o instanceof Byte || o instanceof Character || o instanceof Short ||
|
||||
o instanceof Integer || o instanceof Long || o instanceof Float ||
|
||||
o instanceof Double || o instanceof String) {
|
||||
if (!skipFirstPrefix) {
|
||||
writer.write(prefix);
|
||||
}
|
||||
writer.write(o.toString());
|
||||
writer.write('\n');
|
||||
} else {
|
||||
if (skipFirstPrefix) {
|
||||
dumpObject(o, writer, "");
|
||||
} else {
|
||||
dumpObject(o, writer, prefix);
|
||||
}
|
||||
|
||||
String newPrefix = prefix + " ";
|
||||
for (Field field : o.getClass().getDeclaredFields()) {
|
||||
// Skip static filed
|
||||
if (Modifier.isStatic(field.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
field.setAccessible(true);
|
||||
String name = field.getName();
|
||||
try {
|
||||
Object value = field.get(o);
|
||||
writer.write(newPrefix);writer.write(name);writer.write(": ");dump(value, writer, newPrefix, true);
|
||||
} catch (IllegalAccessException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.yorozuya;
|
||||
|
||||
public class Pool<T> {
|
||||
|
||||
private final T[] mArray;
|
||||
private final int mMaxSize;
|
||||
private int mSize;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Pool(int size) {
|
||||
if (size <= 0) {
|
||||
throw new IllegalStateException("Pool size must > 0, it is " + size);
|
||||
}
|
||||
mArray = (T[]) new Object[size];
|
||||
mMaxSize = size;
|
||||
mSize = 0;
|
||||
}
|
||||
|
||||
public void push(T t) {
|
||||
if (t != null && mSize < mMaxSize) {
|
||||
mArray[mSize++] = t;
|
||||
}
|
||||
}
|
||||
|
||||
public T pop() {
|
||||
if (mSize > 0) {
|
||||
T t = mArray[--mSize];
|
||||
mArray[mSize] = null;
|
||||
return t;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.yorozuya;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.util.TypedValue;
|
||||
|
||||
import androidx.annotation.AttrRes;
|
||||
|
||||
public final class ResourcesUtils {
|
||||
private ResourcesUtils() {}
|
||||
|
||||
private static final Object mAccessLock = new Object();
|
||||
private static TypedValue mTmpValue = new TypedValue();
|
||||
|
||||
public static float getFloat(Resources resources, int resId) {
|
||||
TypedValue outValue = new TypedValue();
|
||||
resources.getValue(resId, outValue, true);
|
||||
return outValue.getFloat();
|
||||
}
|
||||
|
||||
private static void getAttrValue(Context context, int attrId, TypedValue value) {
|
||||
context.getTheme().resolveAttribute(attrId, value, true);
|
||||
}
|
||||
|
||||
public static int getAttrColor(Context context, @AttrRes int attrId) {
|
||||
TypedValue value;
|
||||
synchronized (mAccessLock) {
|
||||
value = mTmpValue;
|
||||
if (value == null) {
|
||||
value = new TypedValue();
|
||||
}
|
||||
getAttrValue(context, attrId, value);
|
||||
if (value.type >= TypedValue.TYPE_FIRST_INT
|
||||
&& value.type <= TypedValue.TYPE_LAST_INT) {
|
||||
mTmpValue = value;
|
||||
return value.data;
|
||||
} else {
|
||||
throw new Resources.NotFoundException(
|
||||
"Attribute ID #0x" + Integer.toHexString(attrId) + " type #0x"
|
||||
+ Integer.toHexString(value.type) + " is not valid");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean getAttrBoolean(Context context, @AttrRes int attrId) {
|
||||
synchronized (mAccessLock) {
|
||||
TypedValue value = mTmpValue;
|
||||
if (value == null) {
|
||||
mTmpValue = value = new TypedValue();
|
||||
}
|
||||
getAttrValue(context, attrId, value);
|
||||
if (value.type >= TypedValue.TYPE_FIRST_INT
|
||||
&& value.type <= TypedValue.TYPE_LAST_INT) {
|
||||
return value.data != 0;
|
||||
}
|
||||
throw new Resources.NotFoundException(
|
||||
"Attribute ID #0x" + Integer.toHexString(attrId) + " type #0x"
|
||||
+ Integer.toHexString(value.type) + " is not valid");
|
||||
}
|
||||
}
|
||||
|
||||
public static int getAttrDimensionPixelOffset(Context context, @AttrRes int attrId) {
|
||||
synchronized (mAccessLock) {
|
||||
TypedValue value = mTmpValue;
|
||||
if (value == null) {
|
||||
mTmpValue = value = new TypedValue();
|
||||
}
|
||||
getAttrValue(context, attrId, value);
|
||||
if (value.type == TypedValue.TYPE_DIMENSION) {
|
||||
return TypedValue.complexToDimensionPixelOffset(
|
||||
value.data, context.getResources().getDisplayMetrics());
|
||||
}
|
||||
throw new Resources.NotFoundException(
|
||||
"Attribute ID #0x" + Integer.toHexString(attrId) + " type #0x"
|
||||
+ Integer.toHexString(value.type) + " is not valid");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.yorozuya;
|
||||
|
||||
import android.view.animation.Animation;
|
||||
|
||||
public class SimpleAnimationListener implements Animation.AnimationListener {
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.yorozuya;
|
||||
|
||||
import android.animation.Animator;
|
||||
|
||||
public abstract class SimpleAnimatorListener implements Animator.AnimatorListener {
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.yorozuya;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
public final class SimpleHandler extends Handler {
|
||||
|
||||
private static Handler sInstance;
|
||||
|
||||
public static Handler getInstance() {
|
||||
if (sInstance == null) {
|
||||
sInstance = new Handler(Looper.getMainLooper());
|
||||
}
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
private SimpleHandler(Looper mainLooper) {
|
||||
super(mainLooper);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,580 @@
|
||||
/*
|
||||
* Copyright 2015 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.yorozuya;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public final class StringUtils {
|
||||
private StringUtils() {}
|
||||
|
||||
public static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
|
||||
private static final String[] ESCAPE_CHARATER_LIST = {
|
||||
"&",
|
||||
"<",
|
||||
">",
|
||||
""",
|
||||
"'",
|
||||
"×",
|
||||
" "
|
||||
};
|
||||
|
||||
private static final String[] UNESCAPE_CHARATER_LIST = {
|
||||
"&",
|
||||
"<",
|
||||
">",
|
||||
"\"",
|
||||
"'",
|
||||
"×",
|
||||
" "
|
||||
};
|
||||
|
||||
/**
|
||||
* Unescape xml. It do not work perfectly.
|
||||
*/
|
||||
public static String unescapeXml(String str) {
|
||||
return replaceEach(str, ESCAPE_CHARATER_LIST, UNESCAPE_CHARATER_LIST);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Replaces all occurrences of a String within another String.</p>
|
||||
*
|
||||
* <p>A {@code null} reference passed to this method is a no-op.</p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.replace(null, *, *) = null
|
||||
* StringUtils.replace("", *, *) = ""
|
||||
* StringUtils.replace("any", null, *) = "any"
|
||||
* StringUtils.replace("any", *, null) = "any"
|
||||
* StringUtils.replace("any", "", *) = "any"
|
||||
* StringUtils.replace("aba", "a", null) = "aba"
|
||||
* StringUtils.replace("aba", "a", "") = "b"
|
||||
* StringUtils.replace("aba", "a", "z") = "zbz"
|
||||
* </pre>
|
||||
*
|
||||
* @see #replace(String text, String searchString, String replacement, int max)
|
||||
* @param text text to search and replace in, may be null
|
||||
* @param searchString the String to search for, may be null
|
||||
* @param replacement the String to replace it with, may be null
|
||||
* @return the text with any replacements processed,
|
||||
* {@code null} if null String input
|
||||
*/
|
||||
public static String replace(final String text, final String searchString, final String replacement) {
|
||||
return replace(text, searchString, replacement, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Replaces a String with another String inside a larger String,
|
||||
* for the first {@code max} values of the search String.</p>
|
||||
*
|
||||
* <p>A {@code null} reference passed to this method is a no-op.</p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.replace(null, *, *, *) = null
|
||||
* StringUtils.replace("", *, *, *) = ""
|
||||
* StringUtils.replace("any", null, *, *) = "any"
|
||||
* StringUtils.replace("any", *, null, *) = "any"
|
||||
* StringUtils.replace("any", "", *, *) = "any"
|
||||
* StringUtils.replace("any", *, *, 0) = "any"
|
||||
* StringUtils.replace("abaa", "a", null, -1) = "abaa"
|
||||
* StringUtils.replace("abaa", "a", "", -1) = "b"
|
||||
* StringUtils.replace("abaa", "a", "z", 0) = "abaa"
|
||||
* StringUtils.replace("abaa", "a", "z", 1) = "zbaa"
|
||||
* StringUtils.replace("abaa", "a", "z", 2) = "zbza"
|
||||
* StringUtils.replace("abaa", "a", "z", -1) = "zbzz"
|
||||
* </pre>
|
||||
*
|
||||
* @param text text to search and replace in, may be null
|
||||
* @param searchString the String to search for, may be null
|
||||
* @param replacement the String to replace it with, may be null
|
||||
* @param max maximum number of values to replace, or {@code -1} if no maximum
|
||||
* @return the text with any replacements processed,
|
||||
* {@code null} if null String input
|
||||
*/
|
||||
public static String replace(final String text, final String searchString, final String replacement, int max) {
|
||||
if (TextUtils.isEmpty(text) || TextUtils.isEmpty(searchString) || replacement == null || max == 0) {
|
||||
return text;
|
||||
}
|
||||
int start = 0;
|
||||
int end = text.indexOf(searchString, start);
|
||||
if (end < 0) {
|
||||
return text;
|
||||
}
|
||||
final int replLength = searchString.length();
|
||||
int increase = replacement.length() - replLength;
|
||||
increase = increase < 0 ? 0 : increase;
|
||||
increase *= max < 0 ? 16 : max > 64 ? 64 : max;
|
||||
final StringBuilder buf = new StringBuilder(text.length() + increase);
|
||||
while (end >= 0) {
|
||||
buf.append(text.substring(start, end)).append(replacement);
|
||||
start = end + replLength;
|
||||
if (--max == 0) {
|
||||
break;
|
||||
}
|
||||
end = text.indexOf(searchString, start);
|
||||
}
|
||||
buf.append(text.substring(start));
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Replaces all occurrences of Strings within another String.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* A {@code null} reference passed to this method is a no-op, or if
|
||||
* any "search string" or "string to replace" is null, that replace will be
|
||||
* ignored. This will not repeat. For repeating replaces, call the
|
||||
* overloaded method.
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.replaceEach(null, *, *) = null
|
||||
* StringUtils.replaceEach("", *, *) = ""
|
||||
* StringUtils.replaceEach("aba", null, null) = "aba"
|
||||
* StringUtils.replaceEach("aba", new String[0], null) = "aba"
|
||||
* StringUtils.replaceEach("aba", null, new String[0]) = "aba"
|
||||
* StringUtils.replaceEach("aba", new String[]{"a"}, null) = "aba"
|
||||
* StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""}) = "b"
|
||||
* StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"}) = "aba"
|
||||
* StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}) = "wcte"
|
||||
* (example of how it does not repeat)
|
||||
* StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}) = "dcte"
|
||||
* </pre>
|
||||
*
|
||||
* @param text
|
||||
* text to search and replace in, no-op if null
|
||||
* @param searchList
|
||||
* the Strings to search for, no-op if null
|
||||
* @param replacementList
|
||||
* the Strings to replace them with, no-op if null
|
||||
* @return the text with any replacements processed, {@code null} if
|
||||
* null String input
|
||||
* @throws IllegalArgumentException
|
||||
* if the lengths of the arrays are not the same (null is ok,
|
||||
* and/or size 0)
|
||||
*/
|
||||
// Get from org.apache.commons.lang3.StringUtils
|
||||
public static String replaceEach(final String text, final String[] searchList,
|
||||
final String[] replacementList) {
|
||||
return replaceEach(text, searchList, replacementList, false, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Replaces all occurrences of Strings within another String.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* A {@code null} reference passed to this method is a no-op, or if
|
||||
* any "search string" or "string to replace" is null, that replace will be
|
||||
* ignored.
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.replaceEach(null, *, *, *) = null
|
||||
* StringUtils.replaceEach("", *, *, *) = ""
|
||||
* StringUtils.replaceEach("aba", null, null, *) = "aba"
|
||||
* StringUtils.replaceEach("aba", new String[0], null, *) = "aba"
|
||||
* StringUtils.replaceEach("aba", null, new String[0], *) = "aba"
|
||||
* StringUtils.replaceEach("aba", new String[]{"a"}, null, *) = "aba"
|
||||
* StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""}, *) = "b"
|
||||
* StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"}, *) = "aba"
|
||||
* StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}, *) = "wcte"
|
||||
* (example of how it repeats)
|
||||
* StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, false) = "dcte"
|
||||
* StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, true) = "tcte"
|
||||
* StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, *) = IllegalStateException
|
||||
* </pre>
|
||||
*
|
||||
* @param text
|
||||
* text to search and replace in, no-op if null
|
||||
* @param searchList
|
||||
* the Strings to search for, no-op if null
|
||||
* @param replacementList
|
||||
* the Strings to replace them with, no-op if null
|
||||
* @param repeat if true, then replace repeatedly
|
||||
* until there are no more possible replacements or timeToLive < 0
|
||||
* @param timeToLive
|
||||
* if less than 0 then there is a circular reference and endless
|
||||
* loop
|
||||
* @return the text with any replacements processed, {@code null} if
|
||||
* null String input
|
||||
* @throws IllegalStateException
|
||||
* if the search is repeating and there is an endless loop due
|
||||
* to outputs of one being inputs to another
|
||||
* @throws IllegalArgumentException
|
||||
* if the lengths of the arrays are not the same (null is ok,
|
||||
* and/or size 0)
|
||||
*/
|
||||
// Get from org.apache.commons.lang3.StringUtils
|
||||
private static String replaceEach(
|
||||
final String text, final String[] searchList,
|
||||
final String[] replacementList, final boolean repeat,
|
||||
final int timeToLive) {
|
||||
|
||||
// mchyzer Performance note: This creates very few new objects (one major goal)
|
||||
// let me know if there are performance requests, we can create a harness to measure
|
||||
|
||||
if (text == null || TextUtils.isEmpty(text) || searchList == null ||
|
||||
searchList.length == 0 || replacementList == null || replacementList.length == 0) {
|
||||
return text;
|
||||
}
|
||||
|
||||
// if recursing, this shouldn't be less than 0
|
||||
if (timeToLive < 0) {
|
||||
throw new IllegalStateException("Aborting to protect against StackOverflowError - " +
|
||||
"output of one loop is the input of another");
|
||||
}
|
||||
|
||||
final int searchLength = searchList.length;
|
||||
final int replacementLength = replacementList.length;
|
||||
|
||||
// make sure lengths are ok, these need to be equal
|
||||
if (searchLength != replacementLength) {
|
||||
throw new IllegalArgumentException("Search and Replace array lengths don't match: "
|
||||
+ searchLength
|
||||
+ " vs "
|
||||
+ replacementLength);
|
||||
}
|
||||
|
||||
// keep track of which still have matches
|
||||
final boolean[] noMoreMatchesForReplIndex = new boolean[searchLength];
|
||||
|
||||
// index on index that the match was found
|
||||
int textIndex = -1;
|
||||
int replaceIndex = -1;
|
||||
int tempIndex = -1;
|
||||
|
||||
// index of replace array that will replace the search string found
|
||||
// NOTE: logic duplicated below START
|
||||
for (int i = 0; i < searchLength; i++) {
|
||||
if (noMoreMatchesForReplIndex[i] || searchList[i] == null ||
|
||||
TextUtils.isEmpty(searchList[i]) || replacementList[i] == null) {
|
||||
continue;
|
||||
}
|
||||
tempIndex = text.indexOf(searchList[i]);
|
||||
|
||||
// see if we need to keep searching for this
|
||||
if (tempIndex == -1) {
|
||||
noMoreMatchesForReplIndex[i] = true;
|
||||
} else {
|
||||
if (textIndex == -1 || tempIndex < textIndex) {
|
||||
textIndex = tempIndex;
|
||||
replaceIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
// NOTE: logic mostly below END
|
||||
|
||||
// no search strings found, we are done
|
||||
if (textIndex == -1) {
|
||||
return text;
|
||||
}
|
||||
|
||||
int start = 0;
|
||||
|
||||
// get a good guess on the size of the result buffer so it doesn't have to double if it goes over a bit
|
||||
int increase = 0;
|
||||
|
||||
// count the replacement text elements that are larger than their corresponding text being replaced
|
||||
for (int i = 0; i < searchList.length; i++) {
|
||||
if (searchList[i] == null || replacementList[i] == null) {
|
||||
continue;
|
||||
}
|
||||
final int greater = replacementList[i].length() - searchList[i].length();
|
||||
if (greater > 0) {
|
||||
increase += 3 * greater; // assume 3 matches
|
||||
}
|
||||
}
|
||||
// have upper-bound at 20% increase, then let Java take over
|
||||
increase = Math.min(increase, text.length() / 5);
|
||||
|
||||
final StringBuilder buf = new StringBuilder(text.length() + increase);
|
||||
|
||||
while (textIndex != -1) {
|
||||
|
||||
for (int i = start; i < textIndex; i++) {
|
||||
buf.append(text.charAt(i));
|
||||
}
|
||||
buf.append(replacementList[replaceIndex]);
|
||||
|
||||
start = textIndex + searchList[replaceIndex].length();
|
||||
|
||||
textIndex = -1;
|
||||
replaceIndex = -1;
|
||||
tempIndex = -1;
|
||||
// find the next earliest match
|
||||
// NOTE: logic mostly duplicated above START
|
||||
for (int i = 0; i < searchLength; i++) {
|
||||
if (noMoreMatchesForReplIndex[i] || searchList[i] == null ||
|
||||
TextUtils.isEmpty(searchList[i]) || replacementList[i] == null) {
|
||||
continue;
|
||||
}
|
||||
tempIndex = text.indexOf(searchList[i], start);
|
||||
|
||||
// see if we need to keep searching for this
|
||||
if (tempIndex == -1) {
|
||||
noMoreMatchesForReplIndex[i] = true;
|
||||
} else {
|
||||
if (textIndex == -1 || tempIndex < textIndex) {
|
||||
textIndex = tempIndex;
|
||||
replaceIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
// NOTE: logic duplicated above END
|
||||
|
||||
}
|
||||
final int textLength = text.length();
|
||||
for (int i = start; i < textLength; i++) {
|
||||
buf.append(text.charAt(i));
|
||||
}
|
||||
final String result = buf.toString();
|
||||
if (!repeat) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return replaceEach(result, searchList, replacementList, repeat, timeToLive - 1);
|
||||
}
|
||||
|
||||
public static boolean endsWith(String string, String[] suffixs) {
|
||||
for (int i = 0, n = suffixs.length; i < n; i++) {
|
||||
if (string.endsWith(suffixs[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Splits the provided text into an array, separator specified.
|
||||
* This is an alternative to using StringTokenizer.</p>
|
||||
*
|
||||
* <p>The separator is not included in the returned String array.
|
||||
* Adjacent separators are treated as one separator.
|
||||
* For more control over the split use the StrTokenizer class.</p>
|
||||
*
|
||||
* <p>A {@code null} input String returns {@code null}.</p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.split(null, *) = null
|
||||
* StringUtils.split("", *) = []
|
||||
* StringUtils.split("a.b.c", '.') = ["a", "b", "c"]
|
||||
* StringUtils.split("a..b.c", '.') = ["a", "b", "c"]
|
||||
* StringUtils.split("a:b:c", '.') = ["a:b:c"]
|
||||
* StringUtils.split("a b c", ' ') = ["a", "b", "c"]
|
||||
* </pre>
|
||||
*
|
||||
* @param str the String to parse, may be null
|
||||
* @param separatorChar the character used as the delimiter
|
||||
* @return an array of parsed Strings, {@code null} if null String input
|
||||
* @since 2.0
|
||||
*/
|
||||
// Get from org.apache.commons.lang3.StringUtils
|
||||
public static String[] split(final String str, final char separatorChar) {
|
||||
return splitWorker(str, separatorChar, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the logic for the {@code split} and
|
||||
* {@code splitPreserveAllTokens} methods that do not return a
|
||||
* maximum array length.
|
||||
*
|
||||
* @param str the String to parse, may be {@code null}
|
||||
* @param separatorChar the separate character
|
||||
* @param preserveAllTokens if {@code true}, adjacent separators are
|
||||
* treated as empty token separators; if {@code false}, adjacent
|
||||
* separators are treated as one separator.
|
||||
* @return an array of parsed Strings, {@code null} if null String input
|
||||
*/
|
||||
// Get from org.apache.commons.lang3.StringUtils
|
||||
private static String[] splitWorker(final String str, final char separatorChar, final boolean preserveAllTokens) {
|
||||
// Performance tuned for 2.0 (JDK1.4)
|
||||
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
final int len = str.length();
|
||||
if (len == 0) {
|
||||
return EMPTY_STRING_ARRAY;
|
||||
}
|
||||
final List<String> list = new ArrayList<>();
|
||||
int i = 0, start = 0;
|
||||
boolean match = false;
|
||||
boolean lastMatch = false;
|
||||
while (i < len) {
|
||||
if (str.charAt(i) == separatorChar) {
|
||||
if (match || preserveAllTokens) {
|
||||
list.add(str.substring(start, i));
|
||||
match = false;
|
||||
lastMatch = true;
|
||||
}
|
||||
start = ++i;
|
||||
continue;
|
||||
}
|
||||
lastMatch = false;
|
||||
match = true;
|
||||
i++;
|
||||
}
|
||||
if (match || preserveAllTokens && lastMatch) {
|
||||
list.add(str.substring(start, i));
|
||||
}
|
||||
return list.toArray(new String[list.size()]);
|
||||
}
|
||||
|
||||
public static String avoidNull(String value) {
|
||||
return avoidNull(value, "");
|
||||
}
|
||||
|
||||
public static String avoidNull(String value, String defaultValue) {
|
||||
return value == null ? defaultValue : value;
|
||||
}
|
||||
|
||||
public static boolean isAllDigit(String str) {
|
||||
if (TextUtils.isEmpty(str)) {
|
||||
return false;
|
||||
} else {
|
||||
for (int i = 0, n = str.length(); i < n; i++) {
|
||||
char ch = str.charAt(i);
|
||||
if (ch < '0' || ch > '9') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static int length(String str) {
|
||||
return null == str ? 0 : str.length();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Counts how many times the char appears in the given string.</p>
|
||||
*
|
||||
* <p>A {@code null} or empty ("") String input returns {@code 0}.</p>
|
||||
*
|
||||
* <pre>
|
||||
* StringUtils.countMatches(null, *) = 0
|
||||
* StringUtils.countMatches("", *) = 0
|
||||
* StringUtils.countMatches("abba", 0) = 0
|
||||
* StringUtils.countMatches("abba", 'a') = 2
|
||||
* StringUtils.countMatches("abba", 'b') = 2
|
||||
* StringUtils.countMatches("abba", 'x') = 0
|
||||
* </pre>
|
||||
*
|
||||
* @param str the CharSequence to check, may be null
|
||||
* @param ch the char to count
|
||||
* @return the number of occurrences, 0 if the CharSequence is {@code null}
|
||||
*/
|
||||
public static int countMatches(final CharSequence str, final char ch) {
|
||||
if (TextUtils.isEmpty(str)) {
|
||||
return 0;
|
||||
}
|
||||
int count = 0;
|
||||
// We could also call str.toCharArray() for faster look ups but that would generate more garbage.
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
if (ch == str.charAt(i)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* All null or empty, or all not
|
||||
*/
|
||||
public static boolean equals(String str1, String str2) {
|
||||
return (TextUtils.isEmpty(str1) && TextUtils.isEmpty(str2)) ||
|
||||
(!TextUtils.isEmpty(str1) && !TextUtils.isEmpty(str2) && str1.equals(str2));
|
||||
}
|
||||
|
||||
public static int ordinalIndexOf(String str, char c, int n) {
|
||||
if (null == str || n < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int pos = -1;
|
||||
do {
|
||||
pos = str.indexOf(c, pos + 1);
|
||||
} while (n-- > 0 && pos != -1);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
public static final char[] WHITE_SPACE_ARRAY = {
|
||||
'\u0009', // TAB
|
||||
'\u0020', // SPACE
|
||||
'\u00A0', // NO-BREAK SPACE
|
||||
'\u3000', // IDEOGRAPHIC SPACE
|
||||
};
|
||||
|
||||
/**
|
||||
* Works like {@link String#trim()}, but more white space is excluded.
|
||||
* The white space characters is {@link #WHITE_SPACE_ARRAY}.
|
||||
*
|
||||
* @see {@link #trim(String, char[])}
|
||||
*/
|
||||
public static String trim(String str) {
|
||||
return trim(str, WHITE_SPACE_ARRAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Works like {@link String#trim()}, but custom characters is excluded.
|
||||
*
|
||||
* @see {@link #trim(String)}
|
||||
*/
|
||||
public static String trim(String str, char[] excluded) {
|
||||
if (null == str) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int start = 0, last = str.length() - 1;
|
||||
int end = last;
|
||||
while ((start <= end) && (Arrays.binarySearch(excluded, str.charAt(start)) >= 0)) {
|
||||
start++;
|
||||
}
|
||||
while ((end >= start) && (Arrays.binarySearch(excluded, str.charAt(end)) >= 0)) {
|
||||
end--;
|
||||
}
|
||||
if (start == 0 && end == last) {
|
||||
return str;
|
||||
}
|
||||
|
||||
return str.substring(start, end + 1);
|
||||
}
|
||||
|
||||
public static String remove(String str, char[] removed) {
|
||||
if (TextUtils.isEmpty(str) || null == removed || 0 == removed.length) {
|
||||
return str;
|
||||
}
|
||||
|
||||
final char[] chars = str.toCharArray();
|
||||
int pos = 0;
|
||||
for (int i = 0; i < chars.length; i++) {
|
||||
if (!Utilities.contain(removed, chars[i])) {
|
||||
chars[pos++] = chars[i];
|
||||
}
|
||||
}
|
||||
return new String(chars, 0, pos);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2015 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.yorozuya;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public final class Utilities {
|
||||
|
||||
/**
|
||||
* Whether the array contain the element
|
||||
*
|
||||
* @param array the array
|
||||
* @param obj the element
|
||||
* @return true for the array contain the element
|
||||
*/
|
||||
public static boolean contain(@Nullable Object[] array, @Nullable Object obj) {
|
||||
if (null == array) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (Object o : array) {
|
||||
if (ObjectUtils.equal(o, obj)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the array contain the element
|
||||
*
|
||||
* @param array the array
|
||||
* @param ch the element
|
||||
* @return true for the array contain the element
|
||||
*/
|
||||
public static boolean contain(@Nullable char[] array, char ch) {
|
||||
if (null == array) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (char c: array) {
|
||||
if (c == ch) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String getNameFromUrl(String url) {
|
||||
if (!TextUtils.isEmpty(url)) {
|
||||
int fragment = url.lastIndexOf('#');
|
||||
if (fragment > 0) {
|
||||
url = url.substring(0, fragment);
|
||||
}
|
||||
|
||||
int query = url.lastIndexOf('?');
|
||||
if (query > 0) {
|
||||
url = url.substring(0, query);
|
||||
}
|
||||
|
||||
int filenamePos = url.lastIndexOf('/');
|
||||
String filename =
|
||||
0 <= filenamePos ? url.substring(filenamePos + 1) : url;
|
||||
|
||||
// if the filename contains special characters, we don't
|
||||
// consider it valid for our matching purposes:
|
||||
if (filename.length() != 0 &&
|
||||
Pattern.matches("[a-zA-Z_0-9\\.\\-\\(\\)%]+", filename)) {
|
||||
int dotPos = filename.lastIndexOf('.');
|
||||
if (0 < dotPos) {
|
||||
return filename.substring(0, dotPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static final String[] MIME_TYPE_ARRAY = {
|
||||
"image/jpg"
|
||||
};
|
||||
|
||||
private static final String[] EXTENSION_ARRAY = {
|
||||
"jpg"
|
||||
};
|
||||
|
||||
public static String getExtensionFromMimeType(String mimeType) {
|
||||
if (TextUtils.isEmpty(mimeType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 0, n = MIME_TYPE_ARRAY.length; i < n; i++) {
|
||||
if (MIME_TYPE_ARRAY[i].equals(mimeType)) {
|
||||
return EXTENSION_ARRAY[i];
|
||||
}
|
||||
}
|
||||
|
||||
return MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,515 @@
|
||||
/*
|
||||
* Copyright 2015 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.yorozuya;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
import android.view.ViewTreeObserver;
|
||||
|
||||
import androidx.annotation.IdRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public final class ViewUtils {
|
||||
private ViewUtils() {}
|
||||
|
||||
public static final int MAX_SIZE = Integer.MAX_VALUE & ~(0x3 << 30);
|
||||
|
||||
private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
|
||||
|
||||
/**
|
||||
* Get view center location in window
|
||||
*
|
||||
* @param view the view to check
|
||||
* @param location an array of two integers in which to hold the coordinates
|
||||
*/
|
||||
public static void getCenterInWindows(View view, int[] location) {
|
||||
getLocationInWindow(view, location);
|
||||
location[0] += view.getWidth() / 2;
|
||||
location[1] += view.getHeight() / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get view location in window
|
||||
*
|
||||
* @param view the view to check
|
||||
* @param location an array of two integers in which to hold the coordinates
|
||||
*/
|
||||
public static void getLocationInWindow(View view, int[] location) {
|
||||
getLocationInAncestor(view, location, android.R.id.content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get view center in ths ancestor
|
||||
*
|
||||
* @param view the view to start with
|
||||
* @param location the container of result
|
||||
* @param ancestorId the ancestor id
|
||||
*/
|
||||
public static void getCenterInAncestor(View view, int[] location, int ancestorId) {
|
||||
getLocationInAncestor(view, location, ancestorId);
|
||||
location[0] += view.getWidth() / 2;
|
||||
location[1] += view.getHeight() / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get view location in ths ancestor
|
||||
*
|
||||
* @param view the view to start with
|
||||
* @param location the container of result
|
||||
* @param ancestorId the ancestor id
|
||||
*/
|
||||
public static void getLocationInAncestor(View view, int[] location, int ancestorId) {
|
||||
if (location == null || location.length < 2) {
|
||||
throw new IllegalArgumentException(
|
||||
"location must be an array of two integers");
|
||||
}
|
||||
|
||||
float[] position = new float[2];
|
||||
|
||||
position[0] = view.getLeft();
|
||||
position[1] = view.getTop();
|
||||
|
||||
ViewParent viewParent = view.getParent();
|
||||
while (viewParent instanceof View) {
|
||||
view = (View) viewParent;
|
||||
if (view.getId() == ancestorId) {
|
||||
break;
|
||||
}
|
||||
|
||||
position[0] -= view.getScrollX();
|
||||
position[1] -= view.getScrollY();
|
||||
|
||||
position[0] += view.getLeft();
|
||||
position[1] += view.getTop();
|
||||
|
||||
viewParent = view.getParent();
|
||||
}
|
||||
|
||||
location[0] = (int) (position[0] + 0.5f);
|
||||
location[1] = (int) (position[1] + 0.5f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get view center in ths ancestor
|
||||
*
|
||||
* @param view the view to start with
|
||||
* @param location the container of result
|
||||
* @param ancestor the ancestor
|
||||
*/
|
||||
public static void getCenterInAncestor(View view, int[] location, View ancestor) {
|
||||
getLocationInAncestor(view, location, ancestor);
|
||||
location[0] += view.getWidth() / 2;
|
||||
location[1] += view.getHeight() / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get view location in ths ancestor
|
||||
*
|
||||
* @param view the view to start with
|
||||
* @param location the container of result
|
||||
* @param ancestor the ancestor
|
||||
*/
|
||||
public static void getLocationInAncestor(View view, int[] location, View ancestor) {
|
||||
if (location == null || location.length < 2) {
|
||||
throw new IllegalArgumentException(
|
||||
"location must be an array of two integers");
|
||||
}
|
||||
|
||||
float[] position = new float[2];
|
||||
|
||||
position[0] = view.getLeft();
|
||||
position[1] = view.getTop();
|
||||
|
||||
ViewParent viewParent = view.getParent();
|
||||
while (viewParent instanceof View) {
|
||||
view = (View) viewParent;
|
||||
if (viewParent == ancestor) {
|
||||
break;
|
||||
}
|
||||
|
||||
position[0] -= view.getScrollX();
|
||||
position[1] -= view.getScrollY();
|
||||
|
||||
position[0] += view.getLeft();
|
||||
position[1] += view.getTop();
|
||||
|
||||
viewParent = view.getParent();
|
||||
}
|
||||
|
||||
location[0] = (int) (position[0] + 0.5f);
|
||||
location[1] = (int) (position[1] + 0.5f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for a ancestor view with the given id. If this view has the given
|
||||
* id, return this view.
|
||||
*
|
||||
* @param view the view to start with
|
||||
* @param id The id to search for.
|
||||
* @return The view that has the given id in the hierarchy or null
|
||||
*/
|
||||
public static View getAncestor(View view, int id) {
|
||||
if (view.getId() == id) {
|
||||
return view;
|
||||
}
|
||||
|
||||
ViewParent viewParent = view.getParent();
|
||||
while (viewParent instanceof View) {
|
||||
view = (View) viewParent;
|
||||
if (view.getId() == id) {
|
||||
return view;
|
||||
}
|
||||
viewParent = view.getParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for a child view with the given id. If this view has the given
|
||||
* id, return this view.
|
||||
*
|
||||
* @param view the view to start with
|
||||
* @param id the id to search for
|
||||
* @return the view that has the given id in the hierarchy or null
|
||||
*/
|
||||
public static View getChild(View view, int id) {
|
||||
if (view.getId() == id) {
|
||||
return view;
|
||||
}
|
||||
|
||||
if (view instanceof ViewGroup) {
|
||||
ViewGroup viewGroup = (ViewGroup) view;
|
||||
for (int i = 0, n = viewGroup.getChildCount(); i < n; i++) {
|
||||
View child = viewGroup.getChildAt(i);
|
||||
View result = getChild(child, id);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a bitmap showing a screenshot of the view passed in.
|
||||
*
|
||||
* @param v The view to get screenshot
|
||||
* @return The screenshot
|
||||
*/
|
||||
public static Bitmap getBitmapFromView(View v) {
|
||||
int width = v.getWidth();
|
||||
int height = v.getHeight();
|
||||
if (width == 0 && height == 0) {
|
||||
width = v.getMeasuredWidth();
|
||||
height = v.getMeasuredHeight();
|
||||
}
|
||||
Bitmap bitmap = Bitmap.createBitmap(width, height,
|
||||
Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
canvas.translate(-v.getScrollX(), -v.getScrollY());
|
||||
v.draw(canvas);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove view from its parent
|
||||
*
|
||||
* @param view the view to remove
|
||||
*/
|
||||
public static void removeFromParent(View view) {
|
||||
ViewParent vp = view.getParent();
|
||||
if (vp instanceof ViewGroup)
|
||||
((ViewGroup) vp).removeView(view);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that removes the support for HardwareAcceleration from a
|
||||
* {@link View}.<br/>
|
||||
* <br/>
|
||||
* Check AOSP notice:<br/>
|
||||
*
|
||||
* <pre>
|
||||
* 'ComposeShader can only contain shaders of different types (a BitmapShader and a
|
||||
* LinearGradient for instance, but not two instances of BitmapShader)'. But, 'If your
|
||||
* application is affected by any of these missing features or limitations, you can turn
|
||||
* off hardware acceleration for just the affected portion of your application by calling
|
||||
* setLayerType(View.LAYER_TYPE_SOFTWARE, null).'
|
||||
* </pre>
|
||||
*
|
||||
* @param v
|
||||
* The view
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public static void removeHardwareAccelerationSupport(View v) {
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB) {
|
||||
if (v.getLayerType() != View.LAYER_TYPE_SOFTWARE) {
|
||||
v.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public static void addHardwareAccelerationSupport(View v) {
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB) {
|
||||
if (v.getLayerType() != View.LAYER_TYPE_HARDWARE) {
|
||||
v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void measureView(View v, int width, int height) {
|
||||
int widthMeasureSpec;
|
||||
int heightMeasureSpec;
|
||||
if (width == ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(0,
|
||||
View.MeasureSpec.UNSPECIFIED);
|
||||
else
|
||||
widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(Math.max(width, 0),
|
||||
View.MeasureSpec.EXACTLY);
|
||||
if (height == ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0,
|
||||
View.MeasureSpec.UNSPECIFIED);
|
||||
else
|
||||
heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(Math.max(height, 0),
|
||||
View.MeasureSpec.EXACTLY);
|
||||
|
||||
v.measure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the supplied view is under the given point in the
|
||||
* parent view's coordinate system.
|
||||
*
|
||||
* @param view Child view of the parent to hit test
|
||||
* @param x X position to test in the parent's coordinate system
|
||||
* @param y Y position to test in the parent's coordinate system
|
||||
* @param slop the slop out of the view, or negative for inside
|
||||
* @return true if the supplied view is under the given point, false otherwise
|
||||
*/
|
||||
public static boolean isViewUnder(@Nullable View view, int x, int y, int slop) {
|
||||
if (view == null) {
|
||||
return false;
|
||||
} else {
|
||||
float translationX = 0.0f;
|
||||
float translationY = 0.0f;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
|
||||
translationX = view.getTranslationX();
|
||||
translationY = view.getTranslationY();
|
||||
}
|
||||
return x >= view.getLeft() + translationX - slop &&
|
||||
x < view.getRight() + translationX + slop &&
|
||||
y >= view.getTop() + translationY - slop &&
|
||||
y < view.getBottom() + translationY + slop;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility to return a default size. Uses the supplied size if the
|
||||
* MeasureSpec imposed no constraints. Will get suitable if allowed
|
||||
* by the MeasureSpec.
|
||||
*
|
||||
* @param size Default size for this view
|
||||
* @param measureSpec Constraints imposed by the parent
|
||||
* @return The size this view should be.
|
||||
*/
|
||||
public static int getSuitableSize(int size, int measureSpec) {
|
||||
int result = size;
|
||||
int specMode = View.MeasureSpec.getMode(measureSpec);
|
||||
int specSize = View.MeasureSpec.getSize(measureSpec);
|
||||
|
||||
switch (specMode) {
|
||||
case View.MeasureSpec.UNSPECIFIED:
|
||||
result = size;
|
||||
break;
|
||||
case View.MeasureSpec.EXACTLY:
|
||||
result = specSize;
|
||||
break;
|
||||
case View.MeasureSpec.AT_MOST:
|
||||
return size == 0 ? specSize : Math.min(size, specSize);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* removeOnGlobalLayoutListener
|
||||
*
|
||||
* @param viewTreeObserver the ViewTreeObserver
|
||||
* @param l the OnGlobalLayoutListener
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public static void removeOnGlobalLayoutListener(ViewTreeObserver viewTreeObserver,
|
||||
ViewTreeObserver.OnGlobalLayoutListener l) {
|
||||
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
|
||||
viewTreeObserver.removeGlobalOnLayoutListener(l);
|
||||
} else {
|
||||
viewTreeObserver.removeOnGlobalLayoutListener(l);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get index in parent
|
||||
*
|
||||
* @param view The view
|
||||
* @return The index
|
||||
*/
|
||||
public static int getIndexInParent(View view) {
|
||||
ViewParent parent = view.getParent();
|
||||
if (parent instanceof ViewGroup) {
|
||||
ViewGroup viewParent = (ViewGroup) parent;
|
||||
int count = viewParent.getChildCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
View v = viewParent.getChildAt(i);
|
||||
if (v == view) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform point from parent to child
|
||||
*
|
||||
* @param point the point
|
||||
* @param parent the parent
|
||||
* @param child the child
|
||||
*/
|
||||
public static void transformPointToViewLocal(float[] point, View parent, View child) {
|
||||
point[0] += parent.getScrollX() - child.getLeft();
|
||||
point[1] += parent.getScrollY() - child.getTop();
|
||||
}
|
||||
|
||||
public static void setEnabledRecursively(View view, boolean enabled) {
|
||||
if (view instanceof ViewGroup) {
|
||||
ViewGroup viewGroup = (ViewGroup) view;
|
||||
for (int i = 0, n = viewGroup.getChildCount(); i < n; i++) {
|
||||
setEnabledRecursively(viewGroup.getChildAt(i), enabled);
|
||||
}
|
||||
}
|
||||
view.setEnabled(enabled);
|
||||
}
|
||||
|
||||
public static void dumpViewHierarchy(View view, PrintWriter writer) {
|
||||
dumpViewHierarchy(view, writer, "");
|
||||
}
|
||||
|
||||
private static void dumpViewHierarchy(View view, PrintWriter writer, String prefix) {
|
||||
writer.write(prefix);writer.write(view.getClass().getName());writer.write('\n');
|
||||
if (view instanceof ViewGroup) {
|
||||
String newPrefix = prefix + " ";
|
||||
ViewGroup viewGroup = (ViewGroup) view;
|
||||
for (int i = 0, count = viewGroup.getChildCount(); i < count; i++) {
|
||||
View child = viewGroup.getChildAt(i);
|
||||
dumpViewHierarchy(child, writer, newPrefix);
|
||||
}
|
||||
}
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a value suitable for use in {@link View#setId(int)}.
|
||||
* This value will not collide with ID values generated at build time by aapt for R.id.
|
||||
*
|
||||
* @return a generated ID value
|
||||
*/
|
||||
public static int generateViewId() {
|
||||
for (;;) {
|
||||
final int result = sNextGeneratedId.get();
|
||||
// aapt-generated IDs have the high byte nonzero; clamp to the range under that.
|
||||
int newValue = result + 1;
|
||||
if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
|
||||
if (sNextGeneratedId.compareAndSet(result, newValue)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset this view translationX
|
||||
*/
|
||||
public static void translationXBy(View view, float offset) {
|
||||
view.setTranslationX(view.getTranslationX() + offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset this view translationY
|
||||
*/
|
||||
public static void translationYBy(View view, float offset) {
|
||||
view.setTranslationY(view.getTranslationY() + offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* The visual right position of this view, in pixels. This is equivalent to the
|
||||
* {@link View#setTranslationX(float) translationX} property plus the current
|
||||
* {@link View#getRight() right} property.
|
||||
*
|
||||
* @return The visual right position of this view, in pixels.
|
||||
*/
|
||||
public static float getX2(View view) {
|
||||
return view.getRight() + view.getTranslationX();
|
||||
}
|
||||
|
||||
/**
|
||||
* The visual bottom position of this view, in pixels. This is equivalent to the
|
||||
* {@link View#setTranslationY(float) translationY} property plus the current
|
||||
* {@link View#getBottom()} () bottom} property.
|
||||
*
|
||||
* @return The visual bottom position of this view, in pixels.
|
||||
*/
|
||||
public static float getY2(View view) {
|
||||
return view.getBottom() + view.getTranslationY();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static View $$(Activity activity, @IdRes int id) {
|
||||
View result = activity.findViewById(id);
|
||||
if (null == result) {
|
||||
throw new NullPointerException("Can't find view with id: " + id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static View $$(Dialog dialog, @IdRes int id) {
|
||||
View result = dialog.findViewById(id);
|
||||
if (null == result) {
|
||||
throw new NullPointerException("Can't find view with id: " + id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static View $$(View view, @IdRes int id) {
|
||||
View result = view.findViewById(id);
|
||||
if (null == result) {
|
||||
throw new NullPointerException("Can't find view with id: " + id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -6,10 +6,10 @@
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:fillAlpha="1"
|
||||
android:fillColor="@color/shortcut_bg"
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M46,24A22,22 0,0 1,24 46,22 22,0 0,1 2,24 22,22 0,0 1,24 2,22 22,0 0,1 46,24Z" />
|
||||
<path
|
||||
android:fillAlpha="1"
|
||||
android:fillColor="@color/colorPrimary"
|
||||
android:fillColor="@android:color/black"
|
||||
android:pathData="M20,17V31L31,24Z" />
|
||||
</vector>
|
||||
@@ -6,10 +6,10 @@
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:fillAlpha="1"
|
||||
android:fillColor="@color/shortcut_bg"
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M46,24A22,22 0,0 1,24 46,22 22,0 0,1 2,24 22,22 0,0 1,24 2,22 22,0 0,1 46,24Z" />
|
||||
<path
|
||||
android:fillAlpha="1"
|
||||
android:fillColor="@color/colorPrimary"
|
||||
android:fillColor="@android:color/black"
|
||||
android:pathData="m18,31h4L22,17h-4zM26,17v14h4L30,17Z" />
|
||||
</vector>
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<color android:color="@android:color/system_neutral1_50" />
|
||||
</item>
|
||||
<item android:drawable="?android:attr/selectableItemBackground" />
|
||||
</layer-list>
|
||||
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<selector
|
||||
android:enterFadeDuration="@android:integer/config_shortAnimTime"
|
||||
android:exitFadeDuration="@android:integer/config_shortAnimTime">
|
||||
<item android:state_checked="true">
|
||||
<color android:color="@color/check_text_view_mask" />
|
||||
</item>
|
||||
</selector>
|
||||
</item>
|
||||
<item android:drawable="?android:attr/selectableItemBackground" />
|
||||
</layer-list>
|
||||
@@ -1,47 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="64dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
|
||||
<path
|
||||
android:fillColor="@color/teal_500"
|
||||
android:pathData="M0,12a12,12 0 1,0 24,0a12,12 0 1,0 -24,0" />
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/black"
|
||||
android:pathData="M16.5,9.9L16.1,9.6L19.1,5.9C19.2,5.8,19.4,5.8,19.5,5.9L19.5,5.9C19.6,6,19.6,6.2,19.5,6.3L16.5,9.9Z" />
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/black"
|
||||
android:pathData="M9.1,8.8L8.6,9.1L6.2,5C6.1,4.9,6.2,4.7,6.3,4.6L6.3,4.6C6.4,4.5,6.6,4.6,6.7,4.7L9.1,8.8Z" />
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M1.8,14.6L22.1,16.4C21.4,11.3,17.6,7.9,12.6,7.5C7.8,7,3.4,9.7,1.8,14.6Z" />
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/black"
|
||||
android:pathData="M15.4,11.9a1.1,1.1 0 1,0 2.2,0a1.1,1.1 0 1,0 -2.2,0" />
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/black"
|
||||
android:pathData="M7.1,11.2a1.1,1.1 0 1,0 2.2,0a1.1,1.1 0 1,0 -2.2,0" />
|
||||
|
||||
</vector>
|
||||
@@ -1,10 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,3c-4.97,0 -9,4.03 -9,9s4.03,9 9,9s9,-4.03 9,-9c0,-0.46 -0.04,-0.92 -0.1,-1.36c-0.98,1.37 -2.58,2.26 -4.4,2.26c-2.98,0 -5.4,-2.42 -5.4,-5.4c0,-1.81 0.89,-3.42 2.26,-4.4C12.92,3.04 12.46,3 12,3L12,3z" />
|
||||
</vector>
|
||||
@@ -5,6 +5,6 @@
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillAlpha="1"
|
||||
android:fillColor="@color/colorPrimary"
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="m42,68h8V40H42ZM58,40v28h8V40Z" />
|
||||
</vector>
|
||||
@@ -5,6 +5,6 @@
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillAlpha="1"
|
||||
android:fillColor="@color/colorPrimary"
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M46,40V68L68,54Z" />
|
||||
</vector>
|
||||
@@ -1,92 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:baselineAligned="true"
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="@dimen/keyline_margin"
|
||||
app:fitsSystemWindowsInsets="bottom">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal|top"
|
||||
android:gravity="center"
|
||||
android:padding="@dimen/keyline_margin"
|
||||
android:text="@string/set_pattern_protection_tip"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatCheckBox
|
||||
android:id="@+id/fingerprint_checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:text="@string/enable_biometric"
|
||||
android:visibility="gone" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/buttonPanel"
|
||||
style="?attr/buttonBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="12dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingRight="12dp"
|
||||
android:paddingBottom="4dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/set"
|
||||
style="@style/Widget.MaterialComponents.Button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/set" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/cancel"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@android:string/cancel" />
|
||||
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1">
|
||||
|
||||
<com.hippo.widget.lockpattern.LockPatternView
|
||||
android:id="@+id/pattern_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
@@ -11,13 +11,12 @@
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="4dp"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize" />
|
||||
android:layout_height="wrap_content" />
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="@dimen/keyline_margin"
|
||||
app:fitsSystemWindowsInsets="bottom">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:padding="@dimen/keyline_margin"
|
||||
android:text="@string/set_pattern_protection_tip"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
<com.hippo.widget.MaxSizeContainer xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="32dp"
|
||||
app:maxHeight="320dp"
|
||||
app:maxWidth="320dp">
|
||||
|
||||
<com.hippo.widget.lockpattern.LockPatternView
|
||||
android:id="@+id/pattern_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</com.hippo.widget.MaxSizeContainer>
|
||||
</FrameLayout>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatCheckBox
|
||||
android:id="@+id/fingerprint_checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:text="@string/enable_biometric"
|
||||
android:visibility="gone" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/buttonPanel"
|
||||
style="?attr/buttonBarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:gravity="bottom"
|
||||
android:orientation="horizontal"
|
||||
android:paddingLeft="12dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingRight="12dp"
|
||||
android:paddingBottom="20dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/cancel"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="4dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@android:string/cancel" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/set"
|
||||
style="@style/Widget.MaterialComponents.Button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="4dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/set" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -137,56 +137,56 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/keep_screen_on"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/settings_read_keep_screen_on" />
|
||||
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/show_clock"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/settings_read_show_clock" />
|
||||
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/show_progress"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/settings_read_show_progress" />
|
||||
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/show_battery"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/settings_read_show_battery" />
|
||||
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/show_page_interval"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/settings_read_show_page_interval" />
|
||||
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/volume_page"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/settings_read_volume_page"
|
||||
android:textColor="?android:attr/textColorPrimary" />
|
||||
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/reverse_volume_page"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/settings_read_reverse_volume" />
|
||||
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/reading_fullscreen"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/settings_read_reading_fullscreen" />
|
||||
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/custom_screen_lightness"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -105,8 +105,7 @@
|
||||
android:paddingTop="16dp"
|
||||
android:paddingRight="12dp"
|
||||
android:paddingBottom="16dp"
|
||||
app:srcCompat="@drawable/v_dots_vertical_secondary_dark_x24"
|
||||
app:tint="@color/primary_drawable_black" />
|
||||
app:srcCompat="@drawable/v_dots_vertical_secondary_dark_x24" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
style="@style/Widget.Material3.CardView.Elevated"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="2dp"
|
||||
android:background="?selectableItemBackground"
|
||||
android:layout_margin="4dp"
|
||||
android:checkable="true"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
@@ -61,7 +60,7 @@
|
||||
style="@style/CardMessage"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/title"
|
||||
android:layout_above="@id/rating"
|
||||
android:layout_alignStart="@id/title"
|
||||
android:layout_marginTop="2dp" />
|
||||
|
||||
@@ -86,7 +85,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_above="@id/category"
|
||||
android:layout_alignStart="@id/title"
|
||||
android:layout_marginBottom="4dp" />
|
||||
android:layout_marginBottom="2dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/posted"
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:fitsSystemWindowsInsets="top|bottom">
|
||||
|
||||
<com.hippo.widget.MaxSizeContainer xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="32dp"
|
||||
app:maxHeight="360dp"
|
||||
app:maxWidth="320dp">
|
||||
|
||||
<com.hippo.widget.lockpattern.LockPatternView
|
||||
android:id="@+id/pattern_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
|
||||
</com.hippo.widget.MaxSizeContainer>
|
||||
</FrameLayout>
|
||||
@@ -89,7 +89,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/search_enable_advance"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -15,18 +15,8 @@
|
||||
-->
|
||||
|
||||
<resources>
|
||||
|
||||
<color name="primary_drawable">#ffffffff</color>
|
||||
|
||||
<!-- divider -->
|
||||
<color name="divider">#20ffffff</color>
|
||||
|
||||
<!-- shadow -->
|
||||
|
||||
<!-- Gallery Activity -->
|
||||
|
||||
<!-- Lock pattern view -->
|
||||
<color name="lock_pattern_view_regular_color">#8affffff</color>
|
||||
|
||||
<!--Shortcut background-->
|
||||
</resources>
|
||||
@@ -519,7 +519,6 @@
|
||||
<string name="settings_about_declaration" translatable="false">EhViewer</string>
|
||||
<string name="settings_about_declaration_summary">EhViewer ไม่มีความแตกต่างจากตัวเว็บ E-Hentai.org แต่อย่างใด</string>
|
||||
<string name="settings_about_author">นักพัฒนา</string>
|
||||
<string name="settings_about_author_summary" translatable="false">Hippo <ehviewersu$gmail.com></string>
|
||||
<string name="settings_about_telegram">Telegram</string>
|
||||
<string name="settings_about_website">เว็บไซต์</string>
|
||||
<string name="settings_about_source">ซอร์สโค้ด</string>
|
||||
|
||||
@@ -761,4 +761,5 @@
|
||||
<string name="open_by_default">默认打开</string>
|
||||
|
||||
<string name="show_library_version">依赖库之版本</string>
|
||||
<string name="settings_privacy_require_unlock">需要解锁</string>
|
||||
</resources>
|
||||
@@ -15,12 +15,9 @@
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<color name="primary_drawable">#8a000000</color>
|
||||
<color name="primary_drawable_dark">#ffffffff</color>
|
||||
<color name="primary_drawable_black">#ccffffff</color>
|
||||
<color name="secondary_drawable_black">#3dffffff</color>
|
||||
<color name="red_500">#fff44336</color>
|
||||
<color name="teal_500">#ff009688</color>
|
||||
<color name="purple_a700">#ffaa00ff</color>
|
||||
<color name="cyan_600">#ff00acc1</color>
|
||||
<color name="light_green_600">#ff7cb342</color>
|
||||
@@ -28,7 +25,6 @@
|
||||
<color name="deep_orange_400">#ffff7043</color>
|
||||
<color name="grey_500">#ff9e9e9e</color>
|
||||
<color name="brown_500">#ff795548</color>
|
||||
<color name="colorPrimary">@color/teal_500</color>
|
||||
<color name="loading_indicator_red">#ffe53935</color>
|
||||
<color name="loading_indicator_purple">@color/purple_a700</color>
|
||||
<color name="loading_indicator_blue">#ff1d87e4</color>
|
||||
@@ -36,28 +32,17 @@
|
||||
<color name="loading_indicator_green">@color/light_green_600</color>
|
||||
<color name="loading_indicator_yellow">@color/yellow_800</color>
|
||||
<color name="loading_indicator_orange">@color/deep_orange_400</color>
|
||||
<color name="check_text_view_mask">#4a000000</color>
|
||||
|
||||
<!-- Red -->
|
||||
<color name="doujinshi">#fff44336</color>
|
||||
<!-- Orange -->
|
||||
<color name="manga">#ffff9800</color>
|
||||
<!-- Yellow 700 -->
|
||||
<color name="artist_cg">#ffFbc02d</color>
|
||||
<!-- Green -->
|
||||
<color name="game_cg">#ff4caf50</color>
|
||||
<!-- Light Green -->
|
||||
<color name="western">#ff8bc34a</color>
|
||||
<!-- Blue -->
|
||||
<color name="non_h">#ff2196f3</color>
|
||||
<!-- Indigo -->
|
||||
<color name="image_set">#ff3f51b5</color>
|
||||
<!-- Purple -->
|
||||
<color name="cosplay">#ff9c27b0</color>
|
||||
<!-- Deep Purple 300 -->
|
||||
<color name="asian_porn">#ff9575cd</color>
|
||||
<!-- Pink 300 -->
|
||||
<color name="misc">#fff06292</color>
|
||||
|
||||
<!-- divider -->
|
||||
<color name="divider">#20000000</color>
|
||||
@@ -68,9 +53,6 @@
|
||||
<!-- Gallery Activity -->
|
||||
|
||||
<!-- Lock pattern view -->
|
||||
<color name="lock_pattern_view_regular_color">#8a000000</color>
|
||||
<color name="lock_pattern_view_success_color">@color/colorPrimary</color>
|
||||
<color name="lock_pattern_view_error_color">@color/red_500</color>
|
||||
|
||||
<!--Shortcut background-->
|
||||
<color name="shortcut_bg">#F5F5F5</color>
|
||||
|
||||
@@ -82,8 +82,6 @@
|
||||
<dimen name="add_thickness">2dp</dimen>
|
||||
|
||||
<!-- CategoryTable -->
|
||||
<dimen name="category_table_item_height">40dp</dimen>
|
||||
<dimen name="category_table_item_margin">4dp</dimen>
|
||||
|
||||
<!-- AdvanceSearchTable -->
|
||||
<dimen name="advance_search_table_item_margin">4dp</dimen>
|
||||
@@ -97,7 +95,6 @@
|
||||
<dimen name="image_search_max_size">240dp</dimen>
|
||||
|
||||
<!-- Switch padding -->
|
||||
<dimen name="switch_padding">8dp</dimen>
|
||||
|
||||
<!-- FabLayout -->
|
||||
<dimen name="fab_layout_primary_margin">24dp</dimen>
|
||||
@@ -120,9 +117,6 @@
|
||||
<dimen name="gallery_guide_divider_width">3dp</dimen>
|
||||
|
||||
<!-- Lock pattern view -->
|
||||
<dimen name="lock_pattern_dot_line_width">3dp</dimen>
|
||||
<dimen name="lock_pattern_dot_size">12dp</dimen>
|
||||
<dimen name="lock_pattern_dot_size_activated">28dp</dimen>
|
||||
|
||||
<!-- Custom preference -->
|
||||
|
||||
|
||||
@@ -17,6 +17,5 @@
|
||||
|
||||
<drawable name="transparent">@android:color/transparent</drawable>
|
||||
<drawable name="tile_background_activated">#7f9c27b0</drawable>
|
||||
<drawable name="sadpanda_low_poly">@color/colorPrimary</drawable>
|
||||
|
||||
</resources>
|
||||
@@ -620,7 +620,7 @@
|
||||
<string name="settings_about_declaration" translatable="false">EhViewer</string>
|
||||
<string name="settings_about_declaration_summary">EhViewer is not affiliated with E-Hentai.org in any way</string>
|
||||
<string name="settings_about_author">Author</string>
|
||||
<string name="settings_about_author_summary" translatable="false"><del>Hippo &lt;ehviewersu$gmail.com&gt;</del><br>NekoInverter &lt;nekoworkshop$protonmail.com&gt;</string>
|
||||
<string name="settings_about_author_summary" translatable="false"><del>Hippo &lt;ehviewersu$gmail.com&gt;</del><br>NekoInverter &lt;nekoworkshop$protonmail.com&gt; <br>飛鳥澪</string>
|
||||
<string name="settings_about_telegram">Telegram</string>
|
||||
<string name="settings_about_latest_release">Latest release</string>
|
||||
<string name="settings_about_website">Website</string>
|
||||
@@ -825,4 +825,5 @@
|
||||
<string name="open_by_default">Open by default</string>
|
||||
|
||||
<string name="show_library_version">Library Version</string>
|
||||
<string name="settings_privacy_require_unlock">Require Unlock</string>
|
||||
</resources>
|
||||
@@ -19,7 +19,6 @@
|
||||
<style name="AppTheme" parent="Base.AppTheme" />
|
||||
|
||||
<!-- TextAppearance -->
|
||||
<style name="TextAppearance" parent="TextAppearance.Material3.TitleMedium" />
|
||||
|
||||
<!-- Progress View -->
|
||||
<style name="ProgressView">
|
||||
@@ -28,38 +27,19 @@
|
||||
<item name="android:minHeight">48dp</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.CardTitle">
|
||||
<item name="android:textSize">@dimen/text_little_small</item>
|
||||
</style>
|
||||
|
||||
<style name="CardTitle">
|
||||
<item name="android:textAppearance">@style/TextAppearance.CardTitle</item>
|
||||
<item name="android:textAppearance">@style/TextAppearance.Material3.TitleSmall</item>
|
||||
<item name="android:ellipsize">end</item>
|
||||
<item name="android:maxLines">2</item>
|
||||
<item name="android:lineSpacingExtra">0sp</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.CardMessage">
|
||||
<item name="android:textSize">@dimen/text_small</item>
|
||||
</style>
|
||||
|
||||
<style name="CardMessage">
|
||||
<item name="android:textAppearance">@style/TextAppearance.CardMessage</item>
|
||||
<item name="android:textAppearance">@style/TextAppearance.Material3.BodyMedium</item>
|
||||
<item name="android:ellipsize">end</item>
|
||||
<item name="android:singleLine">true</item>
|
||||
<item name="android:maxLines">1</item>
|
||||
</style>
|
||||
|
||||
<!-- CategoryText -->
|
||||
<style name="CategoryText">
|
||||
<item name="android:textAppearance">@style/TextAppearance.Material3.ActionBar.Subtitle
|
||||
</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
<item name="android:gravity">center</item>
|
||||
<item name="android:minHeight">@dimen/category_table_item_height</item>
|
||||
<item name="android:foreground">@drawable/check_text_view_foreground</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.CategoryTitle" parent="TextAppearance.Material3.TitleMedium">
|
||||
<item name="android:textStyle">bold</item>
|
||||
</style>
|
||||
@@ -71,15 +51,6 @@
|
||||
<item name="android:gravity">center_vertical</item>
|
||||
</style>
|
||||
|
||||
<style name="ButtonInCard">
|
||||
<item name="android:textAppearance">@style/TextAppearance.Material3.LabelLarge</item>
|
||||
<item name="android:textStyle">bold</item>
|
||||
<item name="android:gravity">center</item>
|
||||
<item name="android:minHeight">48dp</item>
|
||||
<item name="android:minWidth">88dp</item>
|
||||
<item name="android:padding">8dp</item>
|
||||
</style>
|
||||
|
||||
<style name="Slider">
|
||||
<item name="thickness">2dp</item>
|
||||
<item name="radius">6dp</item>
|
||||
|
||||
@@ -21,10 +21,8 @@
|
||||
<!-- ========== -->
|
||||
|
||||
<style name="Base.AppTheme" parent="Theme.Material3.DynamicColors.DayNight">
|
||||
<item name="colorControlActivated">@color/colorPrimary</item>
|
||||
<item name="colorEdgeEffect">@color/grey_500</item>
|
||||
<item name="dividerColor">@color/divider</item>
|
||||
<item name="colorControlNormal">@color/primary_drawable</item>
|
||||
<item name="drawableColorSecondary">@color/material_dynamic_secondary50</item>
|
||||
<item name="drawableColorThemePrimary">@color/material_dynamic_primary50</item>
|
||||
<item name="guideBackgroundColor">@color/material_dynamic_neutral80</item>
|
||||
@@ -36,6 +34,8 @@
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
<item name="materialSwitchStyle">@style/Widget.Material3.CompoundButton.MaterialSwitch</item>
|
||||
<item name="switchStyle">@style/Widget.Material3.CompoundButton.MaterialSwitch</item>
|
||||
</style>
|
||||
|
||||
<style name="ThemeOverlay" />
|
||||
@@ -45,10 +45,8 @@
|
||||
<item name="android:colorBackground">@android:color/black</item>
|
||||
<item name="toolbarColor">@android:color/black</item>
|
||||
<item name="dividerColor">@color/divider_black</item>
|
||||
<item name="colorControlNormal">@color/primary_drawable_black</item>
|
||||
<item name="drawableColorSecondary">@color/secondary_drawable_black</item>
|
||||
<item name="drawableColorThemePrimary">@color/primary_drawable_black</item>
|
||||
<item name="guideBackgroundColor">#e5000000</item>
|
||||
</style>
|
||||
|
||||
<!-- ============ -->
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<intent
|
||||
android:action="start_all"
|
||||
android:targetClass="com.hippo.ehviewer.shortcuts.ShortcutsActivity"
|
||||
android:targetPackage="io.github.nekoinverter.ehviewer" />
|
||||
android:targetPackage="moe.tarsin.ehviewer" />
|
||||
<categories android:name="android.shortcut.conversation" />
|
||||
</shortcut>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<intent
|
||||
android:action="stop_all"
|
||||
android:targetClass="com.hippo.ehviewer.shortcuts.ShortcutsActivity"
|
||||
android:targetPackage="io.github.nekoinverter.ehviewer" />
|
||||
android:targetPackage="moe.tarsin.ehviewer" />
|
||||
<categories android:name="android.shortcut.conversation" />
|
||||
</shortcut>
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<Preference
|
||||
android:fragment="com.hippo.ehviewer.ui.fragment.SetSecurityFragment"
|
||||
android:key="pattern_protection"
|
||||
android:title="@string/settings_privacy_pattern_protection_title"
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="require_unlock"
|
||||
android:title="@string/settings_privacy_require_unlock"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 735 KiB After Width: | Height: | Size: 1003 KiB |
+1
-1
@@ -5,7 +5,7 @@ buildscript {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'com.android.application' version '7.2.0' apply false
|
||||
id 'com.android.application' version '7.2.1' apply false
|
||||
id 'org.jetbrains.kotlin.android' version '1.6.21' apply false
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -14,7 +14,7 @@ android {
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments '-DANDROID_STL=c++_shared'
|
||||
arguments '-DANDROID_STL=none'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ android {
|
||||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
version "3.23.1"
|
||||
path 'src/main/jni/CMakeLists.txt'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
project(ehviewer)
|
||||
cmake_minimum_required(VERSION 3.4.1)
|
||||
|
||||
# Build liblzma as shared lib
|
||||
set(BUILD_SHARED_LIBS ON)
|
||||
# Build liblzma
|
||||
add_subdirectory(liblzma)
|
||||
|
||||
# Build libzstd
|
||||
SET(ZSTD_BUILD_SHARED OFF)
|
||||
add_subdirectory(libzstd/build/cmake)
|
||||
|
||||
# Configure libarchive for android platform
|
||||
@@ -38,4 +38,4 @@ include_directories(libfuse/libfuse/include)
|
||||
# Build and link our app's native lib
|
||||
add_library(${PROJECT_NAME} SHARED main.c archive.c)
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC image/image libarchive/libarchive)
|
||||
target_link_libraries(${PROJECT_NAME} image archive fuse)
|
||||
target_link_libraries(${PROJECT_NAME} image archive_static fuse)
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,8 +0,0 @@
|
||||
//
|
||||
// Created by tarsin on 2022/5/24.
|
||||
//
|
||||
|
||||
#ifndef EHVIEWER_FUSE_ARCHIVE_H
|
||||
#define EHVIEWER_FUSE_ARCHIVE_H
|
||||
|
||||
#endif //EHVIEWER_FUSE_ARCHIVE_H
|
||||
@@ -23,5 +23,5 @@ set(GIFLIB_SOURCES
|
||||
)
|
||||
set(GIFLIB_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/giflib)
|
||||
|
||||
add_library(gif SHARED ${GIFLIB_SOURCES})
|
||||
add_library(gif STATIC ${GIFLIB_SOURCES})
|
||||
target_include_directories(gif PUBLIC ${GIFLIB_INCLUDES})
|
||||
@@ -39,6 +39,6 @@ set(IMAGE_LIBRARIES
|
||||
gif
|
||||
)
|
||||
|
||||
add_library(image SHARED ${IMAGE_SOURCES})
|
||||
add_library(image STATIC ${IMAGE_SOURCES})
|
||||
target_include_directories(image PRIVATE ${IMAGE_INCLUDES})
|
||||
target_link_libraries(image PRIVATE ${IMAGE_LIBRARIES})
|
||||
@@ -207,5 +207,5 @@ else ()
|
||||
|
||||
endif ()
|
||||
|
||||
add_library(jpeg SHARED ${LIBJPEG_TURBO_SOURCES})
|
||||
add_library(jpeg STATIC ${LIBJPEG_TURBO_SOURCES})
|
||||
target_include_directories(jpeg PUBLIC ${LIBJPEG_TURBO_INCLUDES})
|
||||
@@ -91,7 +91,7 @@ elseif (${ANDROID_ABI} STREQUAL "arm64-v8a")
|
||||
|
||||
endif ()
|
||||
|
||||
add_library(png SHARED ${LIBPNG_SOURCES})
|
||||
add_library(png STATIC ${LIBPNG_SOURCES})
|
||||
target_include_directories(png PUBLIC ${LIBPNG_INCLUDES})
|
||||
target_compile_definitions(png PRIVATE ${LIBPNG_DEFINITIONS})
|
||||
target_link_libraries(png PRIVATE ${LIBPNG_LIBRARIES})
|
||||
@@ -24,6 +24,5 @@ set(STREAM_SOURCES
|
||||
)
|
||||
set(STREAM_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
add_library(stream SHARED ${STREAM_SOURCES})
|
||||
add_library(stream STATIC ${STREAM_SOURCES})
|
||||
target_include_directories(stream PUBLIC ${STREAM_INCLUDES})
|
||||
target_link_libraries(stream PRIVATE log)
|
||||
Submodule native/src/main/jni/libarchive updated: f06bdccdfb...c0896da37f.
@@ -26,5 +26,5 @@ set(FUSE_INCLUDES
|
||||
libfuse/lib
|
||||
libfuse/include)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--version-script=${CMAKE_SOURCE_DIR}/libfuse/libfuse/lib/fuse_versionscript")
|
||||
add_library(${PROJECT_NAME} SHARED ${FUSE_SOURCES})
|
||||
add_library(${PROJECT_NAME} STATIC ${FUSE_SOURCES})
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE ${FUSE_INCLUDES})
|
||||
Reference in new issue
Block a user