Compare commits

...
62 Commits
Author SHA1 Message Date
tarsin cf3769959f Rel
EhViewer / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-05-27 23:37:42 +08:00
tarsin 10e853f968 Build static library 2022-05-27 23:23:04 +08:00
tarsin ac6995be8d Setting preference wants to be material, too 2022-05-27 21:53:34 +08:00
tarsin 045d6f15a7 Workaround for mdc170alpha02
* See https://github.com/material-components/material-components-android/issues/2720
2022-05-27 21:35:36 +08:00
tarsin 2d16f1cb57 Convert to materialswitch 2022-05-27 21:29:04 +08:00
tarsin ebe1d451b0 Drop build guide
* No longer force cmake version
2022-05-27 20:27:56 +08:00
tarsin 7faa4bea0d Add zstd to Credits 2022-05-27 20:26:53 +08:00
tarsin 3785d3130c Inline yorozuya 2022-05-27 00:48:51 +08:00
tarsin 9daa1c27c4 Drop rikkax core 2022-05-27 00:30:05 +08:00
tarsin 67c722ed43 Revert "Drop rikkax core"
This reverts commit 7a64fd4c11.
2022-05-27 00:27:25 +08:00
tarsin 3c8f1809aa Revert "Drop rikkax appcompat"
This reverts commit f9aad6ce9c.
2022-05-27 00:27:14 +08:00
tarsin c5a3059e31 Revert "Revert "Drop rikkax appcompat""
This reverts commit c7ffdbec93.
2022-05-27 00:26:59 +08:00
tarsin f305a59880 Revert "Drop rikkax"
This reverts commit 87d284ad97.
2022-05-27 00:26:48 +08:00
tarsin aa18e5f2f1 Revert "Drawable color fix"
This reverts commit 2f3f3d6480.
2022-05-27 00:26:35 +08:00
tarsin ac25b95eae Revert "Tag color fix"
This reverts commit a92fa304f1.
2022-05-27 00:26:09 +08:00
tarsin a92fa304f1 Tag color fix 2022-05-26 23:00:45 +08:00
tarsin 2f3f3d6480 Drawable color fix 2022-05-26 22:23:00 +08:00
tarsin 0d108393fc Don't set cmake version 2022-05-26 19:57:39 +08:00
tarsin 8d02f8c6a3 Upgrade cmake 2022-05-26 19:51:06 +08:00
tarsin 87d284ad97 Drop rikkax
* TODO: Reimplement some functions

* Locale set is not needed as Android 13 will land

* Don't release until all fixed
2022-05-26 19:37:15 +08:00
tarsin c7ffdbec93 Revert "Drop rikkax appcompat"
This reverts commit f9aad6ce9c.
2022-05-26 18:16:34 +08:00
tarsin f9aad6ce9c Drop rikkax appcompat 2022-05-26 18:10:49 +08:00
tarsin 7a64fd4c11 Drop rikkax core 2022-05-26 18:09:17 +08:00
tarsin 909af0cbed Upgrade material components 2022-05-26 17:51:02 +08:00
tarsin 34db6fc79f Merge pull request #43 from asuka-mio/renovate/com.android.application-7.x
Update plugin com.android.application to v7.2.1
2022-05-26 00:36:48 +08:00
Renovate Bot b025f150c9 Update plugin com.android.application to v7.2.1 2022-05-25 16:29:11 +00:00
tarsin fea6b38c1e Drop rikkax's layoutinflater
* This is unused
2022-05-25 17:31:56 +08:00
tarsin e2a03427f9 Drop rikkax's inset
* This is unused
2022-05-25 17:30:50 +08:00
tarsin 654cb5fdd5 Drop rikkax's recyclerview fix
* Now that we are Android S and above, we don't need these fix anymore
2022-05-25 17:28:04 +08:00
tarsin 333419b716 Drop dynanim
* Unused and unmaintained for three years
2022-05-25 17:16:33 +08:00
tarsin c66b1e4622 Drop a class that no longer used 2022-05-25 16:41:30 +08:00
tarsin ac55e2f8fc Version bump
EhViewer / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-05-25 16:31:27 +08:00
tarsin 24718e7d25 Add my name to author 2022-05-25 16:30:50 +08:00
tarsin 72e1fe9621 Rename packagename 2022-05-25 16:19:59 +08:00
tarsin 0c7bf96bf5 No "require unlock" available when keyguard is not enabled 2022-05-25 15:55:54 +08:00
tarsin f1dde6c8bc Rename .java to .kt 2022-05-25 15:55:54 +08:00
tarsin 3177a8352f Drop unused res 2022-05-25 15:30:01 +08:00
tarsin 3cb024b7f5 Do code reformat 2022-05-25 15:25:57 +08:00
tarsin 0a09d16500 Drop LockPatternView, migrate to system's provider 2022-05-25 15:24:35 +08:00
tarsin a79b1ad785 Rename .java to .kt 2022-05-25 15:24:35 +08:00
tarsin 4388c53929 Clean primary color
* No hardcode primary color anymore
2022-05-25 14:08:57 +08:00
tarsin 02f000d37b Merge pull request #41 from txyyh/feature/checkbox
Use checkbox instead
2022-05-24 23:44:32 +08:00
txyyh 7259e5e315 Use checkbox instead
It's clear that button will be too big when switch to m3 button, and radio button is not suitable, so that's it.
2022-05-24 23:01:43 +08:00
tarsin 7ce744b5af Fix #39 2022-05-24 21:34:37 +08:00
tarsin 57f21c8d39 Version bump
EhViewer / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-05-24 18:19:11 +08:00
tarsin 07f27a8438 screenshot update 2022-05-24 18:16:56 +08:00
tarsin 4f4dc55b4e itemcard layout tweaks 2022-05-24 18:15:33 +08:00
tarsin 8e9cda8e93 update screenshot 2022-05-24 17:37:46 +08:00
tarsin d5cfabd73e Only check this when longpressed on chip 2022-05-24 17:12:05 +08:00
tarsin 4f1b371e75 Drop fuse-archive
* This glue can't work with latest libfuse and require c++ STL for no reason, drop it and try find better impl
2022-05-24 16:59:07 +08:00
tarsin 4d8b50912d Version bump
EhViewer / Build (push) Has been cancelled
Releases / Build (push) Has been cancelled
2022-05-24 16:52:50 +08:00
tarsin 660c717cfb Make elf32 release 2022-05-24 16:51:37 +08:00
tarsin 70d3d9a5b2 Update jconfigint.h 2022-05-24 16:35:37 +08:00
tarsin 464da25ca1 Update android.yml 2022-05-24 16:08:58 +08:00
tarsin c2795120e1 Update CMakeLists.txt 2022-05-24 16:00:51 +08:00
tarsin b10fb46f1f Update build.gradle 2022-05-24 15:48:38 +08:00
tarsin e39897176a Update build.gradle 2022-05-24 15:47:45 +08:00
tarsin 6e7e6fab74 Import fuse-archive 0.1.11
* From https://github.com/google/fuse-archive
2022-05-24 11:33:41 +08:00
tarsin 94009b6829 Build libfuse 2022-05-24 11:09:19 +08:00
tarsin 289217fefc Add libfuse 3.11.0 2022-05-24 10:02:26 +08:00
tarsin 97feab1203 Rename native libs, drop a rikka util 2022-05-24 09:23:05 +08:00
tarsin a9a3b20eb3 Make all lib shared 2022-05-23 21:22:39 +08:00
94 changed files with 3571 additions and 2169 deletions

No files matched your search

+17 -5
View File
@@ -13,8 +13,8 @@ jobs:
- name: Setup Java 11
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 11
distribution: temurin
java-version: 11
- name: Setup Android SDK Tools
uses: android-actions/setup-android@v2
@@ -32,11 +32,23 @@ jobs:
- name: Update ARM64
uses: actions/upload-artifact@v3
with:
name: arm64-${{ github.sha }}
path: app/build/outputs/apk/release/app-arm64-v8a-release.apk
name: arm64-${{ github.sha }}
path: app/build/outputs/apk/release/app-arm64-v8a-release.apk
- name: Update x86_64
uses: actions/upload-artifact@v3
with:
name: x86_64-${{ github.sha }}
path: app/build/outputs/apk/release/app-x86_64-release.apk
path: app/build/outputs/apk/release/app-x86_64-release.apk
- name: Update ARM32
uses: actions/upload-artifact@v3
with:
name: armeabi-v7a-${{ github.sha }}
path: app/build/outputs/apk/release/app-armeabi-v7a-release.apk
- name: Update x86
uses: actions/upload-artifact@v3
with:
name: x86-${{ github.sha }}
path: app/build/outputs/apk/release/app-x86-release.apk
+4 -2
View File
@@ -16,8 +16,8 @@ jobs:
- name: Setup Java 11
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 11
distribution: temurin
java-version: 11
- name: Setup Android SDK Tools
uses: android-actions/setup-android@v2
@@ -39,3 +39,5 @@ jobs:
files: |
app/build/outputs/apk/release/app-arm64-v8a-release.apk
app/build/outputs/apk/release/app-x86_64-release.apk
app/build/outputs/apk/release/app-x86-release.apk
app/build/outputs/apk/release/app-armeabi-v7a-release.apk
+3
View File
@@ -16,3 +16,6 @@
[submodule "native/src/main/jni/libzstd"]
path = native/src/main/jni/libzstd
url = https://github.com/facebook/zstd
[submodule "native/src/main/jni/libfuse/libfuse"]
path = native/src/main/jni/libfuse/libfuse
url = https://github.com/libfuse/libfuse/
+1 -11
View File
@@ -43,17 +43,6 @@ Only Support Android S and above!
![screenshot-01](art/screenshot-01.png)
# 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
+7 -14
View File
@@ -17,7 +17,7 @@ android {
abi {
enable = true
reset()
include 'arm64-v8a', 'x86_64'
include 'arm64-v8a', 'x86_64', 'armeabi-v7a', 'x86'
}
}
@@ -35,11 +35,11 @@ android {
}
defaultConfig {
applicationId "io.github.nekoinverter.ehviewer"
applicationId "moe.tarsin.ehviewer"
minSdkVersion 31
targetSdkVersion 32
versionCode 114522
versionName "1.7.27.2"
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,14 +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.widget:borderview:1.1.0'
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'
+3 -6
View File
@@ -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 = {
"&amp;",
"&lt;",
"&gt;",
"&quot;",
"&#039;",
"&times;",
"&nbsp;"
};
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>
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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"
-10
View File
@@ -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>
-1
View File
@@ -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 &lt;ehviewersu$gmail.com&gt;</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>
-18
View File
@@ -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>
-6
View File
@@ -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 -->
-1
View File
@@ -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>
+2 -1
View File
@@ -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">&lt;del&gt;Hippo &amp;lt;ehviewersu$gmail.com&amp;gt;&lt;/del&gt;&lt;br&gt;NekoInverter &amp;lt;nekoworkshop$protonmail.com&amp;gt;</string>
<string name="settings_about_author_summary" translatable="false">&lt;del&gt;Hippo &amp;lt;ehviewersu$gmail.com&amp;gt;&lt;/del&gt;&lt;br&gt;NekoInverter &amp;lt;nekoworkshop$protonmail.com&amp;gt; &lt;br&gt;飛鳥澪</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>
+2 -31
View File
@@ -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>
+2 -4
View File
@@ -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>
<!-- ============ -->
+2 -2
View File
@@ -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>
+4 -4
View File
@@ -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
View File
@@ -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
}
+7 -2
View File
@@ -12,14 +12,19 @@ android {
targetSdkVersion 32
consumerProguardFiles 'proguard-rules.pro'
externalNativeBuild {
cmake {
arguments '-DANDROID_STL=none'
}
}
ndk {
abiFilters 'arm64-v8a', 'x86_64'
abiFilters 'arm64-v8a', 'x86_64', 'armeabi-v7a', 'x86'
}
}
externalNativeBuild {
cmake {
version "3.23.1"
path 'src/main/jni/CMakeLists.txt'
}
}
+7 -3
View File
@@ -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
@@ -31,7 +31,11 @@ add_subdirectory(libarchive)
# Build hippo's image lib
add_subdirectory(image)
# Build libfuse
add_subdirectory(libfuse)
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)
target_link_libraries(${PROJECT_NAME} image archive_static fuse)
@@ -13,7 +13,7 @@
# limitations under the License.
cmake_minimum_required(VERSION 3.4.1)
project(giflib C)
project(gif C)
set(GIFLIB_SOURCES
giflib/dgif_lib.c
@@ -23,5 +23,5 @@ set(GIFLIB_SOURCES
)
set(GIFLIB_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/giflib)
add_library(giflib STATIC ${GIFLIB_SOURCES})
target_include_directories(giflib PUBLIC ${GIFLIB_INCLUDES})
add_library(gif STATIC ${GIFLIB_SOURCES})
target_include_directories(gif PUBLIC ${GIFLIB_INCLUDES})
@@ -34,9 +34,9 @@ set(IMAGE_LIBRARIES
jnigraphics
GLESv2
stream
libjpeg-turbo
libpng
giflib
jpeg
png
gif
)
add_library(image STATIC ${IMAGE_SOURCES})
@@ -13,7 +13,7 @@
# limitations under the License.
cmake_minimum_required(VERSION 3.4.1)
project(libjpeg-turbo C)
project(jpeg C)
set(LIBJPEG_TURBO_SOURCES
libjpeg-turbo/jaricom.c
@@ -207,5 +207,5 @@ else ()
endif ()
add_library(libjpeg-turbo STATIC ${LIBJPEG_TURBO_SOURCES})
target_include_directories(libjpeg-turbo PUBLIC ${LIBJPEG_TURBO_INCLUDES})
add_library(jpeg STATIC ${LIBJPEG_TURBO_SOURCES})
target_include_directories(jpeg PUBLIC ${LIBJPEG_TURBO_INCLUDES})
@@ -17,7 +17,7 @@
#define VERSION "2.1.3"
/* The size of `size_t', as computed by sizeof. */
#define SIZEOF_SIZE_T 8
#define SIZEOF_SIZE_T __SIZEOF_SIZE_T__
/* Define if your compiler has __builtin_ctzl() and sizeof(unsigned long) == sizeof(size_t). */
#define HAVE_BUILTIN_CTZL
@@ -13,7 +13,7 @@
# limitations under the License.
cmake_minimum_required(VERSION 3.4.1)
project(libpng C)
project(png C)
set(LIBPNG_SOURCES
libpng/png.c
@@ -91,7 +91,7 @@ elseif (${ANDROID_ABI} STREQUAL "arm64-v8a")
endif ()
add_library(libpng STATIC ${LIBPNG_SOURCES})
target_include_directories(libpng PUBLIC ${LIBPNG_INCLUDES})
target_compile_definitions(libpng PRIVATE ${LIBPNG_DEFINITIONS})
target_link_libraries(libpng PRIVATE ${LIBPNG_LIBRARIES})
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})
@@ -0,0 +1,30 @@
cmake_minimum_required(VERSION 3.4.1)
project(fuse C)
add_definitions(-D_FILE_OFFSET_BITS=64)
add_definitions(-DFUSERMOUNT_DIR=\"/system/bin\")
add_definitions(-DFUSE_USE_VERSION=35)
SET(FUSE_SOURCES
libfuse/lib/fuse.c
libfuse/lib/fuse_loop.c
libfuse/lib/fuse_loop_mt.c
libfuse/lib/fuse_lowlevel.c
libfuse/lib/fuse_opt.c
libfuse/lib/fuse_signals.c
libfuse/lib/buffer.c
libfuse/lib/cuse_lowlevel.c
libfuse/lib/helper.c
libfuse/lib/modules/subdir.c
libfuse/lib/modules/iconv.c
libfuse/lib/mount_util.c
libfuse/lib/mount.c
libfuse/lib/fuse_log.c)
set(FUSE_INCLUDES
${CMAKE_CURRENT_SOURCE_DIR}
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} STATIC ${FUSE_SOURCES})
target_include_directories(${PROJECT_NAME} PRIVATE ${FUSE_INCLUDES})
@@ -0,0 +1,2 @@
#define pthread_setcancelstate(x, y)
#define pthread_cancel(c)
+24
View File
@@ -0,0 +1,24 @@
/*
* Autogenerated by the Meson build system.
* Do not edit, your changes will be lost.
*/
#pragma once
#define HAVE_COPY_FILE_RANGE
#define HAVE_FALLOCATE
#define HAVE_FDATASYNC
#define HAVE_FORK
#define HAVE_FSTATAT
#define HAVE_ICONV
#define HAVE_OPENAT
#define HAVE_PIPE2
#define HAVE_POSIX_FALLOCATE
#define HAVE_READLINKAT
#define HAVE_SETXATTR
#define HAVE_SPLICE
#define HAVE_STRUCT_STAT_ST_ATIM
#undef HAVE_STRUCT_STAT_ST_ATIMESPEC
#define HAVE_UTIMENSAT
#define HAVE_VMSPLICE
#define PACKAGE_VERSION "3.6.1"
#include "android_config.h"