public class Aequorea extends Application {
    private static Aequorea mApp;
    private static String mCurrentTheme;
    private static ExecutorService mExecutor;
    
    @Override
    public void onCreate() {
        super.onCreate();
        
        mApp = this;
        mCurrentTheme = ThemeHelper.getTheme();
        mExecutor = Executors.newCachedThreadPool();
    
        initCache();
        initFlurry();
    }

    private void initFlurry() {
        new FlurryAgent.Builder()
                .withDataSaleOptOut(false) //CCPA - the default value is false
                .withCaptureUncaughtExceptions(true)
                .withIncludeBackgroundSessionsInMetrics(true)
                .withLogLevel(Log.VERBOSE)
                .withPerformanceMetrics(FlurryPerformance.ALL)
                .build(this, "CXH6FJ9VS4P8K3K8BFN9");
    }
    
    private void initCache() {
        File cacheDir = new File(getCacheDir().getAbsolutePath() + File.separator + Constants.ARTICLE_PIC_CACHE);
        RichText.initCacheDir(cacheDir);
        ArticleCache.getCache();
    }
    
    public static Application getApp() {
        return mApp;
    }
    
    public static String getCurrentTheme() {
        return mCurrentTheme;
    }
    
    public static boolean isLightTheme() {
        return mCurrentTheme.equals(Constants.THEME_LIGHT);
    }
    
    public static void setCurrentTheme(String theme) {
        mCurrentTheme = theme;
    }
    
    public static ExecutorService getExecutor(){
        return mExecutor;
    }
} public class Constants {
    public static final String AUTHOR = "author";
    public static final String TAG = "tag";
    public static final String ARTICLE_ID = "article_id";
    public static final String AUTHOR_ID = "author_id";
    public static final String TAG_ID = "tag_id";
    public static final String SEARCH_KEY = "search_key";
    public static final String PHOTO = "photo";
    public static final String ARG_URL = "arg_url";
    
    // Article Type
    public static final String ARTICLE_TYPE_MAGAZINE = "magazine_article"; // 封面故事
    public static final String ARTICLE_TYPE_MAGAZINE_V2 = "magazine"; // 封面故事
    public static final String ARTICLE_TYPE_NORMAL = "normal_article"; // 一般文章
    public static final String ARTICLE_TYPE_NORMAL_V2 = "normal"; // 一般文章
    public static final String ARTICLE_TYPE_THEME = "theme"; // 专题
    public static final String ARTICLE_TYPE_TOP_ARTICLE = "top_article"; // 头条故事
    public static final String ARTICLE_TYPE_SUBJECT = "subject"; // 单行本


    public static final int TYPE_MAGAZINE = 1;
    public static final int TYPE_NORMAL = 2;
    public static final int TYPE_THEME = 3;
    public static final int TYPE_TOP_ARTICLE = 4;
    public static final int TYPE_SUBJECT = 5;
    
    // Theme
    public static final String THEME = "theme";
    public static final String THEME_LIGHT = "theme_light";
    public static final String THEME_DARK = "theme_dark";
    
    // Trigger
    public static final int AUTO_LOAD_TRIGGER = 30;
    
    // Key of SharePreference
    public static final String SP_LATEST_MAIN_PAGE = "sp_latest_main_page";
    public static final String SP_HD_SCREENSHOT = "sp_hd_screenshot";
    public static final String SP_ENABLE_SELECTION = "sp_enable_selection";
    public static final String SP_OFFLINE_CACHE = "sp_offline_cache";
    public static final String SP_DISABLE_RECOMMEND_ARTICLE = "sp_disable_recommend_article";
    
    // Cache dir
    public static final String ARTICLE_CACHE = "article_cache";
    public static final String ARTICLE_PIC_CACHE = "article_pic_cache";
} public class RxBus {
    private final Subject<Object> mBus;

    private RxBus() {
        mBus = PublishSubject.create().toSerialized();
    }

    public static RxBus getInstance() {
        return RxBusHolder.RX_BUS_INSTANCE;
    }

    public void post(int type) {
        post(type, null);
    }

    public void post(int type, @Nullable Object data) {
        mBus.onNext(new RxEvent<>(type, data));
    }

    public Observable<Object> toObservable() {
        return mBus;
    }

    public <T> Observable<T> toObservable(Class<T> type) {
        return mBus.ofType(type);
    }

    public <T> Observable<T> toObservable(final int type, final Class<T> clazz) {
        return mBus.ofType(RxEvent.class).filter(new Predicate<RxEvent>() {
            @Override
            public boolean test(@NonNull RxEvent event) throws Exception {
                return event.getType() == type;
            }
        }).cast(clazz).observeOn(AndroidSchedulers.mainThread());
    }

    private static class RxBusHolder {
        private static final RxBus RX_BUS_INSTANCE = new RxBus();
    }
} public class RxEvent<T> {
    public static final int EVENT_TYPE_CHANGE_THEME = 1;

    private int mCode;
    private T mData;

    public RxEvent(int type, T data) {
        mCode = type;
        mData = data;
    }

    public int getType() {
        return mCode;
    }

    public void setType(int mCode) {
        this.mCode = mCode;
    }

    public T getData() {
        return mData;
    }

    public void setData(T data) {
        mData = data;
    }
} public abstract class BaseActivity extends AppCompatActivity implements BaseView {
    public String currentTheme;
    public boolean needToReTheme;
    
    private boolean isForeground;
    
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initTheme();
        
        setContentView(getContentViewId());
        initModel();
        initView();
        initPresenter();
    }
    
    protected void initTheme() {
        currentTheme = ThemeHelper.getTheme();
        setTheme(ThemeHelper.getThemeStyle(currentTheme));
    }
    
    @Override
    protected void onResume() {
        super.onResume();
        
        isForeground = true;
        if (needToReTheme) {
            onThemeSwitchPending();
        }
    }
    
    @Override
    protected void onPause() {
        super.onPause();
        
        isForeground = false;
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
    }
    
    public void setStatusBarStyle(boolean isLightStatusBar) {
        DisplayUtils.setStatusBarStyle(this, isLightStatusBar);
    }
    
    public void setStatusBarInLowProfileMode(boolean isLightStautsBar) {
        DisplayUtils.setStatusInLowProfileMode(this, isLightStautsBar);
    }
    
    public int dp2px(int dp) {
        return DisplayUtils.dp2px(this, dp);
    }
    
    protected abstract int getContentViewId();
    
    protected abstract void initModel();
    
    protected abstract void initView();
    
    protected abstract void initPresenter();
    
    // For those activity that need to change color when user have changed theme.
    @Override
    public void onThemeSwitch() {
        // do nothing
    }
    
    @Override
    public void onThemeSwitchPending() {
        // do nothing
    }
    
    public boolean isInLightTheme() {
        return currentTheme.equals(Constants.THEME_LIGHT);
    }
    
    public boolean activityInForeground() {
        return isForeground;
    }
    
    @Override
    public void onBackPressed() {
        super.onBackPressed();
        overridePendingTransition(0, R.anim.activity_slide_out_bottom);
    }
} public class ACheckBox extends FrameLayout implements View.OnClickListener {
    private AppCompatCheckBox mCheckBox;
    private TextView mTitle;
    private TextView mSummary;
    private String mKey;
    private OnClickCallback mClickCallback;
    
    public ACheckBox(@NonNull Context context) {
        this(context, null);
    }
    
    public ACheckBox(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }
    
    public ACheckBox(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        
        View.inflate(context, R.layout.layout_custom_checkbox, this);
        init(context, attrs);
        setOnClickListener(this);
    }
    
    private void init(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ACheckBox);
        String title = typedArray.getString(R.styleable.ACheckBox_title);
        String summary = typedArray.getString(R.styleable.ACheckBox_summary);
        
        mCheckBox = findViewById(R.id.cb);
        mTitle = findViewById(R.id.tv_title);
        mSummary =findViewById(R.id.tv_summary);
    
        mTitle.setText(title);
        mSummary.setText(summary);
        
        typedArray.recycle();
    }
    
    @Override
    public void onClick(View view) {
        if (mKey != null) {
            boolean checked = !mCheckBox.isChecked();
            mCheckBox.setChecked(checked);
            if (!mKey.equals(Constants.THEME)) {
                SPUtils.setBoolean(mKey, checked);
            }
            if (mClickCallback != null) {
                mClickCallback.click(checked);
            }
        }
    }
    
    public void setColorStateList(ColorStateList stateList) {
        mCheckBox.setSupportButtonTintList(stateList);
    }
    
    public void setTextColor(int titleColor, int summaryColor) {
        mTitle.setTextColor(titleColor);
        mSummary.setTextColor(summaryColor);
    }
    
    public void initStatus(String key, boolean status) {
        mKey = key;
        mCheckBox.setChecked(status);
    }
    
    public void setOnClickCallback(OnClickCallback callback) {
        mClickCallback = callback;
    }
    
    public interface OnClickCallback {
        void click(boolean status);
    }
} public class StatusBarView extends View {
    
    public static final float LIGHT_INIT_MASK_ALPHA = 0.03f;
    public static final float LIGHT_MASK_ALPHA = 0.06f;
    public static final float DARK_INIT_MASK_ALPHA = 0.2f;
    public static final float DARKER_MASK_ALPHA = 0.2f;
    
    private boolean isInitState;
    private ObjectAnimator objectAnimator;
    
    public StatusBarView(Context context) {
        super(context);
        init(context, null, 0, 0);
    }
    
    public StatusBarView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0, 0);
    }
    
    public StatusBarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr, 0);
    }
    
    public StatusBarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs, defStyleAttr, defStyleRes);
    }
    
    private void init(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.StatusBarView, defStyleAttr, defStyleRes);
        boolean isPlaceHolder = a.getBoolean(R.styleable.StatusBarView_placeholder, false);
        a.recycle();
        
        if (!isPlaceHolder) {
            setBackgroundResource(android.R.color.black);
            setAlpha(getAlphaFromDeviceApi());
        }
    }
    
    private float getAlphaFromDeviceApi() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            return LIGHT_INIT_MASK_ALPHA;
        } else {
            return DARKER_MASK_ALPHA;
        }
    }
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), DisplayUtils.getStatusBarHeight(getResources()));
    }
    
    public void setDarkMask() {
        setInitState(false);
        changeAlpha(DARKER_MASK_ALPHA);
    }
    
    public void setLightMask() {
        setInitState(true);
        changeAlpha(LIGHT_INIT_MASK_ALPHA);
    }
    
    // only available in activity that does not need to change alpha
    public void setLight2ndMask() {
        setInitState(true);
        changeAlpha(LIGHT_MASK_ALPHA);
    }
    
    private void changeAlpha(float alphaTo) {
        cancelAnimator();
        if (getAlpha() != alphaTo) {
            objectAnimator = ObjectAnimator.ofFloat(this, "alpha", getAlpha(), alphaTo)
                .setDuration(150);
            objectAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
            objectAnimator.start();
        }
    }
    
    private void cancelAnimator() {
        if (objectAnimator != null) {
            objectAnimator.cancel();
        }
    }
    
    public void setInitState(boolean isInitState) {
        this.isInitState = isInitState;
    }
    
    public boolean isInitState() {
        return isInitState;
    }
} public class DisplayUtils {
    
    public static int getStatusBarHeight(Resources r) {
        int result = 0;
        int resourceId = r.getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            result = r.getDimensionPixelSize(resourceId);
        }
        return result;
    }
    
    public static void setStatusBarStyle(BaseActivity activity, boolean isLight) {
        boolean isMIUI = setMIUIStatusBarStyle(activity, isLight);

        if (!isMIUI) {
            Window window = activity.getWindow();
            View decor = window.getDecorView();

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && isLight) {
                decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
            } else {
                decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
            }
        }
    }
    
    public static void setStatusInLowProfileMode(BaseActivity activity, boolean isLight) {
        View decor = activity.getWindow().getDecorView();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (isLight) {
                decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR | View.SYSTEM_UI_FLAG_LOW_PROFILE);
            } else {
                decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LOW_PROFILE);
            }
        } else {
            decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LOW_PROFILE);
        }
    }

    private static boolean setMIUIStatusBarStyle(BaseActivity activity, boolean isLight) {
        boolean result = false;
        Window window = activity.getWindow();
        if (window != null) {
            Class clazz = window.getClass();
            try {
                int darkModeFlag = 0;
                Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
                Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
                darkModeFlag = field.getInt(layoutParams);
                Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
                if (isLight) {
                    extraFlagField.invoke(window, darkModeFlag, darkModeFlag);//状态栏透明且黑色字体
                } else {
                    extraFlagField.invoke(window, 0, darkModeFlag);//清除黑色字体
                }
                result = true;

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    //开发版 7.7.13 及以后版本采用了系统API，旧方法无效但不会报错，所以两个方式都要加上
                    if (isLight) {
                        activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
                    } else {
                        activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
                    }
                }
            } catch (Exception e) {
                // do nothing
            }
        }
        return result;
    }
    
    public static void cancelTranslucentNavigation(Activity a) {
        a.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
    }

    public static int dp2px(Context context, int dp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getApplicationContext().getResources().getDisplayMetrics());
    }
    
    public static int sp2px(Context context, int sp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, context.getApplicationContext().getResources().getDisplayMetrics());
    }
    
    // take a screen shot of given nestedScrollView
    public static Bitmap shotNestedScrollView(NestedScrollView nestedScrollView, int backgroundColor) {
    
        int recHeight = 0;
        View childView = nestedScrollView.getChildAt(0);
        View recommendationView = nestedScrollView.findViewById(R.id.container_recommendation);
        ViewGroup subRecommendationView = nestedScrollView.findViewById(R.id.container_recommendation_sub);
        
        if (subRecommendationView != null && subRecommendationView.getChildCount() > 0) {
            recHeight = recommendationView.getHeight();
        }
            
        childView.setBackgroundColor(backgroundColor);
    
        Bitmap bitmap = Bitmap.createBitmap(nestedScrollView.getWidth(), childView.getHeight() - recHeight, Bitmap.Config.RGB_565);
    
        Canvas canvas = new Canvas(bitmap);
        nestedScrollView.draw(canvas);
        
        childView.setBackgroundColor(Color.TRANSPARENT);
    
        return bitmap;
    }
} public class SPUtils {
    private static SharedPreferences mSp;
    
    private static SharedPreferences getSp() {
        if (mSp == null) {
            mSp = Aequorea.getApp().getSharedPreferences("Aequorea", Context.MODE_PRIVATE);
        }
        return mSp;
    }
    
    public static void setString(String key, String value) {
        getSp().edit().putString(key, value).apply();
    }
    
    public static String getString(String key) {
        return getSp().getString(key, "");
    }
    
    public static String getString(String key, String defaultValue) {
        return getSp().getString(key, defaultValue);
    }
    
    public static void setInt(String key, int value) {
        getSp().edit().putInt(key, value).apply();
    }
    
    public static int getInt(String key) {
        return getSp().getInt(key, 0);
    }
    
    public static int getInt(String key, int defaultValue) {
        return getSp().getInt(key, defaultValue);
    }
    
    public static void setBoolean(String key, boolean value) {
        getSp().edit().putBoolean(key, value).apply();
    }
    
    public static boolean getBoolean(String key) {
        return getSp().getBoolean(key, false);
    }
} public class ThemeHelper {
    public static String getTheme() {
        String theme = SPUtils.getString(Constants.THEME);
        return "".equals(theme) ? Constants.THEME_LIGHT : theme;
    }
    
    public static void setTheme(String theme) {
        SPUtils.setString(Constants.THEME, theme);
        Aequorea.setCurrentTheme(theme);
    }
    
    public static int getThemeStyle(String theme) {
        switch (theme) {
            default:
            case Constants.THEME_LIGHT:
                return R.style.AppTheme_Light;
            case Constants.THEME_DARK:
                return R.style.AppTheme_Dark;
        }
    }
    
    public static int getArticleThemeStyle(String theme) {
        switch (theme) {
            default:
            case Constants.THEME_LIGHT:
                return R.style.AppTheme_Article_Light;
            case Constants.THEME_DARK:
                return R.style.AppTheme_Article_Dark;
        }
    }
    
    public static int getResourceId(Context context, int attr) {
        TypedValue typedValue = new TypedValue();
        context.getTheme().resolveAttribute(attr, typedValue, true);
        return typedValue.resourceId;
    }
    
    public static int getResourceColor(Context context, int attr){
        TypedValue typedValue = new TypedValue();
        context.getTheme().resolveAttribute(attr, typedValue, true);
        return ContextCompat.getColor(context, typedValue.resourceId);
    }
} public class SettingsPresenter extends BasePresenter<SettingsView> {
    
} public interface SettingsView extends BaseView {
}
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.content.ContextCompat;
import androidx.appcompat.widget.Toolbar;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import nich.work.aequorea.Aequorea;
import nich.work.aequorea.R;
import nich.work.aequorea.common.Constants;
import nich.work.aequorea.common.rx.RxBus;
import nich.work.aequorea.common.rx.RxEvent;
import nich.work.aequorea.common.ui.activity.BaseActivity;
import nich.work.aequorea.common.ui.widget.ACheckBox;
import nich.work.aequorea.common.ui.widget.StatusBarView;
import nich.work.aequorea.common.utils.DisplayUtils;
import nich.work.aequorea.common.utils.SPUtils;
import nich.work.aequorea.common.utils.ThemeHelper;
import nich.work.aequorea.presenter.SettingsPresenter;
import nich.work.aequorea.ui.view.SettingsView;
package nich.work.aequorea.ui.activitiy;




public class SettingsActivity extends BaseActivity implements SettingsView {
    
    private SettingsPresenter mPresenter;
    
    private List<ACheckBox> mList;
    
    @BindView(R.id.cb_hd_screenshot)
    protected ACheckBox mHdScreenshotCb;
    @BindView(R.id.cb_enable_text_selection)
    protected ACheckBox mEnableSelectionCb;
    @BindView(R.id.cb_disable_recommend_article)
    protected ACheckBox mDisableRecommendArticleCb;
    @BindView(R.id.cb_dark_theme)
    protected ACheckBox mDarkThemeCb;
    @BindView(R.id.cb_offline_caching)
    protected ACheckBox mOfflineCb;
    @BindView(R.id.container_settings)
    protected ViewGroup mContainer;
    @BindView(R.id.toolbar)
    protected Toolbar mToolbar;
    @BindView(R.id.status_bar)
    protected StatusBarView mStatusBar;
    @BindView(R.id.main_content)
    protected CoordinatorLayout mCoordinatorLayout;
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        mPresenter.detach();
    }
    
    @Override
    protected int getContentViewId() {
        return R.layout.activity_settings;
    }
    
    @Override
    protected void initModel() {
        // do nothing
    }
    
    @Override
    protected void initView() {
        ButterKnife.bind(this);
        setStatusBarStyle();
    
        mToolbar.setTitle(R.string.settings);
        mToolbar.setNavigationIcon(R.drawable.icon_ab_back_material);
        mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onBackPressed();
            }
        });
    
        mCoordinatorLayout.setPadding(0, DisplayUtils.getStatusBarHeight(getResources()), 0, 0);
    
        mHdScreenshotCb.initStatus(Constants.SP_HD_SCREENSHOT, SPUtils.getBoolean(Constants.SP_HD_SCREENSHOT));