Add crash catch
This commit is contained in:
1 parent
493b70f885
commit
0cbe074fbb
16 files changed
+545
-4
No files matched your search
@@ -31,6 +31,7 @@ public class AppConfig {
|
||||
private static final String DOWNLOAD = "download";
|
||||
private static final String TEMP = "temp";
|
||||
private static final String IMAGE = "image";
|
||||
private static final String CRASH = "crash";
|
||||
|
||||
private static Context sContext;
|
||||
|
||||
@@ -85,6 +86,11 @@ public class AppConfig {
|
||||
return getDirInExternalAppDir(IMAGE);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File getExternalCrashDir() {
|
||||
return getDirInExternalAppDir(CRASH);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static File createExternalTempFile() {
|
||||
return FileUtils.createTempFile(getExternalTempDir(), null);
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
|
||||
import com.hippo.util.PackageUtils;
|
||||
import com.hippo.util.ReadableTime;
|
||||
import com.hippo.yorozuya.IOUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
public class Crash {
|
||||
|
||||
private static void collectInfo(Context context, FileWriter fw) throws IOException {
|
||||
try {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
PackageInfo pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);
|
||||
if (pi != null) {
|
||||
String versionName = pi.versionName == null ? "null" : pi.versionName;
|
||||
String versionCode = String.valueOf(pi.versionCode);
|
||||
fw.write("======== PackageInfo ========\r\n");
|
||||
fw.write("PackageName=");fw.write(pi.packageName);fw.write("\r\n");
|
||||
fw.write("VersionName=");fw.write(versionName);fw.write("\r\n");
|
||||
fw.write("VersionCode=");fw.write(versionCode);fw.write("\r\n");
|
||||
String signature = PackageUtils.getSignature(context, pi.packageName);
|
||||
fw.write("Signature=");fw.write(null != signature ? signature : "null");fw.write("\r\n");
|
||||
fw.write("\r\n");
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
fw.write("======== PackageInfo ========\r\n");
|
||||
fw.write("Can't get package information\r\n");
|
||||
fw.write("\r\n");
|
||||
}
|
||||
|
||||
// Device info
|
||||
fw.write("======== DeviceInfo ========\r\n");
|
||||
fw.write("BOARD=");fw.write(Build.BOARD);fw.write("\r\n");
|
||||
fw.write("BOOTLOADER=");fw.write(Build.BOOTLOADER);fw.write("\r\n");
|
||||
fw.write("CPU_ABI=");fw.write(Build.CPU_ABI);fw.write("\r\n");
|
||||
fw.write("CPU_ABI2=");fw.write(Build.CPU_ABI2);fw.write("\r\n");
|
||||
fw.write("DEVICE=");fw.write(Build.DEVICE);fw.write("\r\n");
|
||||
fw.write("DISPLAY=");fw.write(Build.DISPLAY);fw.write("\r\n");
|
||||
fw.write("FINGERPRINT=");fw.write(Build.FINGERPRINT);fw.write("\r\n");
|
||||
fw.write("HARDWARE=");fw.write(Build.HARDWARE);fw.write("\r\n");
|
||||
fw.write("HOST=");fw.write(Build.HOST);fw.write("\r\n");
|
||||
fw.write("ID=");fw.write(Build.ID);fw.write("\r\n");
|
||||
fw.write("MANUFACTURER=");fw.write(Build.MANUFACTURER);fw.write("\r\n");
|
||||
fw.write("MODEL=");fw.write(Build.MODEL);fw.write("\r\n");
|
||||
fw.write("PRODUCT=");fw.write(Build.PRODUCT);fw.write("\r\n");
|
||||
fw.write("RADIO=");fw.write(Build.getRadioVersion());fw.write("\r\n");
|
||||
fw.write("SERIAL=");fw.write(Build.SERIAL);fw.write("\r\n");
|
||||
fw.write("TAGS=");fw.write(Build.TAGS);fw.write("\r\n");
|
||||
fw.write("TYPE=");fw.write(Build.TYPE);fw.write("\r\n");
|
||||
fw.write("USER=");fw.write(Build.USER);fw.write("\r\n");
|
||||
fw.write("CODENAME=");fw.write(Build.VERSION.CODENAME);fw.write("\r\n");
|
||||
fw.write("INCREMENTAL=");fw.write(Build.VERSION.INCREMENTAL);fw.write("\r\n");
|
||||
fw.write("RELEASE=");fw.write(Build.VERSION.RELEASE);fw.write("\r\n");
|
||||
fw.write("SDK=");fw.write(Integer.toString(Build.VERSION.SDK_INT));fw.write("\r\n");
|
||||
fw.write("\r\n");
|
||||
}
|
||||
|
||||
public static void getThrowableInfo(Throwable t, FileWriter fw) {
|
||||
PrintWriter printWriter = new PrintWriter(fw);
|
||||
t.printStackTrace(printWriter);
|
||||
Throwable cause = t.getCause();
|
||||
while (cause != null) {
|
||||
cause.printStackTrace(printWriter);
|
||||
cause = cause.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
public static void saveCrashInfo2File(Context context, Throwable ex) {
|
||||
File dir = AppConfig.getExternalCrashDir();
|
||||
if (dir == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String fileName = "crash-" + ReadableTime.getFilenamableTime(System.currentTimeMillis()) + ".log";
|
||||
File file = new File(dir, fileName);
|
||||
|
||||
FileWriter fw = null;
|
||||
try {
|
||||
fw = new FileWriter(file);
|
||||
fw.write("TIME=");fw.write(ReadableTime.getFilenamableTime(System.currentTimeMillis()));fw.write("\r\n");
|
||||
fw.write("\r\n");
|
||||
collectInfo(context, fw);
|
||||
fw.write("======== CrashInfo ========\r\n");
|
||||
getThrowableInfo(ex, fw);
|
||||
fw.write("\r\n");
|
||||
|
||||
fw.flush();
|
||||
|
||||
Settings.putCrashFilename(fileName);
|
||||
} catch (Exception e) {
|
||||
file.delete();
|
||||
} finally {
|
||||
IOUtils.closeQuietly(fw);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean hasCrashFile() {
|
||||
String filename = Settings.getCrashFilename();
|
||||
if (filename == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
File dir = AppConfig.getExternalCrashDir();
|
||||
if (dir == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return new File(dir, filename).isFile();
|
||||
}
|
||||
|
||||
public static String getCrashContent() {
|
||||
String filename = Settings.getCrashFilename();
|
||||
if (filename == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
File dir = AppConfig.getExternalCrashDir();
|
||||
if (dir == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
File file = new File(dir, filename);
|
||||
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = new FileInputStream(file);
|
||||
return IOUtils.readString(is, "UTF-8");
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
} finally {
|
||||
IOUtils.closeQuietly(is);
|
||||
}
|
||||
}
|
||||
|
||||
public static void resetCrashFile() {
|
||||
Settings.putCrashFilename(null);
|
||||
}
|
||||
}
|
||||
@@ -49,13 +49,15 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
public class EhApplication extends SceneApplication {
|
||||
public class EhApplication extends SceneApplication implements Thread.UncaughtExceptionHandler {
|
||||
|
||||
private static final String TAG = EhApplication.class.getSimpleName();
|
||||
|
||||
private static final boolean DEBUG_CONACO = false;
|
||||
private static final boolean DEBUG_NATIVE_MEMORY = false;
|
||||
|
||||
private Thread.UncaughtExceptionHandler mDefaultHandler;
|
||||
|
||||
private final AtomicInteger mIdGenerator = new AtomicInteger();
|
||||
private final SparseArray<Object> mGlobalStuffMap = new SparseArray<>();
|
||||
private EhCookieStore mEhCookieStore;
|
||||
@@ -71,6 +73,10 @@ public class EhApplication extends SceneApplication {
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
// Prepare to catch crash
|
||||
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
|
||||
Thread.setDefaultUncaughtExceptionHandler(this);
|
||||
|
||||
super.onCreate();
|
||||
|
||||
GetText.initialize(this);
|
||||
@@ -263,4 +269,31 @@ public class EhApplication extends SceneApplication {
|
||||
}
|
||||
return application.mDownloadManager;
|
||||
}
|
||||
|
||||
private boolean handleException(Throwable ex) {
|
||||
if (ex == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
ex.printStackTrace();
|
||||
Crash.saveCrashInfo2File(this, ex);
|
||||
return true;
|
||||
} catch (Throwable tr) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void uncaughtException(Thread thread, Throwable ex) {
|
||||
if (!handleException(ex) && mDefaultHandler != null) {
|
||||
mDefaultHandler.uncaughtException(thread, ex);
|
||||
}
|
||||
android.os.Process.killProcess(android.os.Process.myPid());
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String getDeveloperEmail() {
|
||||
return "ehviewersu$gmail.com".replace('$', '@');
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.hippo.ehviewer;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
@@ -486,4 +487,19 @@ public class Settings {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/********************
|
||||
****** Crash
|
||||
********************/
|
||||
public static final String KEY_CRASH_FILENAME = "crash_filename";
|
||||
public static final String VALUE_CRASH_FILENAME = null;
|
||||
|
||||
public static String getCrashFilename() {
|
||||
return getString(KEY_CRASH_FILENAME, VALUE_CRASH_FILENAME);
|
||||
}
|
||||
|
||||
@SuppressLint("CommitPrefEdits")
|
||||
public static void putCrashFilename(String value) {
|
||||
sSettingsPre.edit().putString(KEY_CRASH_FILENAME, value).commit();
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.hippo.ehviewer.Crash;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.Settings;
|
||||
import com.hippo.ehviewer.client.EhUrlOpener;
|
||||
@@ -43,6 +44,7 @@ import com.hippo.ehviewer.client.EhUtils;
|
||||
import com.hippo.ehviewer.ui.scene.AnalyticsScene;
|
||||
import com.hippo.ehviewer.ui.scene.BaseScene;
|
||||
import com.hippo.ehviewer.ui.scene.CookieSignInScene;
|
||||
import com.hippo.ehviewer.ui.scene.CrashScene;
|
||||
import com.hippo.ehviewer.ui.scene.DownloadLabelsScene;
|
||||
import com.hippo.ehviewer.ui.scene.DownloadsScene;
|
||||
import com.hippo.ehviewer.ui.scene.FavoritesScene;
|
||||
@@ -75,7 +77,8 @@ public final class MainActivity extends StageActivity
|
||||
|
||||
public static final int CHECK_STEP_WARNING = 0;
|
||||
public static final int CHECK_STEP_ANALYTICS = 1;
|
||||
public static final int CHECK_STEP_SIGN_IN = 2;
|
||||
public static final int CHECK_STEP_CRASH = 2;
|
||||
public static final int CHECK_STEP_SIGN_IN = 3;
|
||||
|
||||
public static final String KEY_TARGET_SCENE = "target_scene";
|
||||
public static final String KEY_TARGET_ARGS = "target_args";
|
||||
@@ -97,6 +100,7 @@ public final class MainActivity extends StageActivity
|
||||
static {
|
||||
registerLaunchMode(WarningScene.class, SceneFragment.LAUNCH_MODE_SINGLE_TASK);
|
||||
registerLaunchMode(AnalyticsScene.class, SceneFragment.LAUNCH_MODE_SINGLE_TASK);
|
||||
registerLaunchMode(CrashScene.class, SceneFragment.LAUNCH_MODE_SINGLE_TASK);
|
||||
registerLaunchMode(SignInScene.class, SceneFragment.LAUNCH_MODE_SINGLE_TASK);
|
||||
registerLaunchMode(WebViewSignInScene.class, SceneFragment.LAUNCH_MODE_SINGLE_TASK);
|
||||
registerLaunchMode(CookieSignInScene.class, SceneFragment.LAUNCH_MODE_SINGLE_TASK);
|
||||
@@ -121,6 +125,11 @@ public final class MainActivity extends StageActivity
|
||||
break;
|
||||
}
|
||||
case CHECK_STEP_ANALYTICS:
|
||||
if (Crash.hasCrashFile()) {
|
||||
startScene(new Announcer(CrashScene.class).setArgs(args));
|
||||
break;
|
||||
}
|
||||
case CHECK_STEP_CRASH:
|
||||
if (!EhUtils.hasSignedIn(this)) {
|
||||
startScene(new Announcer(SignInScene.class).setArgs(args));
|
||||
break;
|
||||
@@ -165,6 +174,8 @@ public final class MainActivity extends StageActivity
|
||||
return new Announcer(WarningScene.class);
|
||||
} else if (Settings.getAskAnalytics()) {
|
||||
return new Announcer(AnalyticsScene.class);
|
||||
} else if (Crash.hasCrashFile()) {
|
||||
return new Announcer(CrashScene.class);
|
||||
} else if (!EhUtils.hasSignedIn(this)) {
|
||||
return new Announcer(SignInScene.class);
|
||||
} else {
|
||||
@@ -187,6 +198,11 @@ public final class MainActivity extends StageActivity
|
||||
newArgs.putString(KEY_TARGET_SCENE, announcer.getClazz().getName());
|
||||
newArgs.putBundle(KEY_TARGET_ARGS, announcer.getArgs());
|
||||
return new Announcer(AnalyticsScene.class).setArgs(newArgs);
|
||||
} else if (Crash.hasCrashFile()) {
|
||||
Bundle newArgs = new Bundle();
|
||||
newArgs.putString(KEY_TARGET_SCENE, announcer.getClazz().getName());
|
||||
newArgs.putBundle(KEY_TARGET_ARGS, announcer.getArgs());
|
||||
return new Announcer(CrashScene.class).setArgs(newArgs);
|
||||
} else if (!EhUtils.hasSignedIn(this)) {
|
||||
Bundle newArgs = new Bundle();
|
||||
newArgs.putString(KEY_TARGET_SCENE, announcer.getClazz().getName());
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.preference.PreferenceFragment;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
|
||||
import com.hippo.ehviewer.Analytics;
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.Settings;
|
||||
import com.hippo.ehviewer.UrlOpener;
|
||||
@@ -83,7 +84,7 @@ public class AboutFragment extends PreferenceFragment
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
String key = preference.getKey();
|
||||
if (KEY_AUTHOR.equals(key)) {
|
||||
ActivityHelper.sendEmail(getActivity(), "ehviewersu$gmail.com".replace('$', '@'),
|
||||
ActivityHelper.sendEmail(getActivity(), EhApplication.getDeveloperEmail(),
|
||||
"About EhViewer", null);
|
||||
} else if (KEY_GOOGLE_PLUS.equals(key)) {
|
||||
UrlOpener.openUrl(getActivity(), "https://plus.google.com/communities/103823982034655188459",
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.hippo.ehviewer.Crash;
|
||||
import com.hippo.ehviewer.EhApplication;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.ehviewer.ui.MainActivity;
|
||||
import com.hippo.rippleold.RippleSalon;
|
||||
import com.hippo.util.ActivityHelper;
|
||||
import com.hippo.yorozuya.ViewUtils;
|
||||
|
||||
public class CrashScene extends BaseScene implements View.OnClickListener {
|
||||
|
||||
/*---------------
|
||||
Whole life cycle
|
||||
---------------*/
|
||||
@Nullable
|
||||
private String mCrash;
|
||||
|
||||
/*---------------
|
||||
View life cycle
|
||||
---------------*/
|
||||
@Nullable
|
||||
private View mCancel;
|
||||
@Nullable
|
||||
private View mSend;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mCrash = Crash.getCrashContent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
mCrash = null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.scene_crash, container, false);
|
||||
|
||||
mCancel = ViewUtils.$$(view, R.id.cancel);
|
||||
mSend = ViewUtils.$$(view, R.id.send);
|
||||
|
||||
mCancel.setOnClickListener(this);
|
||||
mSend.setOnClickListener(this);
|
||||
|
||||
RippleSalon.addRipple(mCancel, true);
|
||||
RippleSalon.addRipple(mSend, true);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mSend == v && null != mCrash) {
|
||||
ActivityHelper.sendEmail(getActivity(), EhApplication.getDeveloperEmail(),
|
||||
"I found a bug in EhViewer !", mCrash);
|
||||
}
|
||||
Crash.resetCrashFile();
|
||||
|
||||
// Start new scene and finish it self
|
||||
((MainActivity) getActivity()).startSceneForCheckStep(
|
||||
MainActivity.CHECK_STEP_CRASH, getArguments());
|
||||
finish();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.Signature;
|
||||
import android.util.Log;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
public final class PackageUtils {
|
||||
|
||||
private static final String TAG = PackageUtils.class.getSimpleName();
|
||||
|
||||
public static String getSignature(Context context, String packageName) {
|
||||
try {
|
||||
@SuppressLint("PackageManagerGetSignatures")
|
||||
PackageInfo pi = context.getPackageManager().getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
|
||||
Signature[] ss = pi.signatures;
|
||||
if (ss != null && ss.length >= 1) {
|
||||
return computeSHA1(ss[0].toByteArray());
|
||||
} else {
|
||||
Log.e(TAG, "Can't find signature in package " + packageName);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Can't find package " + packageName, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return looks like A1:43:6B:34... or null
|
||||
*/
|
||||
public static String computeSHA1(final byte[] certRaw) {
|
||||
StringBuilder sb = new StringBuilder(59);
|
||||
MessageDigest md;
|
||||
try {
|
||||
md = MessageDigest.getInstance("SHA1");
|
||||
byte[] sha1 = md.digest(certRaw);
|
||||
int length = sha1.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (i != 0) {
|
||||
sb.append(':');
|
||||
}
|
||||
byte b = sha1[i];
|
||||
String appendStr = Integer.toString(b & 0xff, 16);
|
||||
if (appendStr.length() == 1) {
|
||||
sb.append(0);
|
||||
}
|
||||
sb.append(appendStr);
|
||||
}
|
||||
|
||||
return sb.toString().toUpperCase();
|
||||
}
|
||||
catch (NoSuchAlgorithmException ex) {
|
||||
Log.e(TAG, "Can't final Algorithm SHA1", ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?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:height="48dp"
|
||||
android:width="48dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/black"
|
||||
android:pathData="@string/pd_bug"/>
|
||||
|
||||
</vector>
|
||||
@@ -49,7 +49,6 @@
|
||||
android:id="@+id/text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/analytics_explain"
|
||||
android:textColor="@android:color/black"
|
||||
android:textSize="@dimen/text_little_small"
|
||||
android:lineSpacingExtra="8dp"/>
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
<?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:padding="@dimen/keyline_margin"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:padding="@dimen/keyline_margin"
|
||||
app:srcCompat="@drawable/v_bug_black_x48"/>
|
||||
|
||||
<com.hippo.widget.MaxSizeContainer
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_gravity="center_horizontal"
|
||||
app:maxWidth="@dimen/single_max_width">
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="@dimen/keyline_margin"
|
||||
android:paddingBottom="@dimen/keyline_margin"
|
||||
android:clipToPadding="false">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/crash_explain"
|
||||
android:textColor="@android:color/black"
|
||||
android:textSize="@dimen/text_little_small"
|
||||
android:lineSpacingExtra="8dp"/>
|
||||
|
||||
</ScrollView>
|
||||
</com.hippo.widget.MaxSizeContainer>
|
||||
|
||||
<LinearLayout
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/cancel"
|
||||
style="@style/CardButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_margin="@dimen/keyline_margin"
|
||||
android:text="@android:string/cancel"
|
||||
android:textColor="@android:color/white"
|
||||
android:background="@drawable/card_purple_2dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/send"
|
||||
style="@style/CardButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_margin="@dimen/keyline_margin"
|
||||
android:text="@string/send"
|
||||
android:textColor="@android:color/white"
|
||||
android:background="@drawable/card_teal_2dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -83,6 +83,10 @@
|
||||
<string name="analytics_explain">您同意让开发者收集您的设备信息与使用情况吗?这将对开发有很大帮助。<br><br>数据统计服务由 Google Analytics 提供,其隐私政策可在 <a href="https://www.google.com/policies/privacy/">https://www.google.com/policies/privacy/</a> 查看。<br><br>若您同意参与数据统计,您的设备信息与应用使用情况将会被记录,其包括但不限于:Android API 版本、设备型号、语言所在地、本应用版本号、会话时长、应用功能使用次数。开发者承诺以下信息并不会被记录:电话号码、电子邮件地址、IMEI。<br><br>在您同意参与数据统计之前,您的信息不会被记录。<br><br>被记录的数据会由 Google Analytics 进行分析,分析报表仅可被本应用开发者访问。</string>
|
||||
<string name="get_it">知道了</string>
|
||||
|
||||
<!-- Crash -->
|
||||
<string name="send">发送</string>
|
||||
<string name="crash_explain">上次应用崩溃了。你愿意把错误日志发送给开发者以便以后得到更好的用户体验么?(以电子邮件的形式发送)</string>
|
||||
|
||||
<!-- Gallery List -->
|
||||
<string name="gallery_list_search_bar_hint">搜索 ExHentai</string>
|
||||
<string name="gallery_list_empty_hit">什么都没有找到</string>
|
||||
|
||||
@@ -83,6 +83,10 @@
|
||||
<string name="analytics_explain">您同意讓開發者收集您的設備信息與使用情況嗎?這將對開發有很大幫助。<br><br>數據統計服務由 Google Analytics 提供,其隱私政策可在 <a href="https://www.google.com/policies/privacy/">https://www.google.com/policies/privacy/</a> 查看。<br><br>若您同意參與數據統計,您的設備信息與應用使用情況將會被記錄,其包括但不限於:Android API 版本、設備型號、語言所在地、本應用版本號、會話時長、應用功能使用次數。開發者承諾以下信息並不會被記錄:電話號碼、電子郵件地址、IMEI。<br><br>在您同意參與數據統計之前,您的信息不會被記錄。<br><br>被記錄的數據會由 Google Analytics 進行分析,分析報表僅可被本應用開發者訪問。</string>
|
||||
<string name="get_it">知道了</string>
|
||||
|
||||
<!-- Crash -->
|
||||
<string name="send">發送</string>
|
||||
<string name="crash_explain">上次應用崩潰了。你願意把錯誤日誌發送給開發者以便以後得到更好的用户體驗麼?(以電子郵件的形式發送)</string>
|
||||
|
||||
<!-- Gallery List -->
|
||||
<string name="gallery_list_search_bar_hint">搜索 ExHentai</string>
|
||||
<string name="gallery_list_empty_hit">什麼都沒有找到</string>
|
||||
|
||||
@@ -83,6 +83,10 @@
|
||||
<string name="analytics_explain">您同意讓開發者收集您的裝置資訊與使用情況嗎?這將對開發有很大幫助。<br><br>資料統計服務由 Google Analytics 提供,其隱私政策可在 <a href="https://www.google.com/policies/privacy/">https://www.google.com/policies/privacy/</a> 檢視。<br><br>若您同意參與資料統計,您的裝置資訊與應用使用情況將會被記錄,其包括但不限於:Android API 版本、裝置型號、語言所在地、本應用版本號、會話時長、應用功能使用次數。開發者承諾以下資訊並不會被記錄:電話號碼、電子郵件地址、IMEI。<br><br>在您同意參與資料統計之前,您的資訊不會被記錄。<br><br>被記錄的資料會由 Google Analytics 進行分析,分析報表僅可被本應用開發者訪問。</string>
|
||||
<string name="get_it">知道了</string>
|
||||
|
||||
<!-- Crash -->
|
||||
<string name="send">傳送</string>
|
||||
<string name="crash_explain">上次應用崩潰了。你願意把錯誤日誌傳送給開發者以便以後得到更好的使用者體驗麼?(以電子郵件的形式傳送)</string>
|
||||
|
||||
<!-- Gallery List -->
|
||||
<string name="gallery_list_search_bar_hint">搜尋 ExHentai</string>
|
||||
<string name="gallery_list_empty_hit">什麼都沒有找到</string>
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
<string name="pd_download_box" translatable="false">M19,3H5C3.9,3,3,3.9,3,5V19C3,20.1,3.9,21,5,21H19C20.1,21,21,20.1,21,19V5C21,3.9,20.1,3,19,3ZM12,17L7,12H10V8H14V12H17L12,17Z</string>
|
||||
<string name="pd_chart" translatable="false">M16,11.78L20.24,4.45L21.97,5.45L16.74,14.5L10.23,10.75L5.46,19H22V21H2V3H4V17.54L9.5,8L16,11.78Z</string>
|
||||
<string name="pd_cookie" translatable="false">M12,3A9,9 0 0,0 3,12A9,9 0 0,0 12,21A9,9 0 0,0 21,12C21,11.5 20.96,11 20.87,10.5C20.6,10 20,10 20,10H18V9C18,8 17,8 17,8H15V7C15,6 14,6 14,6H13V4C13,3 12,3 12,3M9.5,6A1.5,1.5 0 0,1 11,7.5A1.5,1.5 0 0,1 9.5,9A1.5,1.5 0 0,1 8,7.5A1.5,1.5 0 0,1 9.5,6M6.5,10A1.5,1.5 0 0,1 8,11.5A1.5,1.5 0 0,1 6.5,13A1.5,1.5 0 0,1 5,11.5A1.5,1.5 0 0,1 6.5,10M11.5,11A1.5,1.5 0 0,1 13,12.5A1.5,1.5 0 0,1 11.5,14A1.5,1.5 0 0,1 10,12.5A1.5,1.5 0 0,1 11.5,11M16.5,13A1.5,1.5 0 0,1 18,14.5A1.5,1.5 0 0,1 16.5,16H16.5A1.5,1.5 0 0,1 15,14.5H15A1.5,1.5 0 0,1 16.5,13M11,16A1.5,1.5 0 0,1 12.5,17.5A1.5,1.5 0 0,1 11,19A1.5,1.5 0 0,1 9.5,17.5A1.5,1.5 0 0,1 11,16Z</string>
|
||||
<string name="pd_bug" translatable="false">M14,12H10V10H14M14,16H10V14H14M20,8H17.19C16.74,7.22 16.12,6.55 15.37,6.04L17,4.41L15.59,3L13.42,5.17C12.96,5.06 12.5,5 12,5C11.5,5 11.04,5.06 10.59,5.17L8.41,3L7,4.41L8.62,6.04C7.88,6.55 7.26,7.22 6.81,8H4V10H6.09C6.04,10.33 6,10.66 6,11V12H4V14H6V15C6,15.34 6.04,15.67 6.09,16H4V18H6.81C7.85,19.79 9.78,21 12,21C14.22,21 16.15,19.79 17.19,18H20V16H17.91C17.96,15.67 18,15.34 18,15V14H20V12H18V11C18,10.66 17.96,10.33 17.91,10H20V8Z</string>
|
||||
|
||||
<string name="pd_big_label" translatable="false">M12.88,2H4V11L14.97,22.14L24,13.1L12.88,2ZM7.26,5.26C7.96,4.58,9.06,4.58,9.74,5.26C10.42,5.96,10.42,7.06,9.74,7.74S7.94,8.42,7.26,7.74S6.58,5.94,7.26,5.26ZM14.98,19.3L8.76,12.95L14.88,6.83L21.18,13.11L14.98,19.31ZM11.6,21.56L10.23,23L0,12.74V5H2V11.9L11.6,21.56Z</string>
|
||||
<string name="pd_big_search" translatable="false">M20.46,14.65C21.86,16.15,20.31,18.6,18.32,17.87C19.78,17.57,20.79,16.37,20.46,14.65ZM4.32,17.87C6.32,18.6,7.86,16.17,6.46,14.65C6.79,16.38,5.78,17.55,4.32,17.87ZM16.87,4.3C16.6,3.56,15.87,3,15,3C13.7,3,12.7,4.24,13.1,5.58C14.2,5.18,15.44,4.48,16.87,4.31ZM24,16C24,18.76,21.76,21,19,21C14.96,21,14.84,16.88,12,16.88S9.04,21,5,21C2.24,21,0,18.76,0,16C0,13.87,1.53,11.43,3.94,7.18C5.84,3.81,8.69,5.92,10.76,6.58C11.54,6.85,12.41,6.88,13.24,6.58C15.3,5.92,18.14,3.81,20.06,7.18C22.46,11.43,24,13.88,24,16ZM8,16C8,14.35,6.65,13,5,13S2,14.35,2,16S3.35,19,5,19S8,17.65,8,16ZM13,14C13,13.45,12.55,13,12,13S11,13.45,11,14S11.45,15,12,15S13,14.55,13,14ZM22,16C22,14.35,20.65,13,19,13S16,14.35,16,16S17.35,19,19,19S22,17.65,22,16ZM9,3C8.14,3,7.4,3.55,7.13,4.3C8.56,4.48,9.79,5.2,10.9,5.58C11.3,4.24,10.3,3,9,3Z</string>
|
||||
|
||||
@@ -122,6 +122,10 @@
|
||||
<string name="analytics_explain">Would you mind if the developer collected your device information and your usage? It will help the developer a lot.<br><br>Data analysis service is provided by Google Analytics. Its privacy policy can be found at <a href="https://www.google.com/policies/privacy/">https://www.google.com/policies/privacy/</a>.<br><br>If you agree to join data analysis, your device information and your usage will be collected, including but not limited to Android API version, equipment model, language residence, the application version, conversion time, function times. The developer promise that these information will NOT be collected, including phone number, e-mail address, IMEI.<br><br>Your information will not be collected until you agree to join data analysis.<br><br>The collected information will be analyzed by Google Analytics. The analysis report can only be viewed by the developer of this application.</string>
|
||||
<string name="get_it">Got it</string>
|
||||
|
||||
<!-- Crash -->
|
||||
<string name="send">Send</string>
|
||||
<string name="crash_explain">This application crashed last time. Would you mind to send logcat to the developer for a better user experience in future ? (via e-mail)</string>
|
||||
|
||||
<!-- Gallery List -->
|
||||
<string name="gallery_list_search_bar_hint">Search ExHentai</string>
|
||||
<string name="gallery_list_empty_hit">The World is Big and Pandas sit alone</string>
|
||||
|
||||
Reference in new issue
Block a user