Browse Source

Merge branch 'feature/v1.0' into develop

liuyuqi-dellpc 6 years ago
parent
commit
06fcd8624c
47 changed files with 2052 additions and 39 deletions
  1. 1 0
      .gitignore
  2. 17 1
      app/build.gradle
  3. 3 0
      app/proguard-rules.pro
  4. 41 3
      app/src/main/AndroidManifest.xml
  5. 0 13
      app/src/main/java/me/yoqi/app/wxredpacket/MainActivity.java
  6. 156 0
      app/src/main/java/me/yoqi/app/wxredpacket/activities/MainActivity.java
  7. 95 0
      app/src/main/java/me/yoqi/app/wxredpacket/activities/SeekBarPreference.java
  8. 97 0
      app/src/main/java/me/yoqi/app/wxredpacket/activities/SettingsActivity.java
  9. 137 0
      app/src/main/java/me/yoqi/app/wxredpacket/activities/WebViewActivity.java
  10. 54 0
      app/src/main/java/me/yoqi/app/wxredpacket/fragments/CommentSettingsFragment.java
  11. 53 0
      app/src/main/java/me/yoqi/app/wxredpacket/fragments/GeneralSettingsFragment.java
  12. 447 0
      app/src/main/java/me/yoqi/app/wxredpacket/services/HongbaoService.java
  13. 23 0
      app/src/main/java/me/yoqi/app/wxredpacket/utils/ConnectivityUtil.java
  14. 30 0
      app/src/main/java/me/yoqi/app/wxredpacket/utils/DownloadUtil.java
  15. 51 0
      app/src/main/java/me/yoqi/app/wxredpacket/utils/HongbaoLogger.java
  16. 123 0
      app/src/main/java/me/yoqi/app/wxredpacket/utils/HongbaoSignature.java
  17. 56 0
      app/src/main/java/me/yoqi/app/wxredpacket/utils/PowerUtil.java
  18. 117 0
      app/src/main/java/me/yoqi/app/wxredpacket/utils/UpdateTask.java
  19. 128 19
      app/src/main/res/layout/activity_main.xml
  20. 42 0
      app/src/main/res/layout/activity_preferences.xml
  21. 9 0
      app/src/main/res/layout/activity_settings.xml
  22. 47 0
      app/src/main/res/layout/activity_webview.xml
  23. 23 0
      app/src/main/res/layout/preference_category.xml
  24. 47 0
      app/src/main/res/layout/preference_checkbox.xml
  25. 17 0
      app/src/main/res/layout/preference_seekbar.xml
  26. BIN
      app/src/main/res/mipmap-mdpi/bg_snow.png
  27. BIN
      app/src/main/res/mipmap-mdpi/bg_snow_2.png
  28. BIN
      app/src/main/res/mipmap-mdpi/ic_back.png
  29. BIN
      app/src/main/res/mipmap-mdpi/ic_community.png
  30. BIN
      app/src/main/res/mipmap-mdpi/ic_github.png
  31. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher.png
  32. BIN
      app/src/main/res/mipmap-mdpi/ic_open_in_browser.png
  33. BIN
      app/src/main/res/mipmap-mdpi/ic_refresh.png
  34. BIN
      app/src/main/res/mipmap-mdpi/ic_settings.png
  35. BIN
      app/src/main/res/mipmap-mdpi/ic_start.png
  36. BIN
      app/src/main/res/mipmap-mdpi/ic_stop.png
  37. BIN
      app/src/main/res/mipmap-mdpi/ic_uber.jpg
  38. BIN
      app/src/main/res/mipmap-mdpi/open.png
  39. 58 0
      app/src/main/res/values-en/strings.xml
  40. 5 0
      app/src/main/res/values-w820dp/dimens.xml
  41. 55 1
      app/src/main/res/values/strings.xml
  42. 0 2
      app/src/main/res/values/styles.xml
  43. 11 0
      app/src/main/res/xml/accessible_service_config.xml
  44. 36 0
      app/src/main/res/xml/comment_preferences.xml
  45. 65 0
      app/src/main/res/xml/general_preferences.xml
  46. 7 0
      app/src/main/res/xml/provider_paths.xml
  47. 1 0
      gradle.properties

+ 1 - 0
.gitignore

@@ -9,3 +9,4 @@
 .externalNativeBuild
 /.idea
 /.vs
+/docs

+ 17 - 1
app/build.gradle

@@ -4,11 +4,24 @@ android {
     compileSdkVersion 26
     defaultConfig {
         applicationId "me.yoqi.app.wxredpacket"
-        minSdkVersion 15
+        minSdkVersion 16
         targetSdkVersion 26
         versionCode 1
         versionName "1.0"
         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        ndk {
+            // 设置支持的SO库架构
+            abiFilters 'armeabi', 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
+        }
+    }
+    useLibrary 'org.apache.http.legacy'
+    signingConfigs {
+        releaseConfig {
+            storeFile file("../monkeytong.jks")
+            storePassword project.hasProperty("KEYSTORE_PASS") ? KEYSTORE_PASS : System.getenv("KEYSTORE_PASS")
+            keyAlias project.hasProperty("ALIAS_NAME") ? ALIAS_NAME : System.getenv("ALIAS_NAME")
+            keyPassword project.hasProperty("ALIAS_PASS") ? ALIAS_PASS : System.getenv("ALIAS_PASS")
+        }
     }
     buildTypes {
         release {
@@ -19,6 +32,9 @@ android {
 }
 
 dependencies {
+    compile 'com.tencent.bugly:crashreport_upgrade:latest.release'
+    compile 'com.tencent.bugly:nativecrashreport:latest.release'
+
     implementation fileTree(dir: 'libs', include: ['*.jar'])
     implementation 'com.android.support:appcompat-v7:26.1.0'
     implementation 'com.android.support.constraint:constraint-layout:1.0.2'

+ 3 - 0
app/proguard-rules.pro

@@ -19,3 +19,6 @@
 # If you keep the line number information, uncomment this to
 # hide the original source file name.
 #-renamesourcefileattribute SourceFile
+-dontwarn com.tencent.bugly.**
+-keep public class com.tencent.bugly.**{*;}
+-keep class android.support.**{*;}

+ 41 - 3
app/src/main/AndroidManifest.xml

@@ -1,6 +1,16 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="me.yoqi.app.wxredpacket" >
+    package="me.yoqi.app.wxredpacket">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WAKE_LOCK"/>
+
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.READ_LOGS" />
 
     <application
         android:allowBackup="true"
@@ -8,14 +18,42 @@
         android:label="@string/app_name"
         android:roundIcon="@mipmap/ic_launcher_round"
         android:supportsRtl="true"
-        android:theme="@style/AppTheme" >
-        <activity android:name=".MainActivity" >
+        android:theme="@style/AppTheme">
+        <activity android:name=".activities.MainActivity"
+            android:launchMode="singleTask"
+            android:theme="@style/Base.Theme.AppCompat.Light">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
 
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        <activity android:name=".activities.SettingsActivity"
+            android:theme="@style/Base.Theme.AppCompat.Light"></activity>
+        <activity android:name=".activities.WebViewActivity"
+            android:theme="@style/Base.Theme.AppCompat.Light"></activity>
+        <service
+            android:name=".services.HongbaoService"
+            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
+            <intent-filter>
+                <action android:name="android.accessibilityservice.AccessibilityService"/>
+            </intent-filter>
+            <meta-data android:name="android.accessibilityservice"
+                android:resource="@xml/accessible_service_config"/>
+        </service>
+        <provider
+            android:name="android.support.v4.content.FileProvider"
+            android:authorities="me.yoqi.app.hongbao.fileProvider"
+            android:exported="false"
+            android:grantUriPermissions="true">
+            <meta-data
+                android:name="android.support.FILE_PROVIDER_PATHS"
+                android:resource="@xml/provider_paths"/>
+        </provider>
+
+        <activity
+            android:name="com.tencent.bugly.beta.ui.BetaActivity"
+            android:theme="@android:style/Theme.Translucent" />
     </application>
 
 </manifest>

+ 0 - 13
app/src/main/java/me/yoqi/app/wxredpacket/MainActivity.java

@@ -1,13 +0,0 @@
-package me.yoqi.app.wxredpacket;
-
-import android.support.v7.app.AppCompatActivity;
-import android.os.Bundle;
-
-public class MainActivity extends AppCompatActivity {
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_main);
-    }
-}

+ 156 - 0
app/src/main/java/me/yoqi/app/wxredpacket/activities/MainActivity.java

@@ -0,0 +1,156 @@
+package me.yoqi.app.wxredpacket.activities;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.tencent.bugly.Bugly;
+
+import java.util.List;
+
+import me.yoqi.app.wxredpacket.R;
+
+public class MainActivity extends Activity implements AccessibilityManager.AccessibilityStateChangeListener {
+
+    //开关切换按钮
+    private TextView pluginStatusText;
+    private ImageView pluginStatusIcon;
+    private AccessibilityManager accessibilityManager;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Bugly.init(getApplicationContext(), "9410cbd743", false);
+        setContentView(R.layout.activity_main);
+        pluginStatusText = findViewById(R.id.layout_control_accessibility_text);
+        pluginStatusIcon = findViewById(R.id.layout_control_accessibility_icon);
+
+        handleMaterialStatusBar();
+        explicitlyLoadPreferences();
+
+        //监听AccessibilityService 变化
+        accessibilityManager = (AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE);
+        accessibilityManager.addAccessibilityStateChangeListener(this);
+        updateServiceStatus();
+    }
+
+    /**
+     * 设置默认偏好
+     */
+    private void explicitlyLoadPreferences() {
+        PreferenceManager.setDefaultValues(this, R.xml.general_preferences, false);
+    }
+
+    /**
+     * 适配MIUI沉浸状态栏
+     */
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    private void handleMaterialStatusBar() {
+        // Not supported in APK level lower than 21
+        if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
+
+        Window window = this.getWindow();
+
+        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+
+        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+
+        window.setStatusBarColor(0xffE46C62);
+
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        updateServiceStatus();
+        // Check for update when WIFI is connected or on first time.
+//        if (ConnectivityUtil.isWifi(this) || UpdateTask.count == 0)
+//            new UpdateTask(this, false).update();
+    }
+
+    @Override
+    protected void onDestroy() {
+        //移除监听服务
+        accessibilityManager.removeAccessibilityStateChangeListener(this);
+        super.onDestroy();
+    }
+
+    /**跳转到辅助服务设置界面点击事件
+     * @param view
+     */
+    public void openAccessibility(View view) {
+        try {
+            Toast.makeText(this, getString(R.string.turn_on_toast) + pluginStatusText.getText(), Toast.LENGTH_SHORT).show();
+            Intent accessibleIntent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
+            startActivity(accessibleIntent);
+        } catch (Exception e) {
+            Toast.makeText(this, getString(R.string.turn_on_error_toast), Toast.LENGTH_LONG).show();
+            e.printStackTrace();
+        }
+
+    }
+
+    /**设置点击事件
+     * @param view
+     */
+    public void openSettings(View view) {
+        Intent settingsIntent = new Intent(this, SettingsActivity.class);
+        settingsIntent.putExtra("title", getString(R.string.preference));
+        settingsIntent.putExtra("frag_id", "GeneralSettingsFragment");
+        startActivity(settingsIntent);
+    }
+
+
+    @Override
+    public void onAccessibilityStateChanged(boolean enabled) {
+        updateServiceStatus();
+    }
+
+    /**
+     * 更新当前 HongbaoService 显示状态
+     */
+    private void updateServiceStatus() {
+        if (isServiceEnabled()) {
+            pluginStatusText.setText(R.string.service_off);
+            pluginStatusIcon.setBackgroundResource(R.mipmap.ic_stop);
+        } else {
+            pluginStatusText.setText(R.string.service_on);
+            pluginStatusIcon.setBackgroundResource(R.mipmap.ic_start);
+        }
+    }
+
+    /**
+     * 获取 HongbaoService 是否启用状态
+     *
+     * @return
+     */
+    private boolean isServiceEnabled() {
+        List<AccessibilityServiceInfo> accessibilityServices =
+                accessibilityManager.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_GENERIC);
+        for (AccessibilityServiceInfo info : accessibilityServices) {
+            if (info.getId().equals(getPackageName() + "/.services.HongbaoService")) {
+                return true;
+            }
+        }
+        return false;
+    }
+}

+ 95 - 0
app/src/main/java/me/yoqi/app/wxredpacket/activities/SeekBarPreference.java

@@ -0,0 +1,95 @@
+package me.yoqi.app.wxredpacket.activities;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.preference.DialogPreference;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import me.yoqi.app.wxredpacket.R;
+
+/**
+ *
+ */
+public class SeekBarPreference extends DialogPreference {
+    private SeekBar seekBar;
+    private TextView textView;
+    private String hintText, prefKind;
+
+    public SeekBarPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setDialogLayoutResource(R.layout.preference_seekbar);
+
+        for (int i = 0; i < attrs.getAttributeCount(); i++) {
+            String attr = attrs.getAttributeName(i);
+            if (attr.equalsIgnoreCase("pref_kind")) {
+                prefKind = attrs.getAttributeValue(i);
+                break;
+            }
+        }
+        if (prefKind.equals("pref_open_delay")) {
+            hintText = getContext().getString(R.string.delay_open);
+        } else if (prefKind.equals("pref_comment_delay")) {
+            hintText = "发送回复(暂不支持延时)";
+        }
+    }
+
+    @Override
+    protected void onBindDialogView(View view) {
+        super.onBindDialogView(view);
+
+        SharedPreferences pref = getSharedPreferences();
+
+        int delay = pref.getInt(prefKind, 0);
+        this.seekBar = view.findViewById(R.id.delay_seekBar);
+        this.seekBar.setProgress(delay);
+
+        if (prefKind.equals("pref_comment_delay")) {
+            this.seekBar.setEnabled(false);
+        }
+
+        this.textView = view.findViewById(R.id.pref_seekbar_textview);
+        setHintText(0);
+
+        this.seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+            @Override
+            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
+                setHintText(i);
+            }
+
+            @Override
+            public void onStartTrackingTouch(SeekBar seekBar) {
+
+            }
+
+            @Override
+            public void onStopTrackingTouch(SeekBar seekBar) {
+
+            }
+        });
+    }
+
+    @Override
+    protected void onDialogClosed(boolean positiveResult) {
+        if (positiveResult) {
+            SharedPreferences.Editor editor = getEditor();
+            editor.putInt(prefKind, this.seekBar.getProgress());
+            editor.commit();
+        }
+        super.onDialogClosed(positiveResult);
+    }
+
+    /**
+     * 设置弹框
+     * @param delay 延时s
+     */
+    private void setHintText(int delay) {
+        if (delay == 0) {
+            this.textView.setText(getContext().getString(R.string.delay_instantly) + hintText);
+        } else {
+            this.textView.setText(getContext().getString(R.string.delay_delay) + delay + getContext().getString(R.string.delay_sec) + getContext().getString(R.string.delay_then) + hintText);
+        }
+    }
+}

+ 97 - 0
app/src/main/java/me/yoqi/app/wxredpacket/activities/SettingsActivity.java

@@ -0,0 +1,97 @@
+package me.yoqi.app.wxredpacket.activities;
+
+import android.annotation.TargetApi;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.support.v4.app.FragmentActivity;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import me.yoqi.app.wxredpacket.R;
+import me.yoqi.app.wxredpacket.fragments.CommentSettingsFragment;
+import me.yoqi.app.wxredpacket.fragments.GeneralSettingsFragment;
+
+
+/**
+ * 设置界面
+ */
+public class SettingsActivity extends FragmentActivity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_preferences);
+
+        loadUI();
+        prepareSettings();
+    }
+
+    /**
+     * 加载预定义设置
+     */
+    private void prepareSettings() {
+        String title, fragId;
+        Bundle bundle = getIntent().getExtras();
+        if (bundle != null) {
+            //从bundle获取传递的参数。
+            title = bundle.getString("title");
+            fragId = bundle.getString("frag_id");
+        } else {
+            title = getString(R.string.preference);
+            fragId = "GeneralSettingsFragment";
+        }
+
+        TextView textView = findViewById(R.id.settings_bar);
+        textView.setText(title);
+
+        FragmentManager fragmentManager = getFragmentManager();
+        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
+
+        if ("GeneralSettingsFragment".equals(fragId)) {
+            fragmentTransaction.replace(R.id.preferences_fragment, new GeneralSettingsFragment());
+        } else if ("CommentSettingsFragment".equals(fragId)) {
+            fragmentTransaction.replace(R.id.preferences_fragment, new CommentSettingsFragment());
+        }
+        fragmentTransaction.commit();
+    }
+
+    /**
+     * 适配手机,加载UI
+     */
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    private void loadUI() {
+        if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
+        Window window = this.getWindow();
+        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+        window.setStatusBarColor(0xffE46C62);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    /**
+     * @param view
+     */
+    public void performBack(View view) {
+        super.onBackPressed();
+    }
+
+    /**进入AccessibilityPage设置权限
+     * @param view
+     */
+    public void enterAccessibilityPage(View view) {
+        Toast.makeText(this, getString(R.string.turn_on_toast), Toast.LENGTH_SHORT).show();
+        Intent mAccessibleIntent =
+                new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
+        startActivity(mAccessibleIntent);
+    }
+}

+ 137 - 0
app/src/main/java/me/yoqi/app/wxredpacket/activities/WebViewActivity.java

@@ -0,0 +1,137 @@
+package me.yoqi.app.wxredpacket.activities;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.webkit.CookieSyncManager;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import me.yoqi.app.wxredpacket.R;
+import me.yoqi.app.wxredpacket.utils.DownloadUtil;
+
+/**
+ * webview打开网页
+ */
+public class WebViewActivity extends Activity {
+    private WebView webView;
+    private String webViewUrl, webViewTitle;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        loadUI();
+
+        Bundle bundle = getIntent().getExtras();
+        if (bundle != null && !bundle.isEmpty()) {
+            webViewTitle = bundle.getString("title");
+            webViewUrl = bundle.getString("url");
+
+            final TextView webViewBar = findViewById(R.id.webview_bar);
+            webViewBar.setText(webViewTitle);
+
+            webView = findViewById(R.id.webView);
+            webView.getSettings().setBuiltInZoomControls(false);
+            webView.getSettings().setJavaScriptEnabled(true);
+            webView.getSettings().setDomStorageEnabled(true);
+            webView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);
+            webView.setWebViewClient(new WebViewClient() {
+                @Override
+                public boolean shouldOverrideUrlLoading(WebView view, String url) {
+                    if (url.contains("apk")) {
+                        Toast.makeText(getApplicationContext(), getString(R.string.download_backend), Toast.LENGTH_SHORT).show();
+                        (new DownloadUtil()).enqueue(url, getApplicationContext());
+                        return true;
+                    } else if (!url.contains("http")) {
+                        Toast.makeText(getApplicationContext(), getString(R.string.download_redirect), Toast.LENGTH_LONG).show();
+                        webViewBar.setText(getString(R.string.download_hint));
+                        return false;
+                    } else {
+                        view.loadUrl(url);
+                        return false;
+                    }
+                }
+
+                @Override
+                public void onPageFinished(WebView view, String url) {
+                    CookieSyncManager.getInstance().sync();
+                }
+            });
+            webView.loadUrl(webViewUrl);
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+    }
+
+    /**
+     * 加载UI
+     */
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    private void loadUI() {
+        setContentView(R.layout.activity_webview);
+
+        if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
+
+        Window window = this.getWindow();
+
+        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
+
+        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+
+        window.setStatusBarColor(0xffE46C62);
+    }
+
+    public void performBack(View view) {
+        super.onBackPressed();
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (event.getAction() == KeyEvent.ACTION_DOWN) {
+            switch (keyCode) {
+                case KeyEvent.KEYCODE_BACK:
+                    if (webView.canGoBack()) {
+                        webView.goBack();
+                    } else {
+                        finish();
+                    }
+                    return true;
+            }
+
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+    /**打开链接
+     * @param view
+     */
+    public void openLink(View view) {
+        Intent intent = new Intent(Intent.ACTION_VIEW,
+                Uri.parse(this.webViewUrl));
+        startActivity(intent);
+    }
+}

+ 54 - 0
app/src/main/java/me/yoqi/app/wxredpacket/fragments/CommentSettingsFragment.java

@@ -0,0 +1,54 @@
+package me.yoqi.app.wxredpacket.fragments;
+
+import android.os.Build;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceFragment;
+import android.preference.PreferenceManager;
+import android.widget.Toast;
+
+import me.yoqi.app.wxredpacket.R;
+
+/**
+ *接收红包后自动回复设置
+ */
+public class CommentSettingsFragment extends PreferenceFragment {
+    private Bundle savedInstanceState;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        this.savedInstanceState = savedInstanceState;
+        super.onCreate(savedInstanceState);
+        addPreferencesFromResource(R.xml.comment_preferences);
+        setPrefListeners();
+    }
+
+    /**
+     *监听事件
+     */
+    private void setPrefListeners() {
+        Preference updatePref = findPreference("pref_comment_switch");
+        if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+            updatePref.setEnabled(false);
+        }
+        Toast.makeText(getActivity(), "该功能尚处于实验中,只能自动填充感谢语,无法直接发送.", Toast.LENGTH_LONG).show();
+
+        Preference commentWordsPref = findPreference("pref_comment_words");
+        String summary = getResources().getString(R.string.pref_comment_words_summary);
+        String value = PreferenceManager.getDefaultSharedPreferences(getActivity()).getString("pref_comment_words", "");
+        if (value.length() > 0) commentWordsPref.setSummary(summary + ":" + value);
+
+        commentWordsPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+            @Override
+            public boolean onPreferenceChange(Preference preference, Object o) {
+                String summary = getResources().getString(R.string.pref_comment_words_summary);
+                if (o != null && o.toString().length() > 0) {
+                    preference.setSummary(summary + ":" + o.toString());
+                } else {
+                    preference.setSummary(summary);
+                }
+                return true;
+            }
+        });
+    }
+}

+ 53 - 0
app/src/main/java/me/yoqi/app/wxredpacket/fragments/GeneralSettingsFragment.java

@@ -0,0 +1,53 @@
+package me.yoqi.app.wxredpacket.fragments;
+
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceFragment;
+import android.preference.PreferenceManager;
+
+import me.yoqi.app.wxredpacket.R;
+import me.yoqi.app.wxredpacket.utils.UpdateTask;
+
+/**
+ *偏好设置Fragment
+ */
+public class GeneralSettingsFragment extends PreferenceFragment {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        addPreferencesFromResource(R.xml.general_preferences);
+        setPrefListeners();
+    }
+
+    /**
+     *设置Pref的监听事件
+     */
+    private void setPrefListeners() {
+        // Check for updates
+        Preference updatePref = findPreference("pref_etc_check_update");
+        updatePref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+            public boolean onPreferenceClick(Preference preference) {
+                new UpdateTask(getActivity().getApplicationContext(), true).update();
+                return false;
+            }
+        });
+
+        Preference excludeWordsPref = findPreference("pref_watch_exclude_words");
+        String summary = getResources().getString(R.string.pref_watch_exclude_words_summary);
+        String value = PreferenceManager.getDefaultSharedPreferences(getActivity()).getString("pref_watch_exclude_words", "");
+        if (value.length() > 0) excludeWordsPref.setSummary(summary + ":" + value);
+
+        excludeWordsPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+            @Override
+            public boolean onPreferenceChange(Preference preference, Object o) {
+                String summary = getResources().getString(R.string.pref_watch_exclude_words_summary);
+                if (o != null && o.toString().length() > 0) {
+                    preference.setSummary(summary + ":" + o.toString());
+                } else {
+                    preference.setSummary(summary);
+                }
+                return true;
+            }
+        });
+    }
+}

+ 447 - 0
app/src/main/java/me/yoqi/app/wxredpacket/services/HongbaoService.java

@@ -0,0 +1,447 @@
+package me.yoqi.app.wxredpacket.services;
+
+import android.accessibilityservice.AccessibilityService;
+import android.accessibilityservice.GestureDescription;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.preference.PreferenceManager;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import java.util.List;
+
+import me.yoqi.app.wxredpacket.utils.HongbaoSignature;
+import me.yoqi.app.wxredpacket.utils.PowerUtil;
+
+/**
+ * 红包服务,后台监听微信消息,出现红包关键词就打开微信抢红包。
+ */
+public class HongbaoService extends AccessibilityService implements SharedPreferences.OnSharedPreferenceChangeListener {
+    private static final String WECHAT_DETAILS_EN = "Details";
+    private static final String WECHAT_DETAILS_CH = "红包详情";
+    private static final String WECHAT_BETTER_LUCK_EN = "Better luck next time!";
+    private static final String WECHAT_BETTER_LUCK_CH = "手慢了";
+    private static final String WECHAT_EXPIRES_CH = "已超过24小时";
+    private static final String WECHAT_VIEW_SELF_CH = "查看红包";
+    private static final String WECHAT_VIEW_OTHERS_CH = "领取红包";
+    private static final String WECHAT_NOTIFICATION_TIP = "[微信红包]";
+    private static final String WECHAT_LUCKMONEY_RECEIVE_ACTIVITY = "LuckyMoneyReceiveUI";
+    private static final String WECHAT_LUCKMONEY_DETAIL_ACTIVITY = "LuckyMoneyDetailUI";
+    private static final String WECHAT_LUCKMONEY_GENERAL_ACTIVITY = "LauncherUI";
+    private static final String WECHAT_LUCKMONEY_CHATTING_ACTIVITY = "ChattingUI";
+    private String currentActivityName = WECHAT_LUCKMONEY_GENERAL_ACTIVITY;
+
+    private AccessibilityNodeInfo rootNodeInfo, mReceiveNode, mUnpackNode;
+    private boolean mLuckyMoneyPicked, mLuckyMoneyReceived;
+    private int mUnpackCount = 0;
+    private boolean mMutex = false, mListMutex = false, mChatMutex = false;
+    private HongbaoSignature signature = new HongbaoSignature();
+
+    private PowerUtil powerUtil;
+    private SharedPreferences sharedPreferences;
+    /**
+     * AccessibilityEvent,收到event,过滤红包信息
+     *
+     * @param event 事件
+     */
+    @Override
+    public void onAccessibilityEvent(AccessibilityEvent event) {
+        if (sharedPreferences == null) return;
+
+        setCurrentActivityName(event);
+
+        /* 检测通知消息 */
+        if (!mMutex) {
+            if (sharedPreferences.getBoolean("pref_watch_notification", false) && watchNotifications(event))
+                return;
+            if (sharedPreferences.getBoolean("pref_watch_list", false) && watchList(event)) return;
+            mListMutex = false;
+        }
+
+        if (!mChatMutex) {
+            mChatMutex = true;
+            if (sharedPreferences.getBoolean("pref_watch_chat", false)) watchChat(event);
+            mChatMutex = false;
+        }
+    }
+
+    /** 监听是否进入微信红包消息界面
+     * @param event
+     */
+    private void watchChat(AccessibilityEvent event) {
+        Log.i("hongbaoservice","watchChat");
+
+        this.rootNodeInfo = getRootInActiveWindow();
+
+        if (rootNodeInfo == null) return;
+
+        mReceiveNode = null;
+        mUnpackNode = null;
+
+        checkNodeInfo(event.getEventType());
+
+        /* 如果已经接收到红包并且还没有戳开 */
+        if (mLuckyMoneyReceived && !mLuckyMoneyPicked && (mReceiveNode != null)) {
+            mMutex = true;
+
+            mReceiveNode.getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK);
+            mLuckyMoneyReceived = false;
+            mLuckyMoneyPicked = true;
+        }
+        /* 如果戳开但还未领取 */
+        if (mUnpackCount == 1 && (mUnpackNode != null)) {
+            int delayFlag = sharedPreferences.getInt("pref_open_delay", 0) * 1000;
+            new android.os.Handler().postDelayed(
+                    new Runnable() {
+                        public void run() {
+                            try {
+                                openPacket();
+                            } catch (Exception e) {
+                                mMutex = false;
+                                mLuckyMoneyPicked = false;
+                                mUnpackCount = 0;
+                            }
+                        }
+                    },
+                    delayFlag);
+        }
+    }
+
+    /**
+     * 点击“打开”,收红包
+     */
+    private void openPacket() {
+        Log.i("hongbaoservice","openPacket");
+        DisplayMetrics metrics = getResources().getDisplayMetrics();
+        float dpi = metrics.density;
+        if (android.os.Build.VERSION.SDK_INT <= 23) {
+            mUnpackNode.performAction(AccessibilityNodeInfo.ACTION_CLICK);
+        } else {
+            if (android.os.Build.VERSION.SDK_INT > 23) {
+
+                Path path = new Path();
+                if (640 == dpi) {
+                    path.moveTo(720, 1575);
+                } else {
+                    path.moveTo(540, 1060);
+                }
+                GestureDescription.Builder builder = new GestureDescription.Builder();
+                GestureDescription gestureDescription = builder.addStroke(new GestureDescription.StrokeDescription(path, 450, 50)).build();
+                dispatchGesture(gestureDescription, new GestureResultCallback() {
+                    @Override
+                    public void onCompleted(GestureDescription gestureDescription) {
+                        Log.d("test", "onCompleted");
+                        mMutex = false;
+                        super.onCompleted(gestureDescription);
+                    }
+
+                    @Override
+                    public void onCancelled(GestureDescription gestureDescription) {
+                        Log.d("test", "onCancelled");
+                        mMutex = false;
+                        super.onCancelled(gestureDescription);
+                    }
+                }, null);
+
+            }
+        }
+    }
+    /**
+     * 返回桌面
+     */
+    private void back2Home() {
+        Intent home=new Intent(Intent.ACTION_MAIN);
+        home.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        home.addCategory(Intent.CATEGORY_HOME);
+        startActivity(home);
+    }
+
+    /**
+     * @param event
+     */
+    private void setCurrentActivityName(AccessibilityEvent event) {
+        if (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
+            return;
+        }
+
+        try {
+            ComponentName componentName = new ComponentName(
+                    event.getPackageName().toString(),
+                    event.getClassName().toString()
+            );
+
+            getPackageManager().getActivityInfo(componentName, 0);
+            currentActivityName = componentName.flattenToShortString();
+        } catch (PackageManager.NameNotFoundException e) {
+            currentActivityName = WECHAT_LUCKMONEY_GENERAL_ACTIVITY;
+        }
+    }
+
+    /** 监视进入微信app聊天列表:case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
+     * @param event
+     * @return
+     */
+    private boolean watchList(AccessibilityEvent event) {
+        Log.i("hongbaoservice","watchList");
+        if (mListMutex) return false;
+        mListMutex = true;
+        AccessibilityNodeInfo eventSource = event.getSource();
+        // Not a message
+        if (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED || eventSource == null)
+            return false;
+
+        List<AccessibilityNodeInfo> nodes = eventSource.findAccessibilityNodeInfosByText(WECHAT_NOTIFICATION_TIP);
+        //避免当订阅号中出现标题为“[微信红包]拜年红包”(其实并非红包)的信息时误判
+        //判断是否是微信聊天界面com.tencent.mm.ui.LauncherUI
+        if (!nodes.isEmpty() && currentActivityName.contains(WECHAT_LUCKMONEY_GENERAL_ACTIVITY)) {
+            AccessibilityNodeInfo nodeToClick = nodes.get(0);
+            if (nodeToClick == null) return false;
+            CharSequence contentDescription = nodeToClick.getContentDescription();
+            if (contentDescription != null && !signature.getContentDescription().equals(contentDescription)) {
+                nodeToClick.performAction(AccessibilityNodeInfo.ACTION_CLICK);
+                signature.setContentDescription(contentDescription.toString());
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** 监听通知栏消息,判断红包
+     * @param event
+     * @return
+     */
+    private boolean watchNotifications(AccessibilityEvent event) {
+        Log.i("hongbaoservice","watchNotifications");
+
+        // Not a notification
+        if (event.getEventType() != AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED)
+            return false;
+
+        // Not a hongbao
+        String tip = event.getText().toString();
+        if (!tip.contains(WECHAT_NOTIFICATION_TIP)) return true;
+
+        Parcelable parcelable = event.getParcelableData();
+        if (parcelable instanceof Notification) {
+            Notification notification = (Notification) parcelable;
+            try {
+                /* 清除signature,避免进入会话后误判 */
+                signature.cleanSignature();
+//                模拟打开通知栏消息
+                notification.contentIntent.send();
+            } catch (PendingIntent.CanceledException e) {
+                e.printStackTrace();
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public void onInterrupt() {
+        Log.d("HongbaoService", "抢红包服务快被终结了");
+    }
+
+    /** 找到红包节点按钮
+     * @param node 输入的根节点
+     * @return
+     */
+    private AccessibilityNodeInfo findOpenButton(AccessibilityNodeInfo node) {
+        if (node == null)
+            return null;
+
+        //非layout元素
+        if (node.getChildCount() == 0) {
+            if ("android.widget.Button".equals(node.getClassName()))
+                return node;
+            else
+                return null;
+        }
+
+        //layout元素,遍历找button
+        AccessibilityNodeInfo button;
+        for (int i = 0; i < node.getChildCount(); i++) {
+            button = findOpenButton(node.getChild(i));
+            if (button != null)
+                return button;
+        }
+        return null;
+    }
+
+    /** 检测红包节点,mReceiveNode
+     * @param eventType
+     */
+    private void checkNodeInfo(int eventType) {
+        if (this.rootNodeInfo == null) return;
+
+        if (signature.commentString != null) {
+            sendComment();
+            signature.commentString = null;
+        }
+
+        /* 聊天会话窗口,遍历节点匹配“领取红包”和"查看红包" */
+        AccessibilityNodeInfo node1 = (sharedPreferences.getBoolean("pref_watch_self", false)) ?
+                this.getTheLastNode(WECHAT_VIEW_OTHERS_CH, WECHAT_VIEW_SELF_CH) : this.getTheLastNode(WECHAT_VIEW_OTHERS_CH);
+        if (node1 != null &&
+                (currentActivityName.contains(WECHAT_LUCKMONEY_CHATTING_ACTIVITY)
+                        || currentActivityName.contains(WECHAT_LUCKMONEY_GENERAL_ACTIVITY))) {
+            String excludeWords = sharedPreferences.getString("pref_watch_exclude_words", "");
+            if (this.signature.generateSignature(node1, excludeWords)) {
+                mLuckyMoneyReceived = true;
+                mReceiveNode = node1;
+                Log.d("sig", this.signature.toString());
+            }
+            return;
+        }
+
+        /* 戳开红包,红包还没抢完,遍历节点匹配“拆红包” */
+        AccessibilityNodeInfo node2 = findOpenButton(this.rootNodeInfo);
+        if (node2 != null && "android.widget.Button".equals(node2.getClassName()) && currentActivityName.contains(WECHAT_LUCKMONEY_RECEIVE_ACTIVITY)) {
+            mUnpackNode = node2;
+            mUnpackCount += 1;
+            return;
+        }
+
+        /* 戳开红包,红包已被抢完,遍历节点匹配“红包详情”和“手慢了” */
+        boolean hasNodes = this.hasOneOfThoseNodes(
+                WECHAT_BETTER_LUCK_CH, WECHAT_DETAILS_CH,
+                WECHAT_BETTER_LUCK_EN, WECHAT_DETAILS_EN, WECHAT_EXPIRES_CH);
+        //判断是否是红包领取后的详情界面 //判断是否是显示‘开'的那个红包界面com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI
+        if (mMutex && eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && hasNodes
+                && (currentActivityName.contains(WECHAT_LUCKMONEY_DETAIL_ACTIVITY)
+                || currentActivityName.contains(WECHAT_LUCKMONEY_RECEIVE_ACTIVITY))) {
+            mMutex = false;
+            mLuckyMoneyPicked = false;
+            mUnpackCount = 0;
+            performGlobalAction(GLOBAL_ACTION_BACK);
+            signature.commentString = generateCommentString();
+        }
+    }
+
+    /**
+     * 发送回复
+     */
+    private void sendComment() {
+        try {
+            AccessibilityNodeInfo outNode =
+                    getRootInActiveWindow().getChild(0).getChild(0);
+            AccessibilityNodeInfo nodeToInput = outNode.getChild(outNode.getChildCount() - 1).getChild(0).getChild(1);
+
+            if ("android.widget.EditText".equals(nodeToInput.getClassName())) {
+                Bundle arguments = new Bundle();
+                arguments.putCharSequence(AccessibilityNodeInfo
+                        .ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, signature.commentString);
+                nodeToInput.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
+            }
+        } catch (Exception e) {
+            // Not supported
+        }
+    }
+
+
+    /**
+     * @param texts
+     * @return
+     */
+    private boolean hasOneOfThoseNodes(String... texts) {
+        List<AccessibilityNodeInfo> nodes;
+        for (String text : texts) {
+            if (text == null) continue;
+
+            nodes = this.rootNodeInfo.findAccessibilityNodeInfosByText(text);
+
+            if (nodes != null && !nodes.isEmpty()) return true;
+        }
+        return false;
+    }
+
+    /**
+     * @param texts
+     * @return
+     */
+    private AccessibilityNodeInfo getTheLastNode(String... texts) {
+        int bottom = 0;
+        AccessibilityNodeInfo lastNode = null, tempNode;
+        List<AccessibilityNodeInfo> nodes;
+
+        for (String text : texts) {
+            if (text == null) continue;
+
+            nodes = this.rootNodeInfo.findAccessibilityNodeInfosByText(text);
+
+            if (nodes != null && !nodes.isEmpty()) {
+                tempNode = nodes.get(nodes.size() - 1);
+                if (tempNode == null) return null;
+                Rect bounds = new Rect();
+                tempNode.getBoundsInScreen(bounds);
+                if (bounds.bottom > bottom) {
+                    bottom = bounds.bottom;
+                    lastNode = tempNode;
+                    signature.others = text.equals(WECHAT_VIEW_OTHERS_CH);
+                }
+            }
+        }
+        return lastNode;
+    }
+
+    @Override
+    public void onServiceConnected() {
+        super.onServiceConnected();
+        this.watchFlagsFromPreference();
+    }
+
+    /**
+     * 如果设置了锁屏抢则,解锁屏幕
+     */
+    private void watchFlagsFromPreference() {
+        sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
+        sharedPreferences.registerOnSharedPreferenceChangeListener(this);
+
+        this.powerUtil = new PowerUtil(this);
+        Boolean watchOnLockFlag = sharedPreferences.getBoolean("pref_watch_on_lock", false);
+        this.powerUtil.handleWakeLock(watchOnLockFlag);
+    }
+
+    @Override
+    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+        if (key.equals("pref_watch_on_lock")) {
+            Boolean changedValue = sharedPreferences.getBoolean(key, false);
+            this.powerUtil.handleWakeLock(changedValue);
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        this.powerUtil.handleWakeLock(false);
+        super.onDestroy();
+    }
+
+    /** 获取回复字符串
+     * @return 返回回复字符串
+     */
+    private String generateCommentString() {
+        if (!signature.others) return null;
+
+        Boolean needComment = sharedPreferences.getBoolean("pref_comment_switch", false);
+        if (!needComment) return null;
+
+        String[] wordsArray = sharedPreferences.getString("pref_comment_words", "").split(" +");
+        if (wordsArray.length == 0) return null;
+
+        Boolean atSender = sharedPreferences.getBoolean("pref_comment_at", false);
+        if (atSender) {
+            return "@" + signature.sender + " " + wordsArray[(int) (Math.random() * wordsArray.length)];
+        } else {
+            return wordsArray[(int) (Math.random() * wordsArray.length)];
+        }
+    }
+}

+ 23 - 0
app/src/main/java/me/yoqi/app/wxredpacket/utils/ConnectivityUtil.java

@@ -0,0 +1,23 @@
+package me.yoqi.app.wxredpacket.utils;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+
+/**
+ * wifi工具
+ */
+public class ConnectivityUtil {
+    /**判断是否wifi
+     * @param context 全局Context
+     * @return true or false
+     */
+    public static boolean isWifi(Context context) {
+        ConnectivityManager cm =
+                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+
+        NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
+        return activeNetwork != null && activeNetwork.isConnectedOrConnecting()
+                && activeNetwork.getType() == ConnectivityManager.TYPE_WIFI;
+    }
+}

+ 30 - 0
app/src/main/java/me/yoqi/app/wxredpacket/utils/DownloadUtil.java

@@ -0,0 +1,30 @@
+package me.yoqi.app.wxredpacket.utils;
+
+import android.app.DownloadManager;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Environment;
+
+import static android.content.Context.DOWNLOAD_SERVICE;
+
+
+/**
+ * 下载工具
+ */
+public class DownloadUtil {
+
+    /**
+     * 下载队列
+     *
+     * @param url     下载链接
+     * @param context 全局Context
+     */
+    public void enqueue(String url, Context context) {
+        DownloadManager.Request r = new DownloadManager.Request(Uri.parse(url));
+        r.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "Uber.apk");
+        r.allowScanningByMediaScanner();
+        r.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
+        DownloadManager dm = (DownloadManager) context.getSystemService(DOWNLOAD_SERVICE);
+        dm.enqueue(r);
+    }
+}

+ 51 - 0
app/src/main/java/me/yoqi/app/wxredpacket/utils/HongbaoLogger.java

@@ -0,0 +1,51 @@
+package me.yoqi.app.wxredpacket.utils;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+
+/**
+ * 接收红包日志管理
+ */
+public class HongbaoLogger {
+    private Context context;
+    private SQLiteDatabase database;
+
+    private static final int DATABASE_VERSION = 1;
+    private static final String DATABASE_NAME = "wxRedPacket.db";
+    private static final String createDatabaseSQL = "CREATE TABLE IF NOT EXISTS HongbaoLog (id INTEGER PRIMARY KEY AUTOINCREMENT, sender TEXT, content TEXT, time TEXT, amount TEXT);";
+
+    /**构造函数
+     * @param context
+     */
+    public HongbaoLogger(final Context context) {
+        this.context = context;
+        this.initSchemaAndDatabase();
+    }
+
+    /**
+     * 初始化db数据库
+     */
+    private void initSchemaAndDatabase() {
+        this.database = context.openOrCreateDatabase("wxRedPacket", Context.MODE_PRIVATE, null);
+
+        this.database.beginTransaction();
+        this.database.execSQL(createDatabaseSQL);
+        this.database.endTransaction();
+    }
+
+    /** 插入红包数据
+     * @param sender
+     * @param content
+     * @param amount
+     */
+    public void writeHongbaoLog(String sender, String content, String amount) {
+
+    }
+
+    /**
+     * 获取所有数据
+     */
+    public void getAllLogs() {
+
+    }
+}

+ 123 - 0
app/src/main/java/me/yoqi/app/wxredpacket/utils/HongbaoSignature.java

@@ -0,0 +1,123 @@
+package me.yoqi.app.wxredpacket.utils;
+
+import android.graphics.Rect;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+/**
+ * 红包对象,发送者,接收者,发送时间,红包内容
+ */
+public class HongbaoSignature {
+    public String sender, content, time, contentDescription = "", commentString;
+    public boolean others;
+
+    /**
+     * @param node
+     * @param excludeWords
+     * @return
+     */
+    public boolean generateSignature(AccessibilityNodeInfo node, String excludeWords) {
+        try {
+            /* The hongbao container node. It should be a LinearLayout. By specifying that, we can avoid text messages. */
+            AccessibilityNodeInfo hongbaoNode = node.getParent();
+            if (!"android.widget.LinearLayout".equals(hongbaoNode.getClassName())) return false;
+
+            /* The text in the hongbao. Should mean something. */
+            String hongbaoContent = hongbaoNode.getChild(0).getText().toString();
+            if (hongbaoContent == null || "查看红包".equals(hongbaoContent)) return false;
+
+            /* Check the user's exclude words list. */
+            String[] excludeWordsArray = excludeWords.split(" +");
+            for (String word : excludeWordsArray) {
+                if (word.length() > 0 && hongbaoContent.contains(word)) return false;
+            }
+
+            /* The container node for a piece of message. It should be inside the screen.
+                Or sometimes it will get opened twice while scrolling. */
+            AccessibilityNodeInfo messageNode = hongbaoNode.getParent();
+
+            Rect bounds = new Rect();
+            messageNode.getBoundsInScreen(bounds);
+            if (bounds.top < 0) return false;
+
+            /* The sender and possible timestamp. Should mean something too. */
+            String[] hongbaoInfo = getSenderContentDescriptionFromNode(messageNode);
+            if (this.getSignature(hongbaoInfo[0], hongbaoContent, hongbaoInfo[1]).equals(this.toString()))
+                return false;
+
+            /* So far we make sure it's a valid new coming hongbao. */
+            this.sender = hongbaoInfo[0];
+            this.time = hongbaoInfo[1];
+            this.content = hongbaoContent;
+            return true;
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * @return
+     */
+    @Override
+    public String toString() {
+        return this.getSignature(this.sender, this.content, this.time);
+    }
+
+    /**
+     * @param strings
+     * @return
+     */
+    private String getSignature(String... strings) {
+        String signature = "";
+        for (String str : strings) {
+            if (str == null) return null;
+            signature += str + "|";
+        }
+
+        return signature.substring(0, signature.length() - 1);
+    }
+
+    /** 获取ContentDescription
+     * @return contentDescription
+     */
+    public String getContentDescription() {
+        return this.contentDescription;
+    }
+
+    /**
+     * @param description
+     */
+    public void setContentDescription(String description) {
+        this.contentDescription = description;
+    }
+
+    /**
+     * @param node
+     * @return
+     */
+    private String[] getSenderContentDescriptionFromNode(AccessibilityNodeInfo node) {
+        int count = node.getChildCount();
+        String[] result = {"unknownSender", "unknownTime"};
+        for (int i = 0; i < count; i++) {
+            AccessibilityNodeInfo thisNode = node.getChild(i);
+            if ("android.widget.ImageView".equals(thisNode.getClassName()) && "unknownSender".equals(result[0])) {
+                CharSequence contentDescription = thisNode.getContentDescription();
+                if (contentDescription != null)
+                    result[0] = contentDescription.toString().replaceAll("头像$", "");
+            } else if ("android.widget.TextView".equals(thisNode.getClassName()) && "unknownTime".equals(result[1])) {
+                CharSequence thisNodeText = thisNode.getText();
+                if (thisNodeText != null) result[1] = thisNodeText.toString();
+            }
+        }
+        return result;
+    }
+
+    /**
+     * 清除红包标志
+     */
+    public void cleanSignature() {
+        this.content = "";
+        this.time = "";
+        this.sender = "";
+    }
+}

+ 56 - 0
app/src/main/java/me/yoqi/app/wxredpacket/utils/PowerUtil.java

@@ -0,0 +1,56 @@
+package me.yoqi.app.wxredpacket.utils;
+
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.os.PowerManager;
+
+/**
+ * 锁屏唤醒
+ */
+public class PowerUtil {
+    private PowerManager.WakeLock wakeLock;
+    private KeyguardManager.KeyguardLock keyguardLock;
+
+    /**构造函数
+     * @param context 全局Context对象
+     */
+    public PowerUtil(Context context) {
+        //获取电源管理器对象
+        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        //获取PowerManager.WakeLock对象,后面的参数|表示同时传入两个值,最后的是调试用的Tag
+        wakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP,
+                "HongbaoWakelock");
+        //得到键盘锁管理器对象
+        KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
+        keyguardLock = km.newKeyguardLock("HongbaoKeyguardLock");
+    }
+
+    /**
+     *
+     */
+    private void acquire() {
+        wakeLock.acquire(1800000);
+        keyguardLock.disableKeyguard();
+    }
+
+    /**
+     *
+     */
+    private void release() {
+        if (wakeLock.isHeld()) {
+            wakeLock.release();
+            keyguardLock.reenableKeyguard();
+        }
+    }
+
+    /** 解锁屏幕
+     * @param isWake
+     */
+    public void handleWakeLock(boolean isWake) {
+        if (isWake) {
+            this.acquire();
+        } else {
+            this.release();
+        }
+    }
+}

+ 117 - 0
app/src/main/java/me/yoqi/app/wxredpacket/utils/UpdateTask.java

@@ -0,0 +1,117 @@
+package me.yoqi.app.wxredpacket.utils;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.widget.Toast;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.StatusLine;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.json.JSONObject;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import me.yoqi.app.wxredpacket.R;
+
+/**
+ * 更新
+ */
+public class UpdateTask extends AsyncTask<String, String, String> {
+    public static int count = 0;
+    private Context context;
+    private boolean isUpdateOnRelease;
+    public static final String updateUrl = "https://api.github.com/repos/geeeeeeeeek/WeChatLuckyMoney/releases/latest";
+
+    /** 构造函数
+     * @param context
+     * @param needUpdate
+     */
+    public UpdateTask(Context context, boolean needUpdate) {
+        this.context = context;
+        this.isUpdateOnRelease = needUpdate;
+        if (this.isUpdateOnRelease)
+            Toast.makeText(context, context.getString(R.string.checking_new_version), Toast.LENGTH_SHORT).show();
+    }
+
+    /** 后台自动下载
+     * @param uri 下载链接
+     * @return
+     */
+    @Override
+    protected String doInBackground(String... uri) {
+        HttpClient httpclient = new DefaultHttpClient();
+        HttpResponse response;
+        String responseString = null;
+        try {
+            response = httpclient.execute(new HttpGet(uri[0]));
+            StatusLine statusLine = response.getStatusLine();
+            if (statusLine.getStatusCode() == 200) {
+                ByteArrayOutputStream out = new ByteArrayOutputStream();
+                response.getEntity().writeTo(out);
+                responseString = out.toString();
+                out.close();
+            } else {
+                // Close the connection.
+                response.getEntity().getContent().close();
+                throw new IOException(statusLine.getReasonPhrase());
+            }
+        } catch (Exception e) {
+            return null;
+        }
+        return responseString;
+    }
+
+    /** doInBackground执行完后,会回调此方法,用于更新界面信息
+     * @param result
+     */
+    @Override
+    protected void onPostExecute(String result) {
+        super.onPostExecute(result);
+        try {
+            count += 1;
+            JSONObject release = new JSONObject(result);
+
+            // Get current version
+            PackageInfo pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
+            String version = pInfo.versionName;
+
+            String latestVersion = release.getString("tag_name");
+            boolean isPreRelease = release.getBoolean("prerelease");
+            if (!isPreRelease && version.compareToIgnoreCase(latestVersion) >= 0) {
+                // Your version is ahead of or same as the latest.
+                if (this.isUpdateOnRelease)
+                    Toast.makeText(context, R.string.update_already_latest, Toast.LENGTH_SHORT).show();
+            } else {
+                if (!isUpdateOnRelease) {
+                    Toast.makeText(context, context.getString(R.string.update_new_seg1) + latestVersion + context.getString(R.string.update_new_seg3), Toast.LENGTH_LONG).show();
+                    return;
+                }
+                // Need update.
+                String downloadUrl = release.getJSONArray("assets").getJSONObject(0).getString("browser_download_url");
+
+                // Give up on the fucking DownloadManager. The downloaded apk got renamed and unable to install. Fuck.
+                Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(downloadUrl));
+                browserIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                context.startActivity(browserIntent);
+                Toast.makeText(context, context.getString(R.string.update_new_seg1) + latestVersion + context.getString(R.string.update_new_seg2), Toast.LENGTH_LONG).show();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            if (this.isUpdateOnRelease)
+                Toast.makeText(context, R.string.update_error, Toast.LENGTH_LONG).show();
+        }
+    }
+
+    /**
+     * 更新
+     */
+    public void update() {
+        super.execute(updateUrl);
+    }
+}

+ 128 - 19
app/src/main/res/layout/activity_main.xml

@@ -1,19 +1,128 @@
-<?xml version="1.0" encoding="utf-8"?>
-<android.support.constraint.ConstraintLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    tools:context="me.yoqi.app.wxredpacket.MainActivity">
-
-    <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="Hello World!"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintLeft_toLeftOf="parent"
-        app:layout_constraintRight_toRightOf="parent"
-        app:layout_constraintTop_toTopOf="parent" />
-
-</android.support.constraint.ConstraintLayout>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                tools:context=".activities.MainActivity"
+                android:background="#fff">
+    <ImageView
+            android:layout_width="fill_parent"
+            android:layout_height="0dp"
+            android:id="@+id/main_action_bar_placeholder"
+            android:layout_alignParentTop="true"
+            android:background="#E46C62"/>
+    <LinearLayout
+            android:orientation="vertical"
+            android:layout_width="match_parent"
+            android:layout_height="fill_parent"
+            android:id="@+id/layout_header"
+            android:layout_above="@+id/layout_control"
+            android:background="#E46C62"
+            android:layout_marginBottom="12dp">
+
+        <TextView
+            android:id="@+id/textView"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="80dp"
+            android:gravity="center_vertical|center_horizontal"
+            android:text="@string/app_name"
+            android:textColor="#fff"
+            android:textIsSelectable="false"
+            android:textSize="28dp"
+            tools:text="wxRedPacket" />
+
+        <TextView
+            android:id="@+id/textView5"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="4dp"
+            android:gravity="center_vertical|center_horizontal"
+            android:text="@string/app_version"
+            android:textColor="#fff" />
+    </LinearLayout>
+    <ImageView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:background="@mipmap/bg_snow"
+            android:id="@+id/main_bg_snow"
+            android:layout_alignBottom="@id/layout_header"/>
+
+    <LinearLayout
+        android:id="@+id/layout_control"
+        android:layout_width="match_parent"
+        android:layout_height="110dp"
+        android:layout_alignParentBottom="true"
+        android:layout_marginBottom="20dp"
+        android:layout_marginLeft="12dp"
+        android:layout_marginRight="12dp"
+        android:layout_marginTop="8dp"
+        android:baselineAligned="false"
+        android:orientation="horizontal">
+
+        <LinearLayout
+            android:id="@+id/layout_control_accessibility"
+            style="?android:attr/borderlessButtonStyle"
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent"
+            android:layout_marginLeft="5dp"
+            android:layout_marginRight="5dp"
+            android:layout_weight="0.5"
+            android:background="#ffffff"
+            android:onClick="openAccessibility"
+            android:textColor="#858585"
+            android:textSize="20sp">
+
+            <ImageView
+                android:id="@+id/layout_control_accessibility_icon"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginBottom="6dp"
+                android:layout_marginTop="6dp"
+                android:background="@mipmap/ic_start"
+                android:baselineAligned="false" />
+
+            <TextView
+                android:id="@+id/layout_control_accessibility_text"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_margin="5dp"
+                android:text="@string/service_on"
+                android:textColor="#dfaa6a"
+                android:textSize="16sp"
+                android:textStyle="bold" />
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/layout_control_settings"
+            style="?android:attr/borderlessButtonStyle"
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent"
+            android:layout_marginLeft="5dp"
+            android:layout_marginStart="5dp"
+            android:layout_weight="0.5"
+            android:background="#ffffff"
+            android:onClick="openSettings"
+            android:orientation="vertical"
+            android:textColor="#858585"
+            android:textSize="20sp">
+
+            <ImageView
+                android:id="@+id/imageView4"
+                android:layout_width="24dp"
+                android:layout_height="24dp"
+                android:layout_margin="10dp"
+                android:src="@mipmap/ic_settings" />
+
+            <TextView
+                android:id="@+id/textView3"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_margin="5dp"
+                android:text="@string/settings"
+                android:textColor="#858585"
+                android:textSize="16sp"
+                android:textStyle="bold" />
+        </LinearLayout>
+    </LinearLayout>
+
+</RelativeLayout>

+ 42 - 0
app/src/main/res/layout/activity_preferences.xml

@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:tools="http://schemas.android.com/tools"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+    <RelativeLayout android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:background="#E46C62"
+                    tools:context=".SettingsActivity"
+    >
+        <TextView android:layout_width="wrap_content" android:layout_height="54dp"
+                  android:id="@+id/settings_bar"
+                  android:text="@string/preference" android:textColor="#fff"
+                  android:gravity="left|center_vertical|center_horizontal"
+                  android:elegantTextHeight="false" android:textSize="18sp"
+                  android:layout_toRightOf="@+id/preference_back"
+        />
+        <ImageView
+                android:layout_width="40dp"
+                android:layout_height="match_parent"
+                android:id="@+id/preference_back"
+                android:layout_marginLeft="4dp"
+                android:layout_marginRight="4dp"
+                android:clickable="true" android:onClick="performBack"
+                android:layout_alignBottom="@+id/settings_bar"
+                android:src="@mipmap/ic_back" android:padding="10dp"/>
+        <ImageView android:layout_width="40dp" android:layout_height="match_parent" android:id="@+id/imageView3"
+                   android:clickable="true"
+                   android:onClick="enterAccessibilityPage"
+                   android:layout_alignParentRight="true"
+                   android:layout_marginRight="10dp" android:layout_marginLeft="8dp"
+                   android:padding="8dp" android:src="@mipmap/ic_refresh"
+                   android:layout_alignBottom="@+id/settings_bar"/>
+    </RelativeLayout>
+    <FrameLayout android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:id="@+id/preferences_fragment"
+              tools:layout="@android:layout/simple_list_item_1"/>
+</LinearLayout>

+ 9 - 0
app/src/main/res/layout/activity_settings.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context="me.yoqi.app.wxredpacket.activities.SettingsActivity">
+
+</android.support.constraint.ConstraintLayout>

+ 47 - 0
app/src/main/res/layout/activity_webview.xml

@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:tools="http://schemas.android.com/tools"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent" tools:context=".activities.WebViewActivity">
+
+    <RelativeLayout android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:background="#E46C62"
+                    tools:context=".WebViewActivity"
+    >
+        <TextView android:layout_width="wrap_content" android:layout_height="54dp"
+                  android:id="@+id/webview_bar"
+                  android:text="@string/app_name"
+                  android:gravity="left|center_vertical|center_horizontal"
+                  android:elegantTextHeight="false" android:textSize="18sp"
+                  android:layout_toRightOf="@+id/webview_back"
+                  android:textColor="#fff"/>
+        <ImageView
+                android:layout_width="40dp"
+                android:layout_height="fill_parent"
+                android:id="@+id/webview_back"
+                android:layout_marginLeft="4dp"
+                android:layout_marginRight="4dp"
+                android:clickable="true" android:onClick="performBack"
+                android:layout_alignBottom="@+id/webview_bar"
+                android:src="@mipmap/ic_back" android:layout_alignParentBottom="false"
+                android:layout_alignParentTop="false" android:layout_alignParentLeft="true"
+                android:cropToPadding="false" android:padding="10dp"/>
+        <ImageView
+                android:layout_width="40dp"
+                android:layout_height="fill_parent"
+                android:id="@+id/webview_outlink"
+                android:layout_marginRight="10dp"
+                android:clickable="true" android:onClick="openLink"
+                android:layout_alignBottom="@+id/webview_bar"
+                android:src="@mipmap/ic_open_in_browser" android:layout_alignParentRight="true"
+                android:layout_marginLeft="8dp" android:padding="8dp"/>
+    </RelativeLayout>
+    <WebView
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:id="@+id/webView"
+            android:layout_gravity="center_horizontal"/>
+</LinearLayout>

+ 23 - 0
app/src/main/res/layout/preference_category.xml

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:paddingLeft="16dp"
+              android:paddingRight="?android:attr/scrollbarSize">
+    <RelativeLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:paddingTop="10dp"
+            android:layout_marginRight="10dp"
+            android:layout_weight="1">
+
+        <TextView android:id="@+android:id/title"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:singleLine="true"
+                  android:ellipsize="marquee"
+                  android:textColor="#E46C62"
+                  android:textSize="14sp"
+                  android:fadingEdge="horizontal"/>
+    </RelativeLayout>
+</LinearLayout>

+ 47 - 0
app/src/main/res/layout/preference_checkbox.xml

@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:gravity="center_vertical"
+              android:paddingLeft="16dp"
+              android:paddingRight="?android:attr/scrollbarSize" android:baselineAligned="false" android:paddingStart="16dp">
+
+
+
+    <RelativeLayout
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_marginRight="6dp"
+            android:layout_marginTop="12dp"
+            android:layout_marginBottom="12dp"
+            android:layout_weight="1">
+
+        <TextView android:id="@+android:id/title"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:singleLine="true"
+                  android:ellipsize="marquee"
+                  android:textColor="#666666"
+                  android:textSize="16sp"
+                  android:fadingEdge="horizontal" />
+
+        <TextView android:id="@+android:id/summary"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:layout_below="@android:id/title"
+                  android:layout_alignLeft="@android:id/title"
+                  android:textColor="#999"
+                  android:textSize="14sp"
+                  android:maxLines="4" />
+
+    </RelativeLayout>
+
+    <!-- Preference should place its actual preference widget here. -->
+    <LinearLayout android:id="@+android:id/widget_frame"
+                  android:layout_width="wrap_content"
+                  android:layout_height="match_parent"
+                  android:layout_marginRight="6dp"
+                  android:gravity="center"
+                  android:orientation="vertical" android:layout_marginEnd="6dp"/>
+
+</LinearLayout>

+ 17 - 0
app/src/main/res/layout/preference_seekbar.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+    <SeekBar
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:id="@+id/delay_seekBar" android:layout_margin="20dp" android:max="10"/>
+    <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="延迟0秒拆开红包"
+            android:id="@+id/pref_seekbar_textview" android:textColor="#666666" android:textSize="16sp"
+            android:layout_marginLeft="32dp" android:layout_marginRight="32dp" android:layout_marginBottom="20dp"/>
+</LinearLayout>

BIN
app/src/main/res/mipmap-mdpi/bg_snow.png


BIN
app/src/main/res/mipmap-mdpi/bg_snow_2.png


BIN
app/src/main/res/mipmap-mdpi/ic_back.png


BIN
app/src/main/res/mipmap-mdpi/ic_community.png


BIN
app/src/main/res/mipmap-mdpi/ic_github.png


BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png


BIN
app/src/main/res/mipmap-mdpi/ic_open_in_browser.png


BIN
app/src/main/res/mipmap-mdpi/ic_refresh.png


BIN
app/src/main/res/mipmap-mdpi/ic_settings.png


BIN
app/src/main/res/mipmap-mdpi/ic_start.png


BIN
app/src/main/res/mipmap-mdpi/ic_stop.png


BIN
app/src/main/res/mipmap-mdpi/ic_uber.jpg


BIN
app/src/main/res/mipmap-mdpi/open.png


+ 58 - 0
app/src/main/res/values-en/strings.xml

@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">wxRedPacket</string>
+    <string name="app_version">v1.0</string>
+    <string name="app_description">Instructions\n\n ○ Turn on the Accessibility switch\n ○ Go back to WeChat\n ○ Wait for money comes in</string>
+    <string name="preference">Preferences</string>
+    <string name="update_error">An error occurred. Please manually update from GitHub Release Page. (ฅ´ω`ฅ)</string>
+    <string name="update_new_seg1">New version found.(</string>
+    <string name="update_new_seg2">)Preparing download for you. ( /) V (\\ )</string>
+    <string name="update_new_seg3">)Please go to Settings to finish update. (ฅ´ω`ฅ)</string>
+    <string name="update_already_latest">You\'re using the latest version! (❁´▽`❁)</string>
+    <string name="service_off">Turn Off</string>
+    <string name="service_on">Turn On</string>
+    <string name="pref_watch_exclude_words_summary">Skip red packets with these phrases (separated by space)</string>
+    <string name="pref_comment_words_summary">Randomly choose reply from these phrases (separated by space)</string>
+    <string name="url_github_issues">https://github.com/geeeeeeeeek/WeChatLuckyMoney/issues</string>
+    <string name="home_banner_ad_unit_id">ca-app-pub-8428619221469478/4736153346</string>
+    <string name="webview_banner_ad_unit_id">ca-app-pub-8428619221469478/4163694548</string>
+    <string name="community">Community</string>
+    <string name="settings">Settings</string>
+    <string name="uber_ad_title">Your first trip is free up to ¥12</string>
+    <string name="uber_ad_text">Get Free Ride Now</string>
+    <string name="github_1">on GitHub</string>
+    <string name="github_2">this app on GitHub to support us</string>
+    <string name="star_2">Star</string>
+    <string name="star_1">★</string>
+    <string name="turn_on_toast">Click 「WeChat Lucky Money」&#160;</string>
+    <string name="turn_on_error_toast">An error occurred. Please manually open System Settings > Accessibility > WeChat Lucky Money. (ฅ´ω`ฅ)</string>
+    <string name="webview_github_title">Project Homepage on GitHub</string>
+    <string name="webview_uber_title">Uber Free Trip</string>
+    <string name="download_backend">Downloading…</string>
+    <string name="download_redirect">Redirecting to download page…</string>
+    <string name="download_hint">Download Uber by clicking \"普通下载\"</string>
+    <string name="watch_options">Watch Level</string>
+    <string name="watch_notifications">Watch system notifications</string>
+    <string name="watch_notifications_hint">Enter chat when a red packet is detected from WeChat notifications</string>
+    <string name="watch_list">Watch chat list</string>
+    <string name="watch_list_hint">Enter chat when a red packet is detected from chat list</string>
+    <string name="anti_block_options">Anti-block Options</string>
+    <string name="auto_open_packets">Open red packets automatically</string>
+    <string name="open_with_delay">Open red packets with a delay</string>
+    <string name="open_self">Open red packets I gave out</string>
+    <string name="skip_phrases">Skip red packets by words in the message</string>
+    <string name="about">About</string>
+    <string name="check_new_version">Check for updates</string>
+    <string name="help_and_feedback">Help and feedback</string>
+    <string name="help_hint">Submit issues on GitHub homepage</string>
+    <string name="labs">Labs</string>
+    <string name="snatch_on_lockscreen">Snatch red packets on lock screen</string>
+    <string name="snatch_on_lockscreen_hint">Will keep the app active in backend for 30 min. Please proceed with caution, since this might greatly increase your battery usage.</string>
+    <string name="auto_reply">Auto reply</string>
+    <string name="checking_new_version">Checking for updates…</string>
+    <string name="delay_instantly">Instantly&#160;</string>
+    <string name="delay_delay">Delay for&#160;</string>
+    <string name="delay_open">open red packets.</string>
+    <string name="delay_sec">&#160;seconds&#160;</string>
+    <string name="delay_then">and then&#160;</string>
+</resources>

+ 5 - 0
app/src/main/res/values-w820dp/dimens.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>

+ 55 - 1
app/src/main/res/values/strings.xml

@@ -1,3 +1,57 @@
 <resources>
-    <string name="app_name">wxRedPacket</string>
+    <string name="app_name">微信红包</string>
+    <string name="app_version">v1.0</string>
+    <string name="app_description">使用指南\n\n ○ 狠戳插件开关\n ○ 回到微信聊天\n ○ 坐等红包进账\n\n </string>
+    <string name="preference">偏好设置</string>
+    <string name="update_error">遇到一些问题,请前往GitHub手动更新喵(ฅ´ω`ฅ)</string>
+    <string name="update_new_seg1">发现新版本(</string>
+    <string name="update_new_seg2">),正在为您准备下载( /) V (\\ )</string>
+    <string name="update_new_seg3">),请进入设置完成更新(ฅ´ω`ฅ)</string>
+    <string name="update_already_latest">已经是最新版本了(❁´▽`❁)</string>
+    <string name="service_off">关闭插件</string>
+    <string name="service_on">开启插件</string>
+    <string name="pref_watch_exclude_words_summary">不拆开包含这些文字的红包(空格间隔)</string>
+    <string name="pref_comment_words_summary">随机选择下列感谢语(空格间隔)</string>
+    <string name="url_github_issues">https://github.com/geeeeeeeeek/WeChatLuckyMoney/issues</string>
+    <string name="home_banner_ad_unit_id">ca-app-pub-8428619221469478/4736153346</string>
+    <string name="webview_banner_ad_unit_id">ca-app-pub-8428619221469478/4163694548</string>
+    <string name="community">社区</string>
+    <string name="settings">设置</string>
+    <string name="uber_ad_title">首次乘车可获得 ¥12 优惠</string>
+    <string name="uber_ad_text">立即获得免费乘车机会</string>
+    <string name="github_1">在 GitHub 上</string>
+    <string name="github_2">本项目,支持应用开源</string>
+    <string name="star_2">Star</string>
+    <string name="star_1">★</string>
+    <string name="turn_on_toast">点击「微信红包」</string>
+    <string name="turn_on_error_toast">遇到一些问题,请手动打开系统设置>无障碍服务>微信红包(ฅ´ω`ฅ)</string>
+    <string name="webview_github_title">GitHub 项目主页</string>
+    <string name="webview_uber_title">Uber 优惠乘车</string>
+    <string name="download_backend">正在后台下载</string>
+    <string name="download_redirect">正在前往下载页面</string>
+    <string name="download_hint">点击\"普通下载\"获取 Uber</string>
+    <string name="watch_options">监视选项</string>
+    <string name="watch_notifications">监视系统通知</string>
+    <string name="watch_notifications_hint">读取新消息通知中的红包提示并进入聊天页</string>
+    <string name="watch_list">监视聊天列表</string>
+    <string name="watch_list_hint">读取聊天列表中的红包提示并进入聊天页</string>
+    <string name="anti_block_options">防封号选项</string>
+    <string name="auto_open_packets">自动拆开红包</string>
+    <string name="open_with_delay">延时拆开红包</string>
+    <string name="open_self">拆开自己发的红包</string>
+    <string name="skip_phrases">屏蔽红包文字</string>
+    <string name="about">关于应用</string>
+    <string name="check_new_version">检查新版本</string>
+    <string name="help_and_feedback">帮助与反馈</string>
+    <string name="help_hint">前往GitHub项目主页提交issue</string>
+    <string name="labs">实验功能</string>
+    <string name="snatch_on_lockscreen">息屏抢红包</string>
+    <string name="snatch_on_lockscreen_hint">保持30分钟后台活跃,可能会极大增加电量消耗,请谨慎使用</string>
+    <string name="auto_reply">拆开红包后自动回复</string>
+    <string name="checking_new_version">正在检查新版本……</string>
+    <string name="delay_instantly">立即</string>
+    <string name="delay_delay">延迟</string>
+    <string name="delay_open">拆开红包</string>
+    <string name="delay_sec">秒</string>
+    <string name="delay_then">然后</string>
 </resources>

+ 0 - 2
app/src/main/res/values/styles.xml

@@ -1,5 +1,4 @@
 <resources>
-
     <!-- Base application theme. -->
     <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
         <!-- Customize your theme here. -->
@@ -7,5 +6,4 @@
         <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
         <item name="colorAccent">@color/colorAccent</item>
     </style>
-
 </resources>

+ 11 - 0
app/src/main/res/xml/accessible_service_config.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<accessibility-service
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:description="@string/app_description"
+    android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged|typeNotificationStateChanged"
+    android:accessibilityFeedbackType="feedbackAllMask"
+    android:packageNames="com.tencent.mm"
+    android:notificationTimeout="10"
+    android:settingsActivity="me.yoqi.app.wxredpacket.activities.SettingsActivity"
+    android:accessibilityFlags="flagDefault"
+    android:canRetrieveWindowContent="true"/>

+ 36 - 0
app/src/main/res/xml/comment_preferences.xml

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen xmlns:tools="http://schemas.android.com/tools"
+                  xmlns:android="http://schemas.android.com/apk/res/android"
+                  tools:context=".SettingsActivity"
+                  android:icon="@null">
+    <PreferenceCategory
+            android:title="自动回复(Android 5.0以上系统)"
+            android:layout="@layout/preference_category">
+        <CheckBoxPreference
+            android:defaultValue="false"
+            android:key="pref_comment_switch"
+            android:layout="@layout/preference_checkbox"
+            android:persistent="false"
+            android:summary="在拆开其他人的红包后发送自动回复"
+            android:title="开启自动回复" />
+    </PreferenceCategory>
+    <PreferenceCategory
+            android:title="回复选项"
+            android:layout="@layout/preference_category">
+        <me.yoqi.app.wxredpacket.activities.SeekBarPreference
+                android:key="pref_comment_delay"
+                android:title="延时发送回复"
+                pref_kind="pref_comment_delay"
+                android:layout="@layout/preference_checkbox"/>
+        <CheckBoxPreference
+                android:key="pref_comment_at"
+                android:title="\@发红包的人"
+                android:layout="@layout/preference_checkbox"
+                android:defaultValue="false"/>
+        <EditTextPreference
+                android:key="pref_comment_words"
+                android:title="自定义回复内容"
+                android:summary="@string/pref_comment_words_summary"
+                android:layout="@layout/preference_checkbox"/>
+    </PreferenceCategory>
+</PreferenceScreen>

+ 65 - 0
app/src/main/res/xml/general_preferences.xml

@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen xmlns:tools="http://schemas.android.com/tools"
+                  xmlns:android="http://schemas.android.com/apk/res/android"
+                  tools:context=".SettingsActivity"
+                  android:icon="@null">
+    <PreferenceCategory
+            android:title="@string/watch_options"
+            android:layout="@layout/preference_category">
+        <CheckBoxPreference
+                android:key="pref_watch_notification"
+                android:title="@string/watch_notifications"
+                android:summary="@string/watch_notifications_hint"
+                android:defaultValue="true"
+                android:layout="@layout/preference_checkbox"/>
+        <CheckBoxPreference
+                android:key="pref_watch_list"
+                android:title="@string/watch_list"
+                android:summary="@string/watch_list_hint"
+                android:layout="@layout/preference_checkbox"
+                android:defaultValue="false"/>
+    </PreferenceCategory>
+    <PreferenceCategory
+            android:title="@string/anti_block_options"
+            android:layout="@layout/preference_category">
+        <CheckBoxPreference
+                android:key="pref_watch_chat"
+                android:title="@string/auto_open_packets"
+                android:layout="@layout/preference_checkbox"
+                android:defaultValue="true"/>
+        <me.yoqi.app.wxredpacket.activities.SeekBarPreference
+                android:key="pref_open_delay"
+                android:title="@string/open_with_delay"
+                pref_kind="pref_open_delay"
+                android:layout="@layout/preference_checkbox"/>
+        <CheckBoxPreference
+                android:key="pref_watch_self"
+                android:title="@string/open_self"
+                android:layout="@layout/preference_checkbox"
+                android:defaultValue="true"/>
+        <EditTextPreference
+                android:key="pref_watch_exclude_words"
+                android:title="@string/skip_phrases"
+                android:summary="@string/pref_watch_exclude_words_summary"
+                android:layout="@layout/preference_checkbox"/>
+    </PreferenceCategory>
+    <PreferenceCategory
+            android:title="@string/about"
+            android:layout="@layout/preference_category">
+        <Preference
+                android:key="pref_etc_check_update"
+                android:title="@string/check_new_version"
+                android:summary="http://git.yoqi.me:3000/lyq/wxRedPacket"
+                android:layout="@layout/preference_checkbox"/>
+    </PreferenceCategory>
+    <PreferenceCategory
+            android:title="@string/labs"
+            android:layout="@layout/preference_category">
+        <CheckBoxPreference
+                android:key="pref_watch_on_lock"
+                android:title="@string/snatch_on_lockscreen"
+                android:summary="@string/snatch_on_lockscreen_hint"
+                android:layout="@layout/preference_checkbox"
+                android:defaultValue="false"/>
+    </PreferenceCategory>
+</PreferenceScreen>

+ 7 - 0
app/src/main/res/xml/provider_paths.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<paths xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- /storage/emulated/0/Download/${applicationId}/.beta/apk-->
+    <external-path name="beta_external_path" path="Download/"/>
+    <!--/storage/emulated/0/Android/data/${applicationId}/files/apk/-->
+    <external-path name="beta_external_files_path" path="Android/data/"/>
+</paths>

+ 1 - 0
gradle.properties

@@ -15,3 +15,4 @@ org.gradle.jvmargs=-Xmx1024m
 # This option should only be used with decoupled projects. More details, visit
 # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
 # org.gradle.parallel=true
+android.useDeprecatedNdk=true