diff --git a/app/build.gradle b/app/build.gradle index 74ef05126..f39847d36 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -69,7 +69,7 @@ dependencies { compile project(':rippleold') compile project(':unifile') compile project(':yorozuya') - compile fileTree(dir: 'libs', include: ['*.jar']) + compile fileTree(include: ['*.jar'], dir: 'libs') testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.2.0' compile 'com.android.support:customtabs:23.2.0' diff --git a/app/libs/libGoogleAnalyticsServices.jar b/app/libs/libGoogleAnalyticsServices.jar new file mode 100644 index 000000000..082a9180e Binary files /dev/null and b/app/libs/libGoogleAnalyticsServices.jar differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 05e55ee2a..cd4a972b6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -20,6 +20,7 @@ xmlns:android="http://schemas.android.com/apk/res/android"> + diff --git a/app/src/main/java/com/hippo/ehviewer/Analytics.java b/app/src/main/java/com/hippo/ehviewer/Analytics.java new file mode 100644 index 000000000..561eeb012 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/Analytics.java @@ -0,0 +1,31 @@ +/* + * 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 com.google.analytics.tracking.android.EasyTracker; + + +public final class Analytics { + + private Analytics() {} + + public static void start(Context context) { + EasyTracker.getInstance(context).set("&uid", Settings.getUserID()); + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/AppConfig.java b/app/src/main/java/com/hippo/ehviewer/AppConfig.java index 038ba91c6..9dbacec14 100644 --- a/app/src/main/java/com/hippo/ehviewer/AppConfig.java +++ b/app/src/main/java/com/hippo/ehviewer/AppConfig.java @@ -64,6 +64,16 @@ public class AppConfig { return null; } + @Nullable + public static File getFileInAppDir(String filename) { + File appFolder = getExternalAppDir(); + if (appFolder != null) { + File file = new File(appFolder, filename); + return FileUtils.ensureFile(file) ? file : null; + } + return null; + } + @Nullable public static File getDefaultDownloadDir() { return getDirInAppDir(DOWNLOAD); diff --git a/app/src/main/java/com/hippo/ehviewer/EhApplication.java b/app/src/main/java/com/hippo/ehviewer/EhApplication.java index 10e720f08..96b36854b 100644 --- a/app/src/main/java/com/hippo/ehviewer/EhApplication.java +++ b/app/src/main/java/com/hippo/ehviewer/EhApplication.java @@ -92,6 +92,10 @@ public class EhApplication extends SceneApplication { EhDB.mergeOldDB(this); } + if (Settings.getEnableAnalytics()) { + Analytics.start(this); + } + if (DEBUG_NATIVE_MEMORY) { debugNativeMemory(); } diff --git a/app/src/main/java/com/hippo/ehviewer/Settings.java b/app/src/main/java/com/hippo/ehviewer/Settings.java index 3a26eeb07..93590ad71 100644 --- a/app/src/main/java/com/hippo/ehviewer/Settings.java +++ b/app/src/main/java/com/hippo/ehviewer/Settings.java @@ -29,8 +29,12 @@ import com.hippo.ehviewer.gallery.gl.GalleryView; import com.hippo.ehviewer.gallery.gl.ImageView; import com.hippo.unifile.UniFile; import com.hippo.yorozuya.AssertUtils; +import com.hippo.yorozuya.FileUtils; +import com.hippo.yorozuya.MathUtils; import com.hippo.yorozuya.NumberUtils; +import java.io.File; + public class Settings { private static Context sContext; @@ -373,4 +377,91 @@ public class Settings { public static void putDefaultFavSlot(int value) { putInt(KEY_DEFAULT_FAV_SLOT, value); } + + /******************** + ****** Analytics + ********************/ + private static final String KEY_ASK_ANALYTICS = "ask_analytics"; + private static final boolean DEFAULT_ASK_ANALYTICS = true; + + public static boolean getAskAnalytics() { + return getBoolean(KEY_ASK_ANALYTICS, DEFAULT_ASK_ANALYTICS); + } + + public static void putAskAnalytics(boolean value) { + putBoolean(KEY_ASK_ANALYTICS, value); + } + + private static final String KEY_ENABLE_ANALYTICS = "enable_analytics"; + private static final boolean DEFAULT_ENABLE_ANALYTICS = false; + + public static boolean getEnableAnalytics() { + return getBoolean(KEY_ENABLE_ANALYTICS, DEFAULT_ENABLE_ANALYTICS); + } + + public static void putEnableAnalytics(boolean value) { + putBoolean(KEY_ENABLE_ANALYTICS, value); + } + + private static final String KEY_USER_ID = "user_id"; + private static final String FILENAME_USER_ID = ".user_id"; + private static final int LENGTH_USER_ID = 32; + + public static String getUserID() { + boolean writeXml = false; + boolean writeFile = false; + String userID = getString(KEY_USER_ID, null); + File file = AppConfig.getFileInAppDir(FILENAME_USER_ID); + if (null == userID || !isValidUserID(userID)) { + writeXml = true; + // Get use ID from out sd card file + userID = FileUtils.read(file); + if (null == userID || !isValidUserID(userID)) { + writeFile = true; + userID = generateUserID(); + } + } else { + writeFile = true; + } + + if (writeXml) { + putString(KEY_USER_ID, userID); + } + if (writeFile) { + FileUtils.write(file, userID); + } + + return userID; + } + + @NonNull + private static String generateUserID() { + int length = LENGTH_USER_ID; + StringBuilder sb = new StringBuilder(length); + + for (int i = 0; i < length; i++) { + if (MathUtils.random(0, ('9' - '0' + 1) + ('z' - 'a' + 1)) <= '9' - '0') { + sb.append((char) MathUtils.random('0', '9' + 1)); + } else { + sb.append((char) MathUtils.random('a', 'z' + 1)); + } + } + + return sb.toString(); + } + + private static boolean isValidUserID(@Nullable String userID) { + if (null == userID || LENGTH_USER_ID != userID.length()) { + return false; + } + + for (int i = 0; i < LENGTH_USER_ID; i++) { + char ch = userID.charAt(i); + if (!(ch >= '0' && ch <= '9') && !(ch >= 'a' && ch <= 'z')) { + return false; + } + } + + return true; + } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java index bd7e0a176..9e4969ad6 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java @@ -25,7 +25,6 @@ import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.app.AlertDialog; -import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.SwitchCompat; import android.view.KeyEvent; import android.view.View; @@ -56,7 +55,7 @@ import com.hippo.yorozuya.SimpleHandler; import java.io.File; -public class GalleryActivity extends AppCompatActivity +public class GalleryActivity extends TrackedActivity implements GalleryProviderListener, Slider.OnSetProgressListener, GalleryView.Listener { diff --git a/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.java index bbf1aca2a..8ceda8838 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.java @@ -26,6 +26,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.design.widget.NavigationView; import android.support.v4.widget.DrawerLayout; +import android.util.Log; import android.view.Gravity; import android.view.MenuItem; import android.view.View; @@ -37,6 +38,7 @@ import com.hippo.ehviewer.Settings; import com.hippo.ehviewer.client.EhUrlOpener; import com.hippo.ehviewer.client.EhUtils; import com.hippo.ehviewer.ui.annotation.WholeLifeCircle; +import com.hippo.ehviewer.ui.scene.AnalyticsScene; import com.hippo.ehviewer.ui.scene.BaseScene; import com.hippo.ehviewer.ui.scene.DownloadLabelScene; import com.hippo.ehviewer.ui.scene.DownloadsScene; @@ -58,10 +60,19 @@ import com.hippo.yorozuya.ViewUtils; public final class MainActivity extends StageActivity implements NavigationView.OnNavigationItemSelectedListener { + private static final String TAG = MainActivity.class.getSimpleName(); + private static final int PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE = 0; private static final String KEY_NAV_CHECKED_ITEM = "nav_checked_item"; + 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 String KEY_TARGET_SCENE = "target_scene"; + public static final String KEY_TARGET_ARGS = "target_args"; + @Nullable @WholeLifeCircle private DrawerLayout mDrawerLayout; @@ -76,6 +87,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(LoginScene.class, SceneFragment.LAUNCH_MODE_SINGLE_TASK); registerLaunchMode(GalleryListScene.class, SceneFragment.LAUNCH_MODE_SINGLE_TOP); registerLaunchMode(GalleryDetailScene.class, SceneFragment.LAUNCH_MODE_STANDARD); @@ -88,6 +100,46 @@ public final class MainActivity extends StageActivity registerLaunchMode(ProgressScene.class, SceneFragment.LAUNCH_MODE_STANDARD); } + public void startSceneForCheckStep(int checkStep, Bundle args) { + switch (checkStep) { + case CHECK_STEP_WARNING: + if (Settings.getAskAnalytics()) { + startScene(new Announcer(AnalyticsScene.class).setArgs(args)); + break; + } + case CHECK_STEP_ANALYTICS: + if (!EhUtils.hasSignedIn(this)) { + startScene(new Announcer(LoginScene.class).setArgs(args)); + break; + } + case CHECK_STEP_SIGN_IN: + String targetScene = null; + Bundle targetArgs = null; + if (null != args) { + targetScene = args.getString(KEY_TARGET_SCENE); + targetArgs = args.getBundle(KEY_TARGET_ARGS); + } + + Class clazz = null; + if (targetScene != null) { + try { + clazz = Class.forName(targetScene); + } catch (ClassNotFoundException e) { + Log.e(TAG, "Can't find class with name: " + targetScene); + } + } + + if (clazz != null) { + startScene(new Announcer(clazz).setArgs(targetArgs)); + } else { + Bundle newArgs = new Bundle(); + newArgs.putString(GalleryListScene.KEY_ACTION, GalleryListScene.ACTION_HOMEPAGE); + startScene(new Announcer(GalleryListScene.class).setArgs(newArgs)); + } + break; + } + } + @Override public int getContainerViewId() { return R.id.fragment_container; @@ -98,6 +150,8 @@ public final class MainActivity extends StageActivity protected Announcer getLaunchAnnouncer() { if (Settings.getShowWarning()) { return new Announcer(WarningScene.class); + } else if (Settings.getAskAnalytics()) { + return new Announcer(AnalyticsScene.class); } else if (!EhUtils.hasSignedIn(this)) { return new Announcer(LoginScene.class); } else { @@ -112,13 +166,18 @@ public final class MainActivity extends StageActivity if (0 == getSceneCount()) { if (Settings.getShowWarning()) { Bundle newArgs = new Bundle(); - newArgs.putString(WarningScene.KEY_TARGET_SCENE, announcer.getClazz().getName()); - newArgs.putBundle(WarningScene.KEY_TARGET_ARGS, announcer.getArgs()); + newArgs.putString(KEY_TARGET_SCENE, announcer.getClazz().getName()); + newArgs.putBundle(KEY_TARGET_ARGS, announcer.getArgs()); return new Announcer(WarningScene.class).setArgs(newArgs); + } else if (Settings.getAskAnalytics()) { + Bundle newArgs = new Bundle(); + newArgs.putString(KEY_TARGET_SCENE, announcer.getClazz().getName()); + newArgs.putBundle(KEY_TARGET_ARGS, announcer.getArgs()); + return new Announcer(AnalyticsScene.class).setArgs(newArgs); } else if (!EhUtils.hasSignedIn(this)) { Bundle newArgs = new Bundle(); - newArgs.putString(LoginScene.KEY_TARGET_SCENE, announcer.getClazz().getName()); - newArgs.putBundle(LoginScene.KEY_TARGET_ARGS, announcer.getArgs()); + newArgs.putString(KEY_TARGET_SCENE, announcer.getClazz().getName()); + newArgs.putBundle(KEY_TARGET_ARGS, announcer.getArgs()); return new Announcer(LoginScene.class).setArgs(newArgs); } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/ToolbarActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/ToolbarActivity.java index b34e545dc..9409276b5 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/ToolbarActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/ToolbarActivity.java @@ -20,14 +20,13 @@ import android.graphics.drawable.Drawable; import android.support.annotation.DrawableRes; import android.support.annotation.LayoutRes; import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.View; import android.view.ViewGroup; import com.hippo.ehviewer.R; -public class ToolbarActivity extends AppCompatActivity{ +public class ToolbarActivity extends TrackedActivity { @Override public void setContentView(@LayoutRes int layoutResID) { diff --git a/app/src/main/java/com/hippo/ehviewer/ui/TrackedActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/TrackedActivity.java new file mode 100644 index 000000000..c952d27a5 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/ui/TrackedActivity.java @@ -0,0 +1,47 @@ +/* + * Copyright 2016 Hippo Seven + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hippo.ehviewer.ui; + +import android.support.v7.app.AppCompatActivity; + +import com.google.analytics.tracking.android.EasyTracker; +import com.hippo.ehviewer.Settings; + +public class TrackedActivity extends AppCompatActivity { + + private boolean mTrackStarted; + + @Override + public void onStart() { + super.onStart(); + + if (Settings.getEnableAnalytics()) { + EasyTracker.getInstance(this).activityStart(this); + mTrackStarted = true; + } + } + + @Override + public void onStop() { + super.onStop(); + + if (mTrackStarted) { + EasyTracker.getInstance(this).activityStop(this); + mTrackStarted = false; + } + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/ui/TrackedPreferenceActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/TrackedPreferenceActivity.java new file mode 100644 index 000000000..af7ed9e15 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/ui/TrackedPreferenceActivity.java @@ -0,0 +1,46 @@ +/* + * Copyright 2016 Hippo Seven + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hippo.ehviewer.ui; + +import com.google.analytics.tracking.android.EasyTracker; +import com.hippo.app.AppCompatPreferenceActivity; +import com.hippo.ehviewer.Settings; + +public class TrackedPreferenceActivity extends AppCompatPreferenceActivity { + + private boolean mTrackStarted; + + @Override + public void onStart() { + super.onStart(); + + if (Settings.getEnableAnalytics()) { + EasyTracker.getInstance(this).activityStart(this); + mTrackStarted = true; + } + } + + @Override + public void onStop() { + super.onStop(); + + if (mTrackStarted) { + EasyTracker.getInstance(this).activityStop(this); + mTrackStarted = false; + } + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/AnalyticsScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/AnalyticsScene.java new file mode 100644 index 000000000..2fa408d97 --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/AnalyticsScene.java @@ -0,0 +1,96 @@ +/* + * 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 android.widget.TextView; + +import com.hippo.ehviewer.Analytics; +import com.hippo.ehviewer.R; +import com.hippo.ehviewer.Settings; +import com.hippo.ehviewer.ui.MainActivity; +import com.hippo.ehviewer.ui.annotation.ViewLifeCircle; +import com.hippo.rippleold.RippleSalon; +import com.hippo.text.Html; +import com.hippo.text.LinkMovementMethod2; +import com.hippo.yorozuya.ViewUtils; + +public class AnalyticsScene extends BaseScene implements View.OnClickListener { + + @Nullable + @ViewLifeCircle + private View mCancel; + @Nullable + @ViewLifeCircle + private View mOk; + + @Override + public boolean needShowLeftDrawer() { + return false; + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.scene_analytics, container, false); + + mCancel = ViewUtils.$$(view, R.id.cancel); + mOk = ViewUtils.$$(view, R.id.ok); + TextView text = (TextView) ViewUtils.$$(view, R.id.text); + + text.setText(Html.fromHtml(getString(R.string.analytics_explain))); + text.setMovementMethod(new LinkMovementMethod2(getActivity())); + + mCancel.setOnClickListener(this); + mOk.setOnClickListener(this); + + RippleSalon.addRipple(mCancel, true); + RippleSalon.addRipple(mOk, true); + + return view; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + + mCancel = null; + mOk = null; + } + + @Override + public void onClick(View v) { + if (mCancel == v) { + Settings.putEnableAnalytics(false); + } else if (mOk == v) { + Settings.putEnableAnalytics(true); + // Start Analytics + Analytics.start(getContext()); + } + Settings.putAskAnalytics(false); + + // Start new scene and finish it self + ((MainActivity) getActivity()).startSceneForCheckStep( + MainActivity.CHECK_STEP_ANALYTICS, getArguments()); + finish(); + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/LoginScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/LoginScene.java index ccbc1d06b..60ee12972 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/LoginScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/LoginScene.java @@ -23,7 +23,6 @@ import android.os.Bundle; import android.support.annotation.Nullable; import android.support.design.widget.TextInputLayout; import android.support.v7.app.AlertDialog; -import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; @@ -40,8 +39,8 @@ import com.hippo.ehviewer.UrlOpener; import com.hippo.ehviewer.client.EhClient; import com.hippo.ehviewer.client.EhRequest; import com.hippo.ehviewer.client.EhUrl; +import com.hippo.ehviewer.ui.MainActivity; import com.hippo.rippleold.RippleSalon; -import com.hippo.scene.Announcer; import com.hippo.scene.SceneFragment; import com.hippo.scene.StageActivity; import com.hippo.util.ActivityHelper; @@ -52,9 +51,6 @@ public final class LoginScene extends BaseScene implements EditText.OnEditorActi private static final String TAG = LoginScene.class.getSimpleName(); - public static final String KEY_TARGET_SCENE = "target_scene"; - public static final String KEY_TARGET_ARGS = "target_args"; - private static final String KEY_REQUEST_ID = "request_id"; private View mProgress; @@ -216,32 +212,8 @@ public final class LoginScene extends BaseScene implements EditText.OnEditorActi } private void redirectTo() { - String targetScene = null; - Bundle targetArgs = null; - - Bundle args = getArguments(); - if (null != args) { - targetScene = args.getString(KEY_TARGET_SCENE); - targetArgs = args.getBundle(KEY_TARGET_ARGS); - } - - Class clazz = null; - if (targetScene != null) { - try { - clazz = Class.forName(targetScene); - } catch (ClassNotFoundException e) { - Log.e(TAG, "Can't find class with name: " + targetScene); - } - } - - if (clazz != null) { - startScene(new Announcer(clazz).setArgs(targetArgs)); - } else { - Bundle newArgs = new Bundle(); - newArgs.putString(GalleryListScene.KEY_ACTION, GalleryListScene.ACTION_HOMEPAGE); - startScene(new Announcer(GalleryListScene.class).setArgs(newArgs)); - } - + ((MainActivity) getActivity()).startSceneForCheckStep( + MainActivity.CHECK_STEP_SIGN_IN, getArguments()); finish(); } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/WarningScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/WarningScene.java index 1a91eedd8..9efe5f89a 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/WarningScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/WarningScene.java @@ -18,26 +18,21 @@ package com.hippo.ehviewer.ui.scene; import android.os.Bundle; import android.support.annotation.Nullable; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.hippo.ehviewer.R; import com.hippo.ehviewer.Settings; -import com.hippo.ehviewer.client.EhUtils; +import com.hippo.ehviewer.ui.MainActivity; import com.hippo.ehviewer.ui.annotation.ViewLifeCircle; import com.hippo.rippleold.RippleSalon; -import com.hippo.scene.Announcer; import com.hippo.yorozuya.ViewUtils; public final class WarningScene extends BaseScene implements View.OnClickListener { private static final String TAG = WarningScene.class.getSimpleName(); - public static final String KEY_TARGET_SCENE = "target_scene"; - public static final String KEY_TARGET_ARGS = "target_args"; - @Nullable @ViewLifeCircle private View mCancel; @@ -84,47 +79,10 @@ public final class WarningScene extends BaseScene implements View.OnClickListene // Never show this warning anymore Settings.putShowWarning(false); - redirectTo(); + // Start new scene and finish it self + ((MainActivity) getActivity()).startSceneForCheckStep( + MainActivity.CHECK_STEP_WARNING, getArguments()); + finish(); } } - - public void redirectTo() { - String targetScene = null; - Bundle targetArgs = null; - - Bundle args = getArguments(); - if (null != args) { - targetScene = args.getString(KEY_TARGET_SCENE); - targetArgs = args.getBundle(KEY_TARGET_ARGS); - } - - if (EhUtils.hasSignedIn(getContext())) { - Class clazz = null; - if (targetScene != null) { - try { - clazz = Class.forName(targetScene); - } catch (ClassNotFoundException e) { - Log.e(TAG, "Can't find class with name: " + targetScene); - } - } - - if (clazz != null) { - startScene(new Announcer(clazz).setArgs(targetArgs)); - } else { - Bundle newArgs = new Bundle(); - newArgs.putString(GalleryListScene.KEY_ACTION, GalleryListScene.ACTION_HOMEPAGE); - startScene(new Announcer(GalleryListScene.class).setArgs(newArgs)); - } - } else { - Bundle newArgs = null; - if (null != targetScene) { - newArgs = new Bundle(); - newArgs.putString(LoginScene.KEY_TARGET_SCENE, targetScene); - newArgs.putBundle(LoginScene.KEY_TARGET_ARGS, targetArgs); - } - startScene(new Announcer(LoginScene.class).setArgs(newArgs)); - } - - finish(); - } } diff --git a/app/src/main/java/com/hippo/scene/StageActivity.java b/app/src/main/java/com/hippo/scene/StageActivity.java index 2a877304b..47963b569 100644 --- a/app/src/main/java/com/hippo/scene/StageActivity.java +++ b/app/src/main/java/com/hippo/scene/StageActivity.java @@ -23,10 +23,10 @@ import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; -import android.support.v7.app.AppCompatActivity; import android.util.Log; import com.hippo.ehviewer.R; +import com.hippo.ehviewer.ui.TrackedActivity; import com.hippo.yorozuya.AssertUtils; import com.hippo.yorozuya.IntIdGenerator; @@ -35,7 +35,7 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; -public abstract class StageActivity extends AppCompatActivity { +public abstract class StageActivity extends TrackedActivity { private static final String TAG = StageActivity.class.getSimpleName(); diff --git a/app/src/main/java/com/hippo/text/LinkMovementMethod2.java b/app/src/main/java/com/hippo/text/LinkMovementMethod2.java new file mode 100644 index 000000000..498172914 --- /dev/null +++ b/app/src/main/java/com/hippo/text/LinkMovementMethod2.java @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2006 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.text; + +import android.app.Activity; +import android.text.Layout; +import android.text.NoCopySpan; +import android.text.Selection; +import android.text.Spannable; +import android.text.method.ScrollingMovementMethod; +import android.text.style.ClickableSpan; +import android.text.style.URLSpan; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; +import android.widget.TextView; + +import com.hippo.ehviewer.UrlOpener; + +/** + * A movement method that traverses links in the text buffer and scrolls if necessary. + * Supports clicking on links with DPad Center or Enter. + */ +public class LinkMovementMethod2 extends ScrollingMovementMethod { + private static final int CLICK = 1; + private static final int UP = 2; + private static final int DOWN = 3; + + private final Activity mActivity; + + public LinkMovementMethod2(Activity activity) { + mActivity = activity; + } + + @Override + public boolean canSelectArbitrarily() { + return true; + } + + @Override + protected boolean handleMovementKey(TextView widget, Spannable buffer, int keyCode, + int movementMetaState, KeyEvent event) { + switch (keyCode) { + case KeyEvent.KEYCODE_DPAD_CENTER: + case KeyEvent.KEYCODE_ENTER: + if (KeyEvent.metaStateHasNoModifiers(movementMetaState)) { + if (event.getAction() == KeyEvent.ACTION_DOWN && + event.getRepeatCount() == 0 && action(CLICK, widget, buffer)) { + return true; + } + } + break; + } + return super.handleMovementKey(widget, buffer, keyCode, movementMetaState, event); + } + + @Override + protected boolean up(TextView widget, Spannable buffer) { + if (action(UP, widget, buffer)) { + return true; + } + + return super.up(widget, buffer); + } + + @Override + protected boolean down(TextView widget, Spannable buffer) { + if (action(DOWN, widget, buffer)) { + return true; + } + + return super.down(widget, buffer); + } + + @Override + protected boolean left(TextView widget, Spannable buffer) { + if (action(UP, widget, buffer)) { + return true; + } + + return super.left(widget, buffer); + } + + @Override + protected boolean right(TextView widget, Spannable buffer) { + if (action(DOWN, widget, buffer)) { + return true; + } + + return super.right(widget, buffer); + } + + private boolean action(int what, TextView widget, Spannable buffer) { + Layout layout = widget.getLayout(); + + int padding = widget.getTotalPaddingTop() + + widget.getTotalPaddingBottom(); + int areatop = widget.getScrollY(); + int areabot = areatop + widget.getHeight() - padding; + + int linetop = layout.getLineForVertical(areatop); + int linebot = layout.getLineForVertical(areabot); + + int first = layout.getLineStart(linetop); + int last = layout.getLineEnd(linebot); + + ClickableSpan[] candidates = buffer.getSpans(first, last, ClickableSpan.class); + + int a = Selection.getSelectionStart(buffer); + int b = Selection.getSelectionEnd(buffer); + + int selStart = Math.min(a, b); + int selEnd = Math.max(a, b); + + if (selStart < 0) { + if (buffer.getSpanStart(FROM_BELOW) >= 0) { + selStart = selEnd = buffer.length(); + } + } + + if (selStart > last) + selStart = selEnd = Integer.MAX_VALUE; + if (selEnd < first) + selStart = selEnd = -1; + + switch (what) { + case CLICK: + if (selStart == selEnd) { + return false; + } + + ClickableSpan[] link = buffer.getSpans(selStart, selEnd, ClickableSpan.class); + + if (link.length != 1) + return false; + + link[0].onClick(widget); + break; + + case UP: + int beststart, bestend; + + beststart = -1; + bestend = -1; + + for (int i = 0; i < candidates.length; i++) { + int end = buffer.getSpanEnd(candidates[i]); + + if (end < selEnd || selStart == selEnd) { + if (end > bestend) { + beststart = buffer.getSpanStart(candidates[i]); + bestend = end; + } + } + } + + if (beststart >= 0) { + Selection.setSelection(buffer, bestend, beststart); + return true; + } + + break; + + case DOWN: + beststart = Integer.MAX_VALUE; + bestend = Integer.MAX_VALUE; + + for (int i = 0; i < candidates.length; i++) { + int start = buffer.getSpanStart(candidates[i]); + + if (start > selStart || selStart == selEnd) { + if (start < beststart) { + beststart = start; + bestend = buffer.getSpanEnd(candidates[i]); + } + } + } + + if (bestend < Integer.MAX_VALUE) { + Selection.setSelection(buffer, beststart, bestend); + return true; + } + + break; + } + + return false; + } + + @Override + public boolean onTouchEvent(TextView widget, Spannable buffer, + MotionEvent event) { + int action = event.getAction(); + + if (action == MotionEvent.ACTION_UP || + action == MotionEvent.ACTION_DOWN) { + int x = (int) event.getX(); + int y = (int) event.getY(); + + x -= widget.getTotalPaddingLeft(); + y -= widget.getTotalPaddingTop(); + + x += widget.getScrollX(); + y += widget.getScrollY(); + + Layout layout = widget.getLayout(); + int line = layout.getLineForVertical(y); + int off = layout.getOffsetForHorizontal(line, x); + + ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class); + + if (link.length != 0) { + if (action == MotionEvent.ACTION_UP) { + ClickableSpan span = link[0]; + if (span instanceof URLSpan) { + UrlOpener.openUrl(mActivity, ((URLSpan) span).getURL(), true, true); + } else { + span.onClick(widget); + } + } else if (action == MotionEvent.ACTION_DOWN) { + Selection.setSelection(buffer, + buffer.getSpanStart(link[0]), + buffer.getSpanEnd(link[0])); + } + + return true; + } else { + Selection.removeSelection(buffer); + } + } + + return super.onTouchEvent(widget, buffer, event); + } + + @Override + public void initialize(TextView widget, Spannable text) { + Selection.removeSelection(text); + text.removeSpan(FROM_BELOW); + } + + @Override + public void onTakeFocus(TextView view, Spannable text, int dir) { + Selection.removeSelection(text); + + if ((dir & View.FOCUS_BACKWARD) != 0) { + text.setSpan(FROM_BELOW, 0, 0, Spannable.SPAN_POINT_POINT); + } else { + text.removeSpan(FROM_BELOW); + } + } + + private static final Object FROM_BELOW = new NoCopySpan.Concrete(); +} diff --git a/app/src/main/res/drawable/v_chart_accent_x48.xml b/app/src/main/res/drawable/v_chart_accent_x48.xml new file mode 100644 index 000000000..8f7c8fa33 --- /dev/null +++ b/app/src/main/res/drawable/v_chart_accent_x48.xml @@ -0,0 +1,29 @@ + + + + + + + + diff --git a/app/src/main/res/layout/scene_analytics.xml b/app/src/main/res/layout/scene_analytics.xml new file mode 100644 index 000000000..944ce066b --- /dev/null +++ b/app/src/main/res/layout/scene_analytics.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 6f1000c2f..cc70aca72 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -65,6 +65,7 @@ 点击重试 EhViewer 将申请设备存储空间访问权限以便下载画廊。你愿意授权吗? 好痛 + 你同意让开发者收集你的设备信息与使用情况吗?这将对开发有很大帮助。<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 进行分析,分析报表仅可被本应用开发者访问。 搜索 ExHentai diff --git a/app/src/main/res/values/analytics.xml b/app/src/main/res/values/analytics.xml new file mode 100644 index 000000000..091cbdd89 --- /dev/null +++ b/app/src/main/res/values/analytics.xml @@ -0,0 +1,24 @@ + + + + + + UA-51287788-1 + true + error + + diff --git a/app/src/main/res/values/pathdata.xml b/app/src/main/res/values/pathdata.xml index 8b3a1f90c..16befd67c 100644 --- a/app/src/main/res/values/pathdata.xml +++ b/app/src/main/res/values/pathdata.xml @@ -51,6 +51,7 @@ M13,9H11V7H13M13,17H11V11H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z M5,3H19A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3M12,17L12.72,16.34C15.3,14 17,12.46 17,10.57C17,9.03 15.79,7.82 14.25,7.82C13.38,7.82 12.55,8.23 12,8.87C11.45,8.23 10.62,7.82 9.75,7.82C8.21,7.82 7,9.03 7,10.57C7,12.46 8.7,14 11.28,16.34L12,17Z 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 + M16,11.78L20.24,4.45L21.97,5.45L16.74,14.5L10.23,10.75L5.46,19H22V21H2V3H4V17.54L9.5,8L16,11.78Z M13,0C20.2,0,26,5.9,26,13.2,26,16.9,24,20.8,18.7,26.1L13,32,7.2,26.2C2,20.8,0,16.9,0,13.3,0,5.9,5.8,0,13,0Z diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f91f42c94..e5fc2da00 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -104,6 +104,7 @@ Click to retry EhViewer will request WRITE_EXTERNAL_STORAGE permission for downloading gallery. Do you want to grant the permission? It hurts + 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. Search ExHentai diff --git a/yorozuya b/yorozuya index 56224dfe3..e1d491a60 160000 --- a/yorozuya +++ b/yorozuya @@ -1 +1 @@ -Subproject commit 56224dfe341dc7de559d375ad69c1a5be9b27f18 +Subproject commit e1d491a60e36e6915b690414beee7b84b8b3aa8f