├── .gitignore ├── .idea ├── caches │ └── build_file_checksums.ser ├── codeStyles │ └── Project.xml ├── gradle.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── libs │ └── YoumiSdk_v7.5.1_2018-04-23.aar ├── proguard-rules.pro ├── release │ ├── app-release.apk │ └── output.json └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── lizhivscaomei │ │ └── myapp │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── lizhivscaomei │ │ │ └── myapp │ │ │ ├── AppCompatPreferenceActivity.java │ │ │ ├── LoginActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── MyApplication.java │ │ │ ├── RegistActivity.java │ │ │ ├── SettingsActivity.java │ │ │ ├── adapter │ │ │ └── BottomNavigationFragmentPagerAdapter.java │ │ │ ├── fragments │ │ │ ├── FindFragment.java │ │ │ ├── HomeFragment.java │ │ │ └── NotificationFragment.java │ │ │ └── module │ │ │ ├── entity │ │ │ └── LedgerEntity.java │ │ │ └── view │ │ │ ├── AddLegerActivity.java │ │ │ └── LedgerMainActivity.java │ └── res │ │ ├── drawable-v21 │ │ ├── ic_menu_camera.xml │ │ ├── ic_menu_gallery.xml │ │ ├── ic_menu_manage.xml │ │ ├── ic_menu_send.xml │ │ ├── ic_menu_share.xml │ │ └── ic_menu_slideshow.xml │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── ic_dashboard_black_24dp.xml │ │ ├── ic_home_black_24dp.xml │ │ ├── ic_info_black_24dp.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_notifications_black_24dp.xml │ │ ├── ic_sync_black_24dp.xml │ │ └── side_nav_bar.xml │ │ ├── layout │ │ ├── activity_add_leger.xml │ │ ├── activity_home.xml │ │ ├── activity_ledger_main.xml │ │ ├── activity_login.xml │ │ ├── activity_main.xml │ │ ├── activity_regist.xml │ │ ├── app_bar_main.xml │ │ ├── content_ledger_main.xml │ │ ├── content_main.xml │ │ ├── fragment_find.xml │ │ ├── fragment_home.xml │ │ ├── fragment_notification.xml │ │ ├── item_horizontal_icon_text.xml │ │ ├── item_horizontal_text3_line2.xml │ │ ├── item_vertical_icon_text.xml │ │ └── nav_header_main.xml │ │ ├── menu │ │ ├── activity_main_drawer.xml │ │ ├── main.xml │ │ └── navigation.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── drawables.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ ├── pref_data_sync.xml │ │ ├── pref_general.xml │ │ ├── pref_headers.xml │ │ └── pref_notification.xml │ └── test │ └── java │ └── com │ └── lizhivscaomei │ └── myapp │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/libraries 5 | /.idea/modules.xml 6 | /.idea/workspace.xml 7 | .DS_Store 8 | /build 9 | /captures 10 | .externalNativeBuild 11 | -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdkevindeng/SmartAgricultureAndroid/7a7e1394af802771b528e09efec5e2a99b3cfd03/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SmartAgricultureAndroid 2 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 27 5 | defaultConfig { 6 | applicationId "com.lizhivscaomei.myapp" 7 | minSdkVersion 15 8 | targetSdkVersion 27 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | vectorDrawables.useSupportLibrary = true 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | implementation fileTree(dir: 'libs', include: ['*.jar']) 24 | implementation 'com.android.support:appcompat-v7:27.1.1' 25 | implementation 'com.android.support:design:27.1.1' 26 | implementation 'com.android.support.constraint:constraint-layout:1.1.0' 27 | implementation 'com.android.support:support-v4:27.1.1' 28 | implementation 'com.android.support:support-vector-drawable:27.1.1' 29 | implementation 'com.android.support:recyclerview-v7:27.1.1' 30 | testImplementation 'junit:junit:4.12' 31 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 32 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 33 | implementation 'org.xutils:xutils:3.5.0' 34 | implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.7' 35 | /*有米广告*/ 36 | // implementation fileTree(dir: 'libs', include: ['*.aar']) 37 | } 38 | -------------------------------------------------------------------------------- /app/libs/YoumiSdk_v7.5.1_2018-04-23.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdkevindeng/SmartAgricultureAndroid/7a7e1394af802771b528e09efec5e2a99b3cfd03/app/libs/YoumiSdk_v7.5.1_2018-04-23.aar -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/release/app-release.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kdkevindeng/SmartAgricultureAndroid/7a7e1394af802771b528e09efec5e2a99b3cfd03/app/release/app-release.apk -------------------------------------------------------------------------------- /app/release/output.json: -------------------------------------------------------------------------------- 1 | [{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":1,"versionName":"1.0","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}] -------------------------------------------------------------------------------- /app/src/androidTest/java/com/lizhivscaomei/myapp/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.lizhivscaomei.myapp; 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 | * Instrumented 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() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.lizhivscaomei.myapp", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 32 | 35 | 38 | 42 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/lizhivscaomei/myapp/AppCompatPreferenceActivity.java: -------------------------------------------------------------------------------- 1 | package com.lizhivscaomei.myapp; 2 | 3 | import android.content.res.Configuration; 4 | import android.os.Bundle; 5 | import android.preference.PreferenceActivity; 6 | import android.support.annotation.LayoutRes; 7 | import android.support.annotation.Nullable; 8 | import android.support.v7.app.ActionBar; 9 | import android.support.v7.app.AppCompatDelegate; 10 | import android.support.v7.widget.Toolbar; 11 | import android.view.MenuInflater; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | 15 | /** 16 | * 系统设置 17 | * A {@link android.preference.PreferenceActivity} which implements and proxies the necessary calls 18 | * to be used with AppCompat. 19 | */ 20 | public abstract class AppCompatPreferenceActivity extends PreferenceActivity { 21 | 22 | private AppCompatDelegate mDelegate; 23 | 24 | @Override 25 | protected void onCreate(Bundle savedInstanceState) { 26 | getDelegate().installViewFactory(); 27 | getDelegate().onCreate(savedInstanceState); 28 | super.onCreate(savedInstanceState); 29 | } 30 | 31 | @Override 32 | protected void onPostCreate(Bundle savedInstanceState) { 33 | super.onPostCreate(savedInstanceState); 34 | getDelegate().onPostCreate(savedInstanceState); 35 | } 36 | 37 | public ActionBar getSupportActionBar() { 38 | return getDelegate().getSupportActionBar(); 39 | } 40 | 41 | public void setSupportActionBar(@Nullable Toolbar toolbar) { 42 | getDelegate().setSupportActionBar(toolbar); 43 | } 44 | 45 | @Override 46 | public MenuInflater getMenuInflater() { 47 | return getDelegate().getMenuInflater(); 48 | } 49 | 50 | @Override 51 | public void setContentView(@LayoutRes int layoutResID) { 52 | getDelegate().setContentView(layoutResID); 53 | } 54 | 55 | @Override 56 | public void setContentView(View view) { 57 | getDelegate().setContentView(view); 58 | } 59 | 60 | @Override 61 | public void setContentView(View view, ViewGroup.LayoutParams params) { 62 | getDelegate().setContentView(view, params); 63 | } 64 | 65 | @Override 66 | public void addContentView(View view, ViewGroup.LayoutParams params) { 67 | getDelegate().addContentView(view, params); 68 | } 69 | 70 | @Override 71 | protected void onPostResume() { 72 | super.onPostResume(); 73 | getDelegate().onPostResume(); 74 | } 75 | 76 | @Override 77 | protected void onTitleChanged(CharSequence title, int color) { 78 | super.onTitleChanged(title, color); 79 | getDelegate().setTitle(title); 80 | } 81 | 82 | @Override 83 | public void onConfigurationChanged(Configuration newConfig) { 84 | super.onConfigurationChanged(newConfig); 85 | getDelegate().onConfigurationChanged(newConfig); 86 | } 87 | 88 | @Override 89 | protected void onStop() { 90 | super.onStop(); 91 | getDelegate().onStop(); 92 | } 93 | 94 | @Override 95 | protected void onDestroy() { 96 | super.onDestroy(); 97 | getDelegate().onDestroy(); 98 | } 99 | 100 | public void invalidateOptionsMenu() { 101 | getDelegate().invalidateOptionsMenu(); 102 | } 103 | 104 | private AppCompatDelegate getDelegate() { 105 | if (mDelegate == null) { 106 | mDelegate = AppCompatDelegate.create(this, null); 107 | } 108 | return mDelegate; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /app/src/main/java/com/lizhivscaomei/myapp/LoginActivity.java: -------------------------------------------------------------------------------- 1 | package com.lizhivscaomei.myapp; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.annotation.TargetApi; 6 | import android.content.Intent; 7 | import android.content.pm.PackageManager; 8 | import android.support.annotation.NonNull; 9 | import android.support.design.widget.Snackbar; 10 | import android.support.v7.app.AppCompatActivity; 11 | import android.app.LoaderManager.LoaderCallbacks; 12 | 13 | import android.content.CursorLoader; 14 | import android.content.Loader; 15 | import android.database.Cursor; 16 | import android.net.Uri; 17 | import android.os.AsyncTask; 18 | 19 | import android.os.Build; 20 | import android.os.Bundle; 21 | import android.provider.ContactsContract; 22 | import android.text.TextUtils; 23 | import android.view.KeyEvent; 24 | import android.view.View; 25 | import android.view.View.OnClickListener; 26 | import android.view.inputmethod.EditorInfo; 27 | import android.widget.ArrayAdapter; 28 | import android.widget.AutoCompleteTextView; 29 | import android.widget.Button; 30 | import android.widget.EditText; 31 | import android.widget.TextView; 32 | 33 | import java.util.ArrayList; 34 | import java.util.List; 35 | 36 | import static android.Manifest.permission.READ_CONTACTS; 37 | 38 | /** 39 | * A login screen that offers login via email/password. 40 | */ 41 | public class LoginActivity extends AppCompatActivity implements LoaderCallbacks { 42 | 43 | /** 44 | * Id to identity READ_CONTACTS permission request. 45 | */ 46 | private static final int REQUEST_READ_CONTACTS = 0; 47 | 48 | /** 49 | * A dummy authentication store containing known user names and passwords. 50 | * TODO: remove after connecting to a real authentication system. 51 | */ 52 | private static final String[] DUMMY_CREDENTIALS = new String[]{ 53 | "foo@example.com:hello", "bar@example.com:world" 54 | }; 55 | /** 56 | * Keep track of the login task to ensure we can cancel it if requested. 57 | */ 58 | private UserLoginTask mAuthTask = null; 59 | 60 | // UI references. 61 | private AutoCompleteTextView mEmailView; 62 | private EditText mPasswordView; 63 | private View mProgressView; 64 | private View mLoginFormView; 65 | 66 | @Override 67 | protected void onCreate(Bundle savedInstanceState) { 68 | super.onCreate(savedInstanceState); 69 | setContentView(R.layout.activity_login); 70 | // Set up the login form. 71 | mEmailView = (AutoCompleteTextView) findViewById(R.id.email); 72 | populateAutoComplete(); 73 | 74 | mPasswordView = (EditText) findViewById(R.id.password); 75 | mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() { 76 | @Override 77 | public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) { 78 | if (id == EditorInfo.IME_ACTION_DONE || id == EditorInfo.IME_NULL) { 79 | attemptLogin(); 80 | return true; 81 | } 82 | return false; 83 | } 84 | }); 85 | 86 | Button mEmailSignInButton = (Button) findViewById(R.id.email_sign_in_button); 87 | mEmailSignInButton.setOnClickListener(new OnClickListener() { 88 | @Override 89 | public void onClick(View view) { 90 | attemptLogin(); 91 | } 92 | }); 93 | 94 | 95 | Button mregisterButton = (Button) findViewById(R.id.register_button); 96 | mregisterButton.setOnClickListener(new OnClickListener() { 97 | @Override 98 | public void onClick(View v) { 99 | startActivity(new Intent(LoginActivity.this,RegistActivity.class)); 100 | } 101 | }); 102 | mLoginFormView = findViewById(R.id.login_form); 103 | mProgressView = findViewById(R.id.login_progress); 104 | } 105 | 106 | private void populateAutoComplete() { 107 | if (!mayRequestContacts()) { 108 | return; 109 | } 110 | 111 | getLoaderManager().initLoader(0, null, this); 112 | } 113 | 114 | private boolean mayRequestContacts() { 115 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 116 | return true; 117 | } 118 | if (checkSelfPermission(READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) { 119 | return true; 120 | } 121 | if (shouldShowRequestPermissionRationale(READ_CONTACTS)) { 122 | Snackbar.make(mEmailView, R.string.permission_rationale, Snackbar.LENGTH_INDEFINITE) 123 | .setAction(android.R.string.ok, new View.OnClickListener() { 124 | @Override 125 | @TargetApi(Build.VERSION_CODES.M) 126 | public void onClick(View v) { 127 | requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS); 128 | } 129 | }); 130 | } else { 131 | requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS); 132 | } 133 | return false; 134 | } 135 | 136 | /** 137 | * Callback received when a permissions request has been completed. 138 | */ 139 | @Override 140 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, 141 | @NonNull int[] grantResults) { 142 | if (requestCode == REQUEST_READ_CONTACTS) { 143 | if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 144 | populateAutoComplete(); 145 | } 146 | } 147 | } 148 | 149 | 150 | /** 151 | * Attempts to sign in or register the account specified by the login form. 152 | * If there are form errors (invalid email, missing fields, etc.), the 153 | * errors are presented and no actual login attempt is made. 154 | */ 155 | private void attemptLogin() { 156 | if (mAuthTask != null) { 157 | return; 158 | } 159 | 160 | // Reset errors. 161 | mEmailView.setError(null); 162 | mPasswordView.setError(null); 163 | 164 | // Store values at the time of the login attempt. 165 | String email = mEmailView.getText().toString(); 166 | String password = mPasswordView.getText().toString(); 167 | 168 | boolean cancel = false; 169 | View focusView = null; 170 | 171 | // Check for a valid password, if the user entered one. 172 | if (!TextUtils.isEmpty(password) && !isPasswordValid(password)) { 173 | mPasswordView.setError(getString(R.string.error_invalid_password)); 174 | focusView = mPasswordView; 175 | cancel = true; 176 | } 177 | 178 | // Check for a valid email address. 179 | if (TextUtils.isEmpty(email)) { 180 | mEmailView.setError(getString(R.string.error_field_required)); 181 | focusView = mEmailView; 182 | cancel = true; 183 | } else if (!isEmailValid(email)) { 184 | mEmailView.setError(getString(R.string.error_invalid_email)); 185 | focusView = mEmailView; 186 | cancel = true; 187 | } 188 | 189 | if (cancel) { 190 | // There was an error; don't attempt login and focus the first 191 | // form field with an error. 192 | focusView.requestFocus(); 193 | } else { 194 | // Show a progress spinner, and kick off a background task to 195 | // perform the user login attempt. 196 | showProgress(true); 197 | mAuthTask = new UserLoginTask(email, password); 198 | mAuthTask.execute((Void) null); 199 | } 200 | } 201 | 202 | private boolean isEmailValid(String email) { 203 | //TODO: Replace this with your own logic 204 | return email.contains("@"); 205 | } 206 | 207 | private boolean isPasswordValid(String password) { 208 | //TODO: Replace this with your own logic 209 | return password.length() > 4; 210 | } 211 | 212 | /** 213 | * Shows the progress UI and hides the login form. 214 | */ 215 | @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) 216 | private void showProgress(final boolean show) { 217 | // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow 218 | // for very easy animations. If available, use these APIs to fade-in 219 | // the progress spinner. 220 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) { 221 | int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime); 222 | 223 | mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); 224 | mLoginFormView.animate().setDuration(shortAnimTime).alpha( 225 | show ? 0 : 1).setListener(new AnimatorListenerAdapter() { 226 | @Override 227 | public void onAnimationEnd(Animator animation) { 228 | mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); 229 | } 230 | }); 231 | 232 | mProgressView.setVisibility(show ? View.VISIBLE : View.GONE); 233 | mProgressView.animate().setDuration(shortAnimTime).alpha( 234 | show ? 1 : 0).setListener(new AnimatorListenerAdapter() { 235 | @Override 236 | public void onAnimationEnd(Animator animation) { 237 | mProgressView.setVisibility(show ? View.VISIBLE : View.GONE); 238 | } 239 | }); 240 | } else { 241 | // The ViewPropertyAnimator APIs are not available, so simply show 242 | // and hide the relevant UI components. 243 | mProgressView.setVisibility(show ? View.VISIBLE : View.GONE); 244 | mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); 245 | } 246 | } 247 | 248 | @Override 249 | public Loader onCreateLoader(int i, Bundle bundle) { 250 | return new CursorLoader(this, 251 | // Retrieve data rows for the device user's 'profile' contact. 252 | Uri.withAppendedPath(ContactsContract.Profile.CONTENT_URI, 253 | ContactsContract.Contacts.Data.CONTENT_DIRECTORY), ProfileQuery.PROJECTION, 254 | 255 | // Select only email addresses. 256 | ContactsContract.Contacts.Data.MIMETYPE + 257 | " = ?", new String[]{ContactsContract.CommonDataKinds.Email 258 | .CONTENT_ITEM_TYPE}, 259 | 260 | // Show primary email addresses first. Note that there won't be 261 | // a primary email address if the user hasn't specified one. 262 | ContactsContract.Contacts.Data.IS_PRIMARY + " DESC"); 263 | } 264 | 265 | @Override 266 | public void onLoadFinished(Loader cursorLoader, Cursor cursor) { 267 | List emails = new ArrayList<>(); 268 | cursor.moveToFirst(); 269 | while (!cursor.isAfterLast()) { 270 | emails.add(cursor.getString(ProfileQuery.ADDRESS)); 271 | cursor.moveToNext(); 272 | } 273 | 274 | addEmailsToAutoComplete(emails); 275 | } 276 | 277 | @Override 278 | public void onLoaderReset(Loader cursorLoader) { 279 | 280 | } 281 | 282 | private void addEmailsToAutoComplete(List emailAddressCollection) { 283 | //Create adapter to tell the AutoCompleteTextView what to show in its dropdown list. 284 | ArrayAdapter adapter = 285 | new ArrayAdapter<>(LoginActivity.this, 286 | android.R.layout.simple_dropdown_item_1line, emailAddressCollection); 287 | 288 | mEmailView.setAdapter(adapter); 289 | } 290 | 291 | 292 | private interface ProfileQuery { 293 | String[] PROJECTION = { 294 | ContactsContract.CommonDataKinds.Email.ADDRESS, 295 | ContactsContract.CommonDataKinds.Email.IS_PRIMARY, 296 | }; 297 | 298 | int ADDRESS = 0; 299 | int IS_PRIMARY = 1; 300 | } 301 | 302 | /** 303 | * Represents an asynchronous login/registration task used to authenticate 304 | * the user. 305 | */ 306 | public class UserLoginTask extends AsyncTask { 307 | 308 | private final String mEmail; 309 | private final String mPassword; 310 | 311 | UserLoginTask(String email, String password) { 312 | mEmail = email; 313 | mPassword = password; 314 | } 315 | 316 | @Override 317 | protected Boolean doInBackground(Void... params) { 318 | // TODO: attempt authentication against a network service. 319 | 320 | try { 321 | // Simulate network access. 322 | Thread.sleep(2000); 323 | } catch (InterruptedException e) { 324 | return false; 325 | } 326 | 327 | for (String credential : DUMMY_CREDENTIALS) { 328 | String[] pieces = credential.split(":"); 329 | if (pieces[0].equals(mEmail)) { 330 | // Account exists, return true if the password matches. 331 | return pieces[1].equals(mPassword); 332 | } 333 | } 334 | 335 | // TODO: register the new account here. 336 | return true; 337 | } 338 | 339 | @Override 340 | protected void onPostExecute(final Boolean success) { 341 | mAuthTask = null; 342 | showProgress(false); 343 | 344 | if (success) { 345 | finish(); 346 | } else { 347 | mPasswordView.setError(getString(R.string.error_incorrect_password)); 348 | mPasswordView.requestFocus(); 349 | } 350 | } 351 | 352 | @Override 353 | protected void onCancelled() { 354 | mAuthTask = null; 355 | showProgress(false); 356 | } 357 | } 358 | } 359 | 360 | -------------------------------------------------------------------------------- /app/src/main/java/com/lizhivscaomei/myapp/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.lizhivscaomei.myapp; 2 | 3 | import android.content.Intent; 4 | import android.net.Uri; 5 | import android.os.Bundle; 6 | import android.os.Handler; 7 | import android.support.annotation.NonNull; 8 | import android.support.design.widget.BottomNavigationView; 9 | import android.support.design.widget.FloatingActionButton; 10 | import android.support.design.widget.Snackbar; 11 | import android.support.v4.view.ViewPager; 12 | import android.view.KeyEvent; 13 | import android.view.View; 14 | import android.support.design.widget.NavigationView; 15 | import android.support.v4.view.GravityCompat; 16 | import android.support.v4.widget.DrawerLayout; 17 | import android.support.v7.app.ActionBarDrawerToggle; 18 | import android.support.v7.app.AppCompatActivity; 19 | import android.support.v7.widget.Toolbar; 20 | import android.view.Menu; 21 | import android.view.MenuItem; 22 | import android.widget.TextView; 23 | import android.widget.Toast; 24 | 25 | import com.lizhivscaomei.myapp.adapter.BottomNavigationFragmentPagerAdapter; 26 | import com.lizhivscaomei.myapp.fragments.FindFragment; 27 | import com.lizhivscaomei.myapp.fragments.HomeFragment; 28 | import com.lizhivscaomei.myapp.fragments.NotificationFragment; 29 | 30 | import asdf.jwe.gh.AdManager; 31 | import asdf.jwe.gh.os.OffersManager; 32 | 33 | /** 34 | * 主页 35 | */ 36 | public class MainActivity extends AppCompatActivity 37 | implements NavigationView.OnNavigationItemSelectedListener,FindFragment.OnFragmentInteractionListener,HomeFragment.OnFragmentInteractionListener,NotificationFragment.OnFragmentInteractionListener { 38 | private TextView mTextMessage; 39 | private boolean mIsExit; 40 | @Override 41 | protected void onCreate(Bundle savedInstanceState) { 42 | super.onCreate(savedInstanceState); 43 | setContentView(R.layout.activity_main); 44 | 45 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 46 | setSupportActionBar(toolbar); 47 | 48 | DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); 49 | ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( 50 | this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close); 51 | drawer.addDrawerListener(toggle); 52 | toggle.syncState(); 53 | 54 | NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view); 55 | navigationView.setNavigationItemSelectedListener(this); 56 | //ViewPager 57 | final ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager); 58 | viewPager.setAdapter(new BottomNavigationFragmentPagerAdapter(getSupportFragmentManager())); 59 | //底部导航按钮和事件监听 60 | BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation); 61 | viewPager.setCurrentItem(0); 62 | navigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() { 63 | 64 | @Override 65 | public boolean onNavigationItemSelected(@NonNull MenuItem item) { 66 | switch (item.getItemId()) { 67 | case R.id.navigation_home: 68 | viewPager.setCurrentItem(0); 69 | return true; 70 | case R.id.navigation_notifications: 71 | viewPager.setCurrentItem(1); 72 | return true; 73 | case R.id.navigation_dashboard: 74 | viewPager.setCurrentItem(2); 75 | return true; 76 | } 77 | return false; 78 | } 79 | }); 80 | } 81 | 82 | @Override 83 | public void onBackPressed() { 84 | DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); 85 | if (drawer.isDrawerOpen(GravityCompat.START)) { 86 | drawer.closeDrawer(GravityCompat.START); 87 | } else { 88 | super.onBackPressed(); 89 | } 90 | } 91 | 92 | @Override 93 | public boolean onCreateOptionsMenu(Menu menu) { 94 | // Inflate the menu; this adds items to the action bar if it is present. 95 | getMenuInflater().inflate(R.menu.main, menu); 96 | return true; 97 | } 98 | 99 | @Override 100 | public boolean onOptionsItemSelected(MenuItem item) { 101 | // Handle action bar item clicks here. The action bar will 102 | // automatically handle clicks on the Home/Up button, so long 103 | // as you specify a parent activity in AndroidManifest.xml. 104 | int id = item.getItemId(); 105 | 106 | //noinspection SimplifiableIfStatement 107 | if (id == R.id.action_scan) { 108 | //跳转到扫码页面 109 | return true; 110 | } else if (id == R.id.action_search) { 111 | //跳转到查询页面 112 | return true; 113 | } 114 | 115 | return super.onOptionsItemSelected(item); 116 | } 117 | 118 | @SuppressWarnings("StatementWithEmptyBody") 119 | @Override 120 | public boolean onNavigationItemSelected(MenuItem item) { 121 | // Handle navigation view item clicks here. 122 | int id = item.getItemId(); 123 | 124 | if (id == R.id.nav_camera) { 125 | // Handle the camera action 126 | } else if (id == R.id.nav_gallery) { 127 | 128 | } else if (id == R.id.nav_slideshow) { 129 | 130 | } else if (id == R.id.nav_setting) { 131 | startActivity(new Intent(MainActivity.this, SettingsActivity.class)); 132 | } else if (id == R.id.nav_send) { 133 | //清理登录信息 134 | //跳转登录页面 135 | startActivity(new Intent(MainActivity.this, LoginActivity.class)); 136 | 137 | } 138 | 139 | DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); 140 | drawer.closeDrawer(GravityCompat.START); 141 | return true; 142 | } 143 | 144 | @Override 145 | public void onFragmentInteraction(Uri uri) { 146 | 147 | } 148 | /** 149 | * 双击返回键退出 150 | */ 151 | @Override 152 | public boolean onKeyDown(int keyCode, KeyEvent event) { 153 | if (keyCode == KeyEvent.KEYCODE_BACK) { 154 | if (mIsExit) { 155 | this.finish(); 156 | } else { 157 | Toast.makeText(this, "再按一次退出", Toast.LENGTH_SHORT).show(); 158 | mIsExit = true; 159 | new Handler().postDelayed(new Runnable() { 160 | @Override 161 | public void run() { 162 | mIsExit = false; 163 | } 164 | }, 2000); 165 | } 166 | return true; 167 | } 168 | return super.onKeyDown(keyCode, event); 169 | } 170 | 171 | @Override 172 | protected void onDestroy() { 173 | super.onDestroy(); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /app/src/main/java/com/lizhivscaomei/myapp/MyApplication.java: -------------------------------------------------------------------------------- 1 | package com.lizhivscaomei.myapp; 2 | 3 | import android.app.Application; 4 | 5 | import org.xutils.DbManager; 6 | import org.xutils.db.table.TableEntity; 7 | import org.xutils.x; 8 | 9 | import java.io.File; 10 | 11 | public class MyApplication extends Application { 12 | @Override 13 | public void onCreate() { 14 | super.onCreate(); 15 | x.Ext.init(this); 16 | x.Ext.setDebug(false); //输出debug日志,开启会影响性能 17 | 18 | } 19 | 20 | public static DbManager getXDbManager() { 21 | /** 22 | * 初始化DaoConfig配置 23 | */ 24 | DbManager.DaoConfig daoConfig = new DbManager.DaoConfig() 25 | //设置数据库名,默认xutils.db 26 | .setDbName("smartAgriculture.db") 27 | //设置数据库的版本号 28 | .setDbVersion(1) 29 | //设置数据库打开的监听 30 | .setDbOpenListener(new DbManager.DbOpenListener() { 31 | @Override 32 | public void onDbOpened(DbManager db) { 33 | //开启数据库支持多线程操作,提升性能,对写入加速提升巨大 34 | db.getDatabase().enableWriteAheadLogging(); 35 | } 36 | }) 37 | //设置数据库更新的监听 38 | .setDbUpgradeListener(new DbManager.DbUpgradeListener() { 39 | @Override 40 | public void onUpgrade(DbManager db, int oldVersion, int newVersion) { 41 | } 42 | }) 43 | //设置表创建的监听 44 | .setTableCreateListener(new DbManager.TableCreateListener() { 45 | @Override 46 | public void onTableCreated(DbManager db, TableEntity table) { 47 | 48 | } 49 | }); 50 | 51 | return x.getDb(daoConfig); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/lizhivscaomei/myapp/RegistActivity.java: -------------------------------------------------------------------------------- 1 | package com.lizhivscaomei.myapp; 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 | 32 | import java.util.ArrayList; 33 | import java.util.List; 34 | 35 | import static android.Manifest.permission.READ_CONTACTS; 36 | 37 | /** 38 | * A login screen that offers login via email/password. 39 | */ 40 | public class RegistActivity extends AppCompatActivity implements LoaderCallbacks { 41 | 42 | /** 43 | * Id to identity READ_CONTACTS permission request. 44 | */ 45 | private static final int REQUEST_READ_CONTACTS = 0; 46 | 47 | /** 48 | * A dummy authentication store containing known user names and passwords. 49 | * TODO: remove after connecting to a real authentication system. 50 | */ 51 | private static final String[] DUMMY_CREDENTIALS = new String[]{ 52 | "foo@example.com:hello", "bar@example.com:world" 53 | }; 54 | /** 55 | * Keep track of the login task to ensure we can cancel it if requested. 56 | */ 57 | private UserLoginTask mAuthTask = null; 58 | 59 | // UI references. 60 | private AutoCompleteTextView mEmailView; 61 | private EditText mPasswordView; 62 | private View mProgressView; 63 | private View mLoginFormView; 64 | 65 | @Override 66 | protected void onCreate(Bundle savedInstanceState) { 67 | super.onCreate(savedInstanceState); 68 | setContentView(R.layout.activity_regist); 69 | // Set up the login form. 70 | mEmailView = (AutoCompleteTextView) findViewById(R.id.email); 71 | populateAutoComplete(); 72 | 73 | mPasswordView = (EditText) findViewById(R.id.password); 74 | mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() { 75 | @Override 76 | public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) { 77 | if (id == EditorInfo.IME_ACTION_DONE || id == EditorInfo.IME_NULL) { 78 | attemptLogin(); 79 | return true; 80 | } 81 | return false; 82 | } 83 | }); 84 | 85 | Button mEmailSignInButton = (Button) findViewById(R.id.email_sign_in_button); 86 | mEmailSignInButton.setOnClickListener(new OnClickListener() { 87 | @Override 88 | public void onClick(View view) { 89 | attemptLogin(); 90 | } 91 | }); 92 | 93 | mLoginFormView = findViewById(R.id.login_form); 94 | mProgressView = findViewById(R.id.login_progress); 95 | } 96 | 97 | private void populateAutoComplete() { 98 | if (!mayRequestContacts()) { 99 | return; 100 | } 101 | 102 | getLoaderManager().initLoader(0, null, this); 103 | } 104 | 105 | private boolean mayRequestContacts() { 106 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 107 | return true; 108 | } 109 | if (checkSelfPermission(READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) { 110 | return true; 111 | } 112 | if (shouldShowRequestPermissionRationale(READ_CONTACTS)) { 113 | Snackbar.make(mEmailView, R.string.permission_rationale, Snackbar.LENGTH_INDEFINITE) 114 | .setAction(android.R.string.ok, new View.OnClickListener() { 115 | @Override 116 | @TargetApi(Build.VERSION_CODES.M) 117 | public void onClick(View v) { 118 | requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS); 119 | } 120 | }); 121 | } else { 122 | requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS); 123 | } 124 | return false; 125 | } 126 | 127 | /** 128 | * Callback received when a permissions request has been completed. 129 | */ 130 | @Override 131 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, 132 | @NonNull int[] grantResults) { 133 | if (requestCode == REQUEST_READ_CONTACTS) { 134 | if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 135 | populateAutoComplete(); 136 | } 137 | } 138 | } 139 | 140 | 141 | /** 142 | * Attempts to sign in or register the account specified by the login form. 143 | * If there are form errors (invalid email, missing fields, etc.), the 144 | * errors are presented and no actual login attempt is made. 145 | */ 146 | private void attemptLogin() { 147 | if (mAuthTask != null) { 148 | return; 149 | } 150 | 151 | // Reset errors. 152 | mEmailView.setError(null); 153 | mPasswordView.setError(null); 154 | 155 | // Store values at the time of the login attempt. 156 | String email = mEmailView.getText().toString(); 157 | String password = mPasswordView.getText().toString(); 158 | 159 | boolean cancel = false; 160 | View focusView = null; 161 | 162 | // Check for a valid password, if the user entered one. 163 | if (!TextUtils.isEmpty(password) && !isPasswordValid(password)) { 164 | mPasswordView.setError(getString(R.string.error_invalid_password)); 165 | focusView = mPasswordView; 166 | cancel = true; 167 | } 168 | 169 | // Check for a valid email address. 170 | if (TextUtils.isEmpty(email)) { 171 | mEmailView.setError(getString(R.string.error_field_required)); 172 | focusView = mEmailView; 173 | cancel = true; 174 | } else if (!isEmailValid(email)) { 175 | mEmailView.setError(getString(R.string.error_invalid_email)); 176 | focusView = mEmailView; 177 | cancel = true; 178 | } 179 | 180 | if (cancel) { 181 | // There was an error; don't attempt login and focus the first 182 | // form field with an error. 183 | focusView.requestFocus(); 184 | } else { 185 | // Show a progress spinner, and kick off a background task to 186 | // perform the user login attempt. 187 | showProgress(true); 188 | mAuthTask = new UserLoginTask(email, password); 189 | mAuthTask.execute((Void) null); 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<>(RegistActivity.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/java/com/lizhivscaomei/myapp/SettingsActivity.java: -------------------------------------------------------------------------------- 1 | package com.lizhivscaomei.myapp; 2 | 3 | import android.annotation.TargetApi; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.res.Configuration; 7 | import android.media.Ringtone; 8 | import android.media.RingtoneManager; 9 | import android.net.Uri; 10 | import android.os.Build; 11 | import android.os.Bundle; 12 | import android.preference.ListPreference; 13 | import android.preference.Preference; 14 | import android.preference.PreferenceActivity; 15 | import android.support.v7.app.ActionBar; 16 | import android.preference.PreferenceFragment; 17 | import android.preference.PreferenceManager; 18 | import android.preference.RingtonePreference; 19 | import android.text.TextUtils; 20 | import android.view.MenuItem; 21 | 22 | import java.util.List; 23 | 24 | /** 25 | *系统设置 26 | * A {@link PreferenceActivity} that presents a set of application settings. On 27 | * handset devices, settings are presented as a single list. On tablets, 28 | * settings are split by category, with category headers shown to the left of 29 | * the list of settings. 30 | *

31 | * See 32 | * Android Design: Settings for design guidelines and the Settings 34 | * API Guide for more information on developing a Settings UI. 35 | */ 36 | public class SettingsActivity extends AppCompatPreferenceActivity { 37 | 38 | /** 39 | * A preference value change listener that updates the preference's summary 40 | * to reflect its new value. 41 | */ 42 | private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() { 43 | @Override 44 | public boolean onPreferenceChange(Preference preference, Object value) { 45 | String stringValue = value.toString(); 46 | 47 | if (preference instanceof ListPreference) { 48 | // For list preferences, look up the correct display value in 49 | // the preference's 'entries' list. 50 | ListPreference listPreference = (ListPreference) preference; 51 | int index = listPreference.findIndexOfValue(stringValue); 52 | 53 | // Set the summary to reflect the new value. 54 | preference.setSummary( 55 | index >= 0 56 | ? listPreference.getEntries()[index] 57 | : null); 58 | 59 | } else if (preference instanceof RingtonePreference) { 60 | // For ringtone preferences, look up the correct display value 61 | // using RingtoneManager. 62 | if (TextUtils.isEmpty(stringValue)) { 63 | // Empty values correspond to 'silent' (no ringtone). 64 | preference.setSummary(R.string.pref_ringtone_silent); 65 | 66 | } else { 67 | Ringtone ringtone = RingtoneManager.getRingtone( 68 | preference.getContext(), Uri.parse(stringValue)); 69 | 70 | if (ringtone == null) { 71 | // Clear the summary if there was a lookup error. 72 | preference.setSummary(null); 73 | } else { 74 | // Set the summary to reflect the new ringtone display 75 | // name. 76 | String name = ringtone.getTitle(preference.getContext()); 77 | preference.setSummary(name); 78 | } 79 | } 80 | 81 | } else { 82 | // For all other preferences, set the summary to the value's 83 | // simple string representation. 84 | preference.setSummary(stringValue); 85 | } 86 | return true; 87 | } 88 | }; 89 | 90 | /** 91 | * Helper method to determine if the device has an extra-large screen. For 92 | * example, 10" tablets are extra-large. 93 | */ 94 | private static boolean isXLargeTablet(Context context) { 95 | return (context.getResources().getConfiguration().screenLayout 96 | & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE; 97 | } 98 | 99 | /** 100 | * Binds a preference's summary to its value. More specifically, when the 101 | * preference's value is changed, its summary (line of text below the 102 | * preference title) is updated to reflect the value. The summary is also 103 | * immediately updated upon calling this method. The exact display format is 104 | * dependent on the type of preference. 105 | * 106 | * @see #sBindPreferenceSummaryToValueListener 107 | */ 108 | private static void bindPreferenceSummaryToValue(Preference preference) { 109 | // Set the listener to watch for value changes. 110 | preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener); 111 | 112 | // Trigger the listener immediately with the preference's 113 | // current value. 114 | sBindPreferenceSummaryToValueListener.onPreferenceChange(preference, 115 | PreferenceManager 116 | .getDefaultSharedPreferences(preference.getContext()) 117 | .getString(preference.getKey(), "")); 118 | } 119 | 120 | @Override 121 | protected void onCreate(Bundle savedInstanceState) { 122 | super.onCreate(savedInstanceState); 123 | setupActionBar(); 124 | } 125 | 126 | /** 127 | * Set up the {@link android.app.ActionBar}, if the API is available. 128 | */ 129 | private void setupActionBar() { 130 | ActionBar actionBar = getSupportActionBar(); 131 | if (actionBar != null) { 132 | // Show the Up button in the action bar. 133 | actionBar.setDisplayHomeAsUpEnabled(true); 134 | } 135 | } 136 | 137 | /** 138 | * {@inheritDoc} 139 | */ 140 | @Override 141 | public boolean onIsMultiPane() { 142 | return isXLargeTablet(this); 143 | } 144 | 145 | /** 146 | * {@inheritDoc} 147 | */ 148 | @Override 149 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 150 | public void onBuildHeaders(List

target) { 151 | loadHeadersFromResource(R.xml.pref_headers, target); 152 | } 153 | 154 | /** 155 | * This method stops fragment injection in malicious applications. 156 | * Make sure to deny any unknown fragments here. 157 | */ 158 | protected boolean isValidFragment(String fragmentName) { 159 | return PreferenceFragment.class.getName().equals(fragmentName) 160 | || GeneralPreferenceFragment.class.getName().equals(fragmentName) 161 | || DataSyncPreferenceFragment.class.getName().equals(fragmentName) 162 | || NotificationPreferenceFragment.class.getName().equals(fragmentName); 163 | } 164 | 165 | /** 166 | * This fragment shows general preferences only. It is used when the 167 | * activity is showing a two-pane settings UI. 168 | */ 169 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 170 | public static class GeneralPreferenceFragment extends PreferenceFragment { 171 | @Override 172 | public void onCreate(Bundle savedInstanceState) { 173 | super.onCreate(savedInstanceState); 174 | addPreferencesFromResource(R.xml.pref_general); 175 | setHasOptionsMenu(true); 176 | 177 | // Bind the summaries of EditText/List/Dialog/Ringtone preferences 178 | // to their values. When their values change, their summaries are 179 | // updated to reflect the new value, per the Android Design 180 | // guidelines. 181 | bindPreferenceSummaryToValue(findPreference("example_text")); 182 | bindPreferenceSummaryToValue(findPreference("example_list")); 183 | } 184 | 185 | @Override 186 | public boolean onOptionsItemSelected(MenuItem item) { 187 | int id = item.getItemId(); 188 | if (id == android.R.id.home) { 189 | startActivity(new Intent(getActivity(), SettingsActivity.class)); 190 | return true; 191 | } 192 | return super.onOptionsItemSelected(item); 193 | } 194 | } 195 | 196 | /** 197 | * This fragment shows notification preferences only. It is used when the 198 | * activity is showing a two-pane settings UI. 199 | */ 200 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 201 | public static class NotificationPreferenceFragment extends PreferenceFragment { 202 | @Override 203 | public void onCreate(Bundle savedInstanceState) { 204 | super.onCreate(savedInstanceState); 205 | addPreferencesFromResource(R.xml.pref_notification); 206 | setHasOptionsMenu(true); 207 | 208 | // Bind the summaries of EditText/List/Dialog/Ringtone preferences 209 | // to their values. When their values change, their summaries are 210 | // updated to reflect the new value, per the Android Design 211 | // guidelines. 212 | bindPreferenceSummaryToValue(findPreference("notifications_new_message_ringtone")); 213 | } 214 | 215 | @Override 216 | public boolean onOptionsItemSelected(MenuItem item) { 217 | int id = item.getItemId(); 218 | if (id == android.R.id.home) { 219 | startActivity(new Intent(getActivity(), SettingsActivity.class)); 220 | return true; 221 | } 222 | return super.onOptionsItemSelected(item); 223 | } 224 | } 225 | 226 | /** 227 | * This fragment shows data and sync preferences only. It is used when the 228 | * activity is showing a two-pane settings UI. 229 | */ 230 | @TargetApi(Build.VERSION_CODES.HONEYCOMB) 231 | public static class DataSyncPreferenceFragment extends PreferenceFragment { 232 | @Override 233 | public void onCreate(Bundle savedInstanceState) { 234 | super.onCreate(savedInstanceState); 235 | addPreferencesFromResource(R.xml.pref_data_sync); 236 | setHasOptionsMenu(true); 237 | 238 | // Bind the summaries of EditText/List/Dialog/Ringtone preferences 239 | // to their values. When their values change, their summaries are 240 | // updated to reflect the new value, per the Android Design 241 | // guidelines. 242 | bindPreferenceSummaryToValue(findPreference("sync_frequency")); 243 | } 244 | 245 | @Override 246 | public boolean onOptionsItemSelected(MenuItem item) { 247 | int id = item.getItemId(); 248 | if (id == android.R.id.home) { 249 | startActivity(new Intent(getActivity(), SettingsActivity.class)); 250 | return true; 251 | } 252 | return super.onOptionsItemSelected(item); 253 | } 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /app/src/main/java/com/lizhivscaomei/myapp/adapter/BottomNavigationFragmentPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.lizhivscaomei.myapp.adapter; 2 | 3 | import android.support.v4.app.Fragment; 4 | import android.support.v4.app.FragmentManager; 5 | import android.support.v4.app.FragmentPagerAdapter; 6 | 7 | import com.lizhivscaomei.myapp.fragments.FindFragment; 8 | import com.lizhivscaomei.myapp.fragments.HomeFragment; 9 | import com.lizhivscaomei.myapp.fragments.NotificationFragment; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | public class BottomNavigationFragmentPagerAdapter extends FragmentPagerAdapter{ 15 | private FragmentManager fm; 16 | private List fragmentList; 17 | public BottomNavigationFragmentPagerAdapter(FragmentManager fm) { 18 | super(fm); 19 | fragmentList=new ArrayList<>(); 20 | fragmentList.add(new HomeFragment()); 21 | fragmentList.add(new NotificationFragment()); 22 | fragmentList.add(new FindFragment()); 23 | } 24 | 25 | @Override 26 | public Fragment getItem(int position) { 27 | return fragmentList.get(position); 28 | } 29 | 30 | @Override 31 | public int getCount() { 32 | return fragmentList.size(); 33 | } 34 | 35 | public List getFragmentList() { 36 | return fragmentList; 37 | } 38 | 39 | public void setFragmentList(List fragmentList) { 40 | this.fragmentList = fragmentList; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/lizhivscaomei/myapp/fragments/FindFragment.java: -------------------------------------------------------------------------------- 1 | package com.lizhivscaomei.myapp.fragments; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.os.Bundle; 7 | import android.support.v4.app.Fragment; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.widget.AdapterView; 12 | import android.widget.ArrayAdapter; 13 | import android.widget.GridView; 14 | import android.widget.ListAdapter; 15 | import android.widget.SimpleAdapter; 16 | 17 | import com.lizhivscaomei.myapp.R; 18 | import com.lizhivscaomei.myapp.module.view.LedgerMainActivity; 19 | 20 | import java.util.ArrayList; 21 | import java.util.HashMap; 22 | import java.util.List; 23 | import java.util.Map; 24 | 25 | /** 26 | * A simple {@link Fragment} subclass. 27 | * Activities that contain this fragment must implement the 28 | * {@link FindFragment.OnFragmentInteractionListener} interface 29 | * to handle interaction events. 30 | * Use the {@link FindFragment#newInstance} factory method to 31 | * create an instance of this fragment. 32 | */ 33 | public class FindFragment extends Fragment { 34 | // TODO: Rename parameter arguments, choose names that match 35 | // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER 36 | private static final String ARG_PARAM1 = "param1"; 37 | private static final String ARG_PARAM2 = "param2"; 38 | 39 | // TODO: Rename and change types of parameters 40 | private String mParam1; 41 | private String mParam2; 42 | 43 | private OnFragmentInteractionListener mListener; 44 | 45 | public FindFragment() { 46 | // Required empty public constructor 47 | } 48 | 49 | /** 50 | * Use this factory method to create a new instance of 51 | * this fragment using the provided parameters. 52 | * 53 | * @param param1 Parameter 1. 54 | * @param param2 Parameter 2. 55 | * @return A new instance of fragment FindFragment. 56 | */ 57 | // TODO: Rename and change types and number of parameters 58 | public static FindFragment newInstance(String param1, String param2) { 59 | FindFragment fragment = new FindFragment(); 60 | Bundle args = new Bundle(); 61 | args.putString(ARG_PARAM1, param1); 62 | args.putString(ARG_PARAM2, param2); 63 | fragment.setArguments(args); 64 | return fragment; 65 | } 66 | 67 | @Override 68 | public void onCreate(Bundle savedInstanceState) { 69 | super.onCreate(savedInstanceState); 70 | if (getArguments() != null) { 71 | mParam1 = getArguments().getString(ARG_PARAM1); 72 | mParam2 = getArguments().getString(ARG_PARAM2); 73 | } 74 | } 75 | 76 | @Override 77 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 78 | Bundle savedInstanceState) { 79 | // Inflate the layout for this fragment 80 | View view = inflater.inflate(R.layout.fragment_find, container, false); 81 | GridView gridView=(GridView)view.findViewById(R.id.apps); 82 | List> dataList=new ArrayList<>(); 83 | Map leger=new HashMap<>(); 84 | leger.put("text","草莓记账"); 85 | dataList.add(leger); 86 | ListAdapter gridViewAdapter=new SimpleAdapter(getContext(),dataList,R.layout.item_vertical_icon_text,new String[]{"text"},new int[]{R.id.textView}); 87 | gridView.setAdapter(gridViewAdapter); 88 | gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 89 | @Override 90 | public void onItemClick(AdapterView parent, View view, int position, long id) { 91 | if(position==0){ 92 | startActivity(new Intent(getContext(), LedgerMainActivity.class)); 93 | } 94 | } 95 | }); 96 | return view; 97 | } 98 | 99 | // TODO: Rename method, update argument and hook method into UI event 100 | public void onButtonPressed(Uri uri) { 101 | if (mListener != null) { 102 | mListener.onFragmentInteraction(uri); 103 | } 104 | } 105 | 106 | @Override 107 | public void onAttach(Context context) { 108 | super.onAttach(context); 109 | if (context instanceof OnFragmentInteractionListener) { 110 | mListener = (OnFragmentInteractionListener) context; 111 | } else { 112 | throw new RuntimeException(context.toString() 113 | + " must implement OnFragmentInteractionListener"); 114 | } 115 | } 116 | 117 | @Override 118 | public void onDetach() { 119 | super.onDetach(); 120 | mListener = null; 121 | } 122 | 123 | /** 124 | * This interface must be implemented by activities that contain this 125 | * fragment to allow an interaction in this fragment to be communicated 126 | * to the activity and potentially other fragments contained in that 127 | * activity. 128 | *

129 | * See the Android Training lesson Communicating with Other Fragments for more information. 132 | */ 133 | public interface OnFragmentInteractionListener { 134 | // TODO: Update argument type and name 135 | void onFragmentInteraction(Uri uri); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /app/src/main/java/com/lizhivscaomei/myapp/fragments/HomeFragment.java: -------------------------------------------------------------------------------- 1 | package com.lizhivscaomei.myapp.fragments; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.database.Cursor; 6 | import android.net.Uri; 7 | import android.os.AsyncTask; 8 | import android.os.Bundle; 9 | import android.support.v4.app.Fragment; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | import android.webkit.WebView; 14 | import android.webkit.WebViewClient; 15 | import android.widget.LinearLayout; 16 | import android.widget.TextView; 17 | 18 | import com.lizhivscaomei.myapp.MyApplication; 19 | import com.lizhivscaomei.myapp.R; 20 | import com.lizhivscaomei.myapp.module.entity.LedgerEntity; 21 | import com.lizhivscaomei.myapp.module.view.LedgerMainActivity; 22 | 23 | import org.xutils.ex.DbException; 24 | 25 | import java.text.SimpleDateFormat; 26 | import java.util.ArrayList; 27 | import java.util.Date; 28 | import java.util.HashMap; 29 | import java.util.Map; 30 | 31 | /** 32 | * A simple {@link Fragment} subclass. 33 | * Activities that contain this fragment must implement the 34 | * {@link HomeFragment.OnFragmentInteractionListener} interface 35 | * to handle interaction events. 36 | * Use the {@link HomeFragment#newInstance} factory method to 37 | * create an instance of this fragment. 38 | */ 39 | public class HomeFragment extends Fragment { 40 | // TODO: Rename parameter arguments, choose names that match 41 | // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER 42 | private static final String ARG_PARAM1 = "param1"; 43 | private static final String ARG_PARAM2 = "param2"; 44 | 45 | // TODO: Rename and change types of parameters 46 | private String mParam1; 47 | private String mParam2; 48 | 49 | private OnFragmentInteractionListener mListener; 50 | private TextView totalAmountYear, totalWeightYear; 51 | 52 | public HomeFragment() { 53 | // Required empty public constructor 54 | } 55 | 56 | /** 57 | * Use this factory method to create a new instance of 58 | * this fragment using the provided parameters. 59 | * 60 | * @param param1 Parameter 1. 61 | * @param param2 Parameter 2. 62 | * @return A new instance of fragment HomeFragment. 63 | */ 64 | // TODO: Rename and change types and number of parameters 65 | public static HomeFragment newInstance(String param1, String param2) { 66 | HomeFragment fragment = new HomeFragment(); 67 | Bundle args = new Bundle(); 68 | args.putString(ARG_PARAM1, param1); 69 | args.putString(ARG_PARAM2, param2); 70 | fragment.setArguments(args); 71 | return fragment; 72 | } 73 | 74 | @Override 75 | public void onCreate(Bundle savedInstanceState) { 76 | super.onCreate(savedInstanceState); 77 | if (getArguments() != null) { 78 | mParam1 = getArguments().getString(ARG_PARAM1); 79 | mParam2 = getArguments().getString(ARG_PARAM2); 80 | } 81 | 82 | } 83 | 84 | @Override 85 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 86 | Bundle savedInstanceState) { 87 | // Inflate the layout for this fragment 88 | // WebView webView=(WebView)inflater.inflate(R.id.homeWebView,container); 89 | View view = inflater.inflate(R.layout.fragment_home, container, false); 90 | LinearLayout appLayout = (LinearLayout) view.findViewById(R.id.app_ledger); 91 | appLayout.setOnClickListener(new View.OnClickListener() { 92 | @Override 93 | public void onClick(View v) { 94 | startActivity(new Intent(getContext(), LedgerMainActivity.class)); 95 | } 96 | }); 97 | totalAmountYear = (TextView) view.findViewById(R.id.total_amount_year); 98 | totalWeightYear = (TextView) view.findViewById(R.id.total_weight_year); 99 | return view; 100 | } 101 | 102 | @Override 103 | public void onResume() { 104 | super.onResume(); 105 | getTotalYear(); 106 | } 107 | 108 | public void getTotalYear() { 109 | new AsyncTask>() { 110 | 111 | @Override 112 | protected Map doInBackground(Void... voids) { 113 | try { 114 | SimpleDateFormat sf=new SimpleDateFormat("yyyy"); 115 | Cursor cursor = MyApplication.getXDbManager().execQuery("select sum(total_amount) total_amount_year,sum(total_weight) total_weight_year from ledger where date like '%" + sf.format(new Date()) + "%'"); 116 | if(cursor!=null&&cursor.moveToFirst()){ 117 | Map result=new HashMap<>(); 118 | result.put("total_amount_year",cursor.getFloat(cursor.getColumnIndex("total_amount_year"))); 119 | result.put("total_weight_year",cursor.getFloat(cursor.getColumnIndex("total_weight_year"))); 120 | return result; 121 | } 122 | } catch (DbException e) { 123 | } 124 | return null; 125 | } 126 | 127 | @Override 128 | protected void onPostExecute(Map stringFloatMap) { 129 | super.onPostExecute(stringFloatMap); 130 | if(stringFloatMap!=null){ 131 | if(stringFloatMap.get("total_amount_year")!=null&&!stringFloatMap.get("total_amount_year").isNaN()&&stringFloatMap.get("total_amount_year").floatValue()>0){ 132 | totalAmountYear.setText(String.format("%.2f",stringFloatMap.get("total_amount_year").floatValue())); 133 | } 134 | if(stringFloatMap.get("total_weight_year")!=null&&!stringFloatMap.get("total_weight_year").isNaN()&&stringFloatMap.get("total_weight_year").floatValue()>0){ 135 | totalWeightYear.setText(String.format("%.2f",stringFloatMap.get("total_weight_year").floatValue())); 136 | } 137 | }else { 138 | totalAmountYear.setText("0.00"); 139 | totalWeightYear.setText("0.00"); 140 | } 141 | } 142 | }.execute(); 143 | } 144 | 145 | // TODO: Rename method, update argument and hook method into UI event 146 | public void onButtonPressed(Uri uri) { 147 | if (mListener != null) { 148 | mListener.onFragmentInteraction(uri); 149 | } 150 | } 151 | 152 | @Override 153 | public void onAttach(Context context) { 154 | super.onAttach(context); 155 | if (context instanceof OnFragmentInteractionListener) { 156 | mListener = (OnFragmentInteractionListener) context; 157 | } else { 158 | throw new RuntimeException(context.toString() 159 | + " must implement OnFragmentInteractionListener"); 160 | } 161 | } 162 | 163 | @Override 164 | public void onDetach() { 165 | super.onDetach(); 166 | mListener = null; 167 | } 168 | 169 | /** 170 | * This interface must be implemented by activities that contain this 171 | * fragment to allow an interaction in this fragment to be communicated 172 | * to the activity and potentially other fragments contained in that 173 | * activity. 174 | *

175 | * See the Android Training lesson Communicating with Other Fragments for more information. 178 | */ 179 | public interface OnFragmentInteractionListener { 180 | // TODO: Update argument type and name 181 | void onFragmentInteraction(Uri uri); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /app/src/main/java/com/lizhivscaomei/myapp/fragments/NotificationFragment.java: -------------------------------------------------------------------------------- 1 | package com.lizhivscaomei.myapp.fragments; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | import android.os.Bundle; 6 | import android.support.v4.app.Fragment; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | 11 | import com.lizhivscaomei.myapp.R; 12 | 13 | /** 14 | * A simple {@link Fragment} subclass. 15 | * Activities that contain this fragment must implement the 16 | * {@link NotificationFragment.OnFragmentInteractionListener} interface 17 | * to handle interaction events. 18 | * Use the {@link NotificationFragment#newInstance} factory method to 19 | * create an instance of this fragment. 20 | */ 21 | public class NotificationFragment extends Fragment { 22 | // TODO: Rename parameter arguments, choose names that match 23 | // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER 24 | private static final String ARG_PARAM1 = "param1"; 25 | private static final String ARG_PARAM2 = "param2"; 26 | 27 | // TODO: Rename and change types of parameters 28 | private String mParam1; 29 | private String mParam2; 30 | 31 | private OnFragmentInteractionListener mListener; 32 | 33 | public NotificationFragment() { 34 | // Required empty public constructor 35 | } 36 | 37 | /** 38 | * Use this factory method to create a new instance of 39 | * this fragment using the provided parameters. 40 | * 41 | * @param param1 Parameter 1. 42 | * @param param2 Parameter 2. 43 | * @return A new instance of fragment NotificationFragment. 44 | */ 45 | // TODO: Rename and change types and number of parameters 46 | public static NotificationFragment newInstance(String param1, String param2) { 47 | NotificationFragment fragment = new NotificationFragment(); 48 | Bundle args = new Bundle(); 49 | args.putString(ARG_PARAM1, param1); 50 | args.putString(ARG_PARAM2, param2); 51 | fragment.setArguments(args); 52 | return fragment; 53 | } 54 | 55 | @Override 56 | public void onCreate(Bundle savedInstanceState) { 57 | super.onCreate(savedInstanceState); 58 | if (getArguments() != null) { 59 | mParam1 = getArguments().getString(ARG_PARAM1); 60 | mParam2 = getArguments().getString(ARG_PARAM2); 61 | } 62 | } 63 | 64 | @Override 65 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 66 | Bundle savedInstanceState) { 67 | // Inflate the layout for this fragment 68 | return inflater.inflate(R.layout.fragment_notification, container, false); 69 | } 70 | 71 | // TODO: Rename method, update argument and hook method into UI event 72 | public void onButtonPressed(Uri uri) { 73 | if (mListener != null) { 74 | mListener.onFragmentInteraction(uri); 75 | } 76 | } 77 | 78 | @Override 79 | public void onAttach(Context context) { 80 | super.onAttach(context); 81 | if (context instanceof OnFragmentInteractionListener) { 82 | mListener = (OnFragmentInteractionListener) context; 83 | } else { 84 | throw new RuntimeException(context.toString() 85 | + " must implement OnFragmentInteractionListener"); 86 | } 87 | } 88 | 89 | @Override 90 | public void onDetach() { 91 | super.onDetach(); 92 | mListener = null; 93 | } 94 | 95 | /** 96 | * This interface must be implemented by activities that contain this 97 | * fragment to allow an interaction in this fragment to be communicated 98 | * to the activity and potentially other fragments contained in that 99 | * activity. 100 | *

101 | * See the Android Training lesson Communicating with Other Fragments for more information. 104 | */ 105 | public interface OnFragmentInteractionListener { 106 | // TODO: Update argument type and name 107 | void onFragmentInteraction(Uri uri); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /app/src/main/java/com/lizhivscaomei/myapp/module/entity/LedgerEntity.java: -------------------------------------------------------------------------------- 1 | package com.lizhivscaomei.myapp.module.entity; 2 | 3 | import org.xutils.db.annotation.Column; 4 | import org.xutils.db.annotation.Table; 5 | 6 | @Table(name = "ledger") 7 | public class LedgerEntity { 8 | @Column(name = "id",isId = true,autoGen = true) 9 | private int id; 10 | @Column(name = "date", property = "NOT NULL") 11 | private String date;//日期 12 | @Column(name = "total_amount", property = "NOT NULL") 13 | private float totalAmount;//总金额 14 | @Column(name = "total_weight", property = "NOT NULL") 15 | private float totalWeight;//总重量 16 | private float price;//价格 17 | 18 | public String getDate() { 19 | return date; 20 | } 21 | 22 | public void setDate(String date) { 23 | this.date = date; 24 | } 25 | 26 | public float getTotalAmount() { 27 | return totalAmount; 28 | } 29 | 30 | public void setTotalAmount(float totalAmount) { 31 | this.totalAmount = totalAmount; 32 | } 33 | 34 | public float getTotalWeight() { 35 | return totalWeight; 36 | } 37 | 38 | public void setTotalWeight(float totalWeight) { 39 | this.totalWeight = totalWeight; 40 | } 41 | 42 | public float getPrice() { 43 | return price; 44 | } 45 | 46 | public void setPrice(float price) { 47 | this.price = price; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/lizhivscaomei/myapp/module/view/AddLegerActivity.java: -------------------------------------------------------------------------------- 1 | package com.lizhivscaomei.myapp.module.view; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.annotation.TargetApi; 6 | import android.app.LoaderManager.LoaderCallbacks; 7 | import android.content.CursorLoader; 8 | import android.content.Intent; 9 | import android.content.Loader; 10 | import android.content.pm.PackageManager; 11 | import android.database.Cursor; 12 | import android.net.Uri; 13 | import android.os.AsyncTask; 14 | import android.os.Build; 15 | import android.os.Bundle; 16 | import android.provider.ContactsContract; 17 | import android.support.annotation.NonNull; 18 | import android.support.design.widget.Snackbar; 19 | import android.support.v7.app.AppCompatActivity; 20 | import android.text.TextUtils; 21 | import android.view.KeyEvent; 22 | import android.view.View; 23 | import android.view.View.OnClickListener; 24 | import android.view.inputmethod.EditorInfo; 25 | import android.widget.Button; 26 | import android.widget.EditText; 27 | import android.widget.TextView; 28 | import android.widget.Toast; 29 | 30 | import com.lizhivscaomei.myapp.MyApplication; 31 | import com.lizhivscaomei.myapp.R; 32 | import com.lizhivscaomei.myapp.module.entity.LedgerEntity; 33 | 34 | import org.apache.commons.lang3.StringUtils; 35 | import org.xutils.ex.DbException; 36 | import org.xutils.x; 37 | 38 | import java.text.SimpleDateFormat; 39 | import java.util.ArrayList; 40 | import java.util.Date; 41 | import java.util.List; 42 | 43 | import static android.Manifest.permission.READ_CONTACTS; 44 | 45 | /** 46 | * A login screen that offers login via email/password. 47 | */ 48 | public class AddLegerActivity extends AppCompatActivity implements LoaderCallbacks { 49 | 50 | /** 51 | * Id to identity READ_CONTACTS permission request. 52 | */ 53 | private static final int REQUEST_READ_CONTACTS = 0; 54 | 55 | /** 56 | * A dummy authentication store containing known user names and passwords. 57 | * TODO: remove after connecting to a real authentication system. 58 | */ 59 | private static final String[] DUMMY_CREDENTIALS = new String[]{ 60 | "foo@example.com:hello", "bar@example.com:world" 61 | }; 62 | 63 | // UI references. 64 | private EditText totalAmount,date,totalWeight; 65 | private View mProgressView; 66 | private View mLoginFormView; 67 | 68 | @Override 69 | protected void onCreate(Bundle savedInstanceState) { 70 | super.onCreate(savedInstanceState); 71 | setContentView(R.layout.activity_add_leger); 72 | // Set up the login form. 73 | date = (EditText) findViewById(R.id.date); 74 | //默认当天日期 75 | SimpleDateFormat sf=new SimpleDateFormat("yyyy-MM-dd"); 76 | date.setText(sf.format(new Date())); 77 | totalWeight = (EditText) findViewById(R.id.total_weight); 78 | totalWeight.requestFocus(); 79 | totalAmount = (EditText) findViewById(R.id.total_amount); 80 | 81 | Button saveBtn = (Button) findViewById(R.id.save); 82 | saveBtn.setOnClickListener(new OnClickListener() { 83 | @Override 84 | public void onClick(View view) { 85 | attemptSaveDb(); 86 | } 87 | }); 88 | 89 | mLoginFormView = findViewById(R.id.login_form); 90 | mProgressView = findViewById(R.id.login_progress); 91 | } 92 | 93 | 94 | private boolean mayRequestContacts() { 95 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { 96 | return true; 97 | } 98 | if (checkSelfPermission(READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) { 99 | return true; 100 | } 101 | if (shouldShowRequestPermissionRationale(READ_CONTACTS)) { 102 | Snackbar.make(date, R.string.permission_rationale, Snackbar.LENGTH_INDEFINITE) 103 | .setAction(android.R.string.ok, new View.OnClickListener() { 104 | @Override 105 | @TargetApi(Build.VERSION_CODES.M) 106 | public void onClick(View v) { 107 | requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS); 108 | } 109 | }); 110 | } else { 111 | requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS); 112 | } 113 | return false; 114 | } 115 | 116 | /** 117 | * Callback received when a permissions request has been completed. 118 | */ 119 | @Override 120 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, 121 | @NonNull int[] grantResults) { 122 | 123 | } 124 | 125 | 126 | /** 127 | * Attempts to sign in or register the account specified by the login form. 128 | * If there are form errors (invalid email, missing fields, etc.), the 129 | * errors are presented and no actual login attempt is made. 130 | */ 131 | private void attemptSaveDb() { 132 | 133 | // Reset errors. 134 | date.setError(null); 135 | totalAmount.setError(null); 136 | 137 | // Store values at the time of the login attempt. 138 | final LedgerEntity ledgerEntity=new LedgerEntity(); 139 | if(StringUtils.isNotEmpty(totalWeight.getText().toString())){ 140 | ledgerEntity.setTotalWeight(Float.parseFloat(totalWeight.getText().toString())); 141 | } 142 | if(StringUtils.isNotEmpty(totalAmount.getText().toString())){ 143 | 144 | ledgerEntity.setTotalAmount(Float.parseFloat(totalAmount.getText().toString())); 145 | } 146 | ledgerEntity.setDate(date.getText().toString()); 147 | 148 | boolean cancel = true; 149 | View focusView = null; 150 | 151 | //校验空 152 | if (ledgerEntity!=null&& StringUtils.isNotEmpty(ledgerEntity.getDate())) { 153 | totalAmount.setError(getString(R.string.error_field_required)); 154 | if(ledgerEntity.getTotalAmount()>0){ 155 | if(ledgerEntity.getTotalWeight()>0){ 156 | cancel=false; 157 | }else { 158 | totalWeight.setError(getString(R.string.error_field_required)); 159 | focusView = totalWeight; 160 | } 161 | }else { 162 | totalAmount.setError(getString(R.string.error_field_required)); 163 | focusView = totalAmount; 164 | } 165 | }else{ 166 | date.setError(getString(R.string.error_field_required)); 167 | focusView = date; 168 | } 169 | 170 | if (cancel) { 171 | // There was an error; don't attempt login and focus the first 172 | // form field with an error. 173 | focusView.requestFocus(); 174 | } else { 175 | // Show a progress spinner, and kick off a background task to 176 | // perform the user login attempt. 177 | showProgress(true); 178 | //异步保存,写入数据库 179 | new AsyncTask() { 180 | 181 | @Override 182 | protected Boolean doInBackground(LedgerEntity... ledgerEntities) { 183 | try { 184 | MyApplication.getXDbManager().save(ledgerEntity); 185 | } catch (DbException e) { 186 | return false; 187 | } 188 | return true; 189 | } 190 | 191 | @Override 192 | protected void onPostExecute(Boolean aBoolean) { 193 | super.onPostExecute(aBoolean); 194 | if(aBoolean){ 195 | 196 | Toast.makeText(AddLegerActivity.this, "保存成功", Toast.LENGTH_SHORT).show(); 197 | // startActivity(new Intent(AddLegerActivity.this,LedgerMainActivity.class)); 198 | AddLegerActivity.this.finish(); 199 | }else { 200 | 201 | Toast.makeText(AddLegerActivity.this, "保存失败", Toast.LENGTH_SHORT).show(); 202 | } 203 | showProgress(false); 204 | } 205 | }.execute(ledgerEntity); 206 | } 207 | } 208 | 209 | 210 | /** 211 | * Shows the progress UI and hides the login form. 212 | */ 213 | @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) 214 | private void showProgress(final boolean show) { 215 | // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow 216 | // for very easy animations. If available, use these APIs to fade-in 217 | // the progress spinner. 218 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) { 219 | int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime); 220 | 221 | mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); 222 | mLoginFormView.animate().setDuration(shortAnimTime).alpha( 223 | show ? 0 : 1).setListener(new AnimatorListenerAdapter() { 224 | @Override 225 | public void onAnimationEnd(Animator animation) { 226 | mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); 227 | } 228 | }); 229 | 230 | mProgressView.setVisibility(show ? View.VISIBLE : View.GONE); 231 | mProgressView.animate().setDuration(shortAnimTime).alpha( 232 | show ? 1 : 0).setListener(new AnimatorListenerAdapter() { 233 | @Override 234 | public void onAnimationEnd(Animator animation) { 235 | mProgressView.setVisibility(show ? View.VISIBLE : View.GONE); 236 | } 237 | }); 238 | } else { 239 | // The ViewPropertyAnimator APIs are not available, so simply show 240 | // and hide the relevant UI components. 241 | mProgressView.setVisibility(show ? View.VISIBLE : View.GONE); 242 | mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); 243 | } 244 | } 245 | 246 | @Override 247 | public Loader onCreateLoader(int i, Bundle bundle) { 248 | return new CursorLoader(this, 249 | // Retrieve data rows for the device user's 'profile' contact. 250 | Uri.withAppendedPath(ContactsContract.Profile.CONTENT_URI, 251 | ContactsContract.Contacts.Data.CONTENT_DIRECTORY), ProfileQuery.PROJECTION, 252 | 253 | // Select only email addresses. 254 | ContactsContract.Contacts.Data.MIMETYPE + 255 | " = ?", new String[]{ContactsContract.CommonDataKinds.Email 256 | .CONTENT_ITEM_TYPE}, 257 | 258 | // Show primary email addresses first. Note that there won't be 259 | // a primary email address if the user hasn't specified one. 260 | ContactsContract.Contacts.Data.IS_PRIMARY + " DESC"); 261 | } 262 | 263 | @Override 264 | public void onLoadFinished(Loader cursorLoader, Cursor cursor) { 265 | List emails = new ArrayList<>(); 266 | cursor.moveToFirst(); 267 | while (!cursor.isAfterLast()) { 268 | emails.add(cursor.getString(ProfileQuery.ADDRESS)); 269 | cursor.moveToNext(); 270 | } 271 | 272 | } 273 | 274 | @Override 275 | public void onLoaderReset(Loader cursorLoader) { 276 | 277 | } 278 | 279 | 280 | 281 | private interface ProfileQuery { 282 | String[] PROJECTION = { 283 | ContactsContract.CommonDataKinds.Email.ADDRESS, 284 | ContactsContract.CommonDataKinds.Email.IS_PRIMARY, 285 | }; 286 | 287 | int ADDRESS = 0; 288 | int IS_PRIMARY = 1; 289 | } 290 | 291 | } 292 | 293 | -------------------------------------------------------------------------------- /app/src/main/java/com/lizhivscaomei/myapp/module/view/LedgerMainActivity.java: -------------------------------------------------------------------------------- 1 | package com.lizhivscaomei.myapp.module.view; 2 | 3 | import android.content.DialogInterface; 4 | import android.content.Intent; 5 | import android.os.AsyncTask; 6 | import android.os.Bundle; 7 | import android.os.Handler; 8 | import android.support.design.widget.FloatingActionButton; 9 | import android.support.v7.app.AlertDialog; 10 | import android.support.v7.app.AppCompatActivity; 11 | import android.support.v7.widget.Toolbar; 12 | import android.view.KeyEvent; 13 | import android.view.View; 14 | import android.widget.AdapterView; 15 | import android.widget.ListView; 16 | import android.widget.SimpleAdapter; 17 | import android.widget.Toast; 18 | 19 | import com.lizhivscaomei.myapp.MainActivity; 20 | import com.lizhivscaomei.myapp.MyApplication; 21 | import com.lizhivscaomei.myapp.R; 22 | import com.lizhivscaomei.myapp.module.entity.LedgerEntity; 23 | 24 | import org.xutils.ex.DbException; 25 | 26 | import java.util.ArrayList; 27 | import java.util.HashMap; 28 | import java.util.List; 29 | import java.util.Map; 30 | 31 | public class LedgerMainActivity extends AppCompatActivity { 32 | ListView ledgerList; 33 | @Override 34 | protected void onCreate(Bundle savedInstanceState) { 35 | super.onCreate(savedInstanceState); 36 | setContentView(R.layout.activity_ledger_main); 37 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 38 | setSupportActionBar(toolbar); 39 | //记账按钮 40 | FloatingActionButton addBtn = (FloatingActionButton) findViewById(R.id.fab); 41 | addBtn.setOnClickListener(new View.OnClickListener() { 42 | @Override 43 | public void onClick(View view) { 44 | startActivity(new Intent(LedgerMainActivity.this, AddLegerActivity.class)); 45 | } 46 | }); 47 | //ledgerList 48 | ledgerList = (ListView) findViewById(R.id.ledgerList); 49 | ledgerList.setOnItemClickListener(new AdapterView.OnItemClickListener() { 50 | @Override 51 | public void onItemClick(AdapterView parent, View view, int position, final long id) { 52 | final Map selecedItem = (Map) parent.getItemAtPosition(position); 53 | new AlertDialog.Builder(LedgerMainActivity.this).setTitle("提示").setMessage(selecedItem.get("text1").toString() + ",确定要删除吗?").setPositiveButton("确定", new DialogInterface.OnClickListener() { 54 | @Override 55 | public void onClick(DialogInterface dialog, int which) { 56 | new AsyncTask() { 57 | @Override 58 | protected Boolean doInBackground(LedgerEntity... ledgerEntities) { 59 | try { 60 | MyApplication.getXDbManager().delete(ledgerEntities[0]); 61 | } catch (DbException e) { 62 | return false; 63 | } 64 | return true; 65 | } 66 | 67 | @Override 68 | protected void onPostExecute(Boolean aBoolean) { 69 | super.onPostExecute(aBoolean); 70 | if(aBoolean){ 71 | Toast.makeText(LedgerMainActivity.this, "删除成功", Toast.LENGTH_SHORT).show(); 72 | queryList(); 73 | }else { 74 | 75 | Toast.makeText(LedgerMainActivity.this, "删除失败", Toast.LENGTH_SHORT).show(); 76 | } 77 | } 78 | }.execute((LedgerEntity) selecedItem.get("entity")); 79 | } 80 | }).setNegativeButton("取消", new DialogInterface.OnClickListener() { 81 | @Override 82 | public void onClick(DialogInterface dialog, int which) { 83 | 84 | } 85 | }).show(); 86 | } 87 | }); 88 | } 89 | 90 | @Override 91 | protected void onResume() { 92 | super.onResume(); 93 | queryList(); 94 | } 95 | 96 | public void queryList(){ 97 | 98 | new AsyncTask>() { 99 | @Override 100 | protected List doInBackground(Void... voids) { 101 | try { 102 | return MyApplication.getXDbManager().selector(LedgerEntity.class).orderBy("date",true).findAll(); 103 | } catch (DbException e) { 104 | return new ArrayList<>(); 105 | } 106 | } 107 | 108 | @Override 109 | protected void onPostExecute(List ledgerEntityList) { 110 | super.onPostExecute(ledgerEntityList); 111 | List> dataList = new ArrayList<>(); 112 | if(ledgerEntityList!=null){ 113 | for (LedgerEntity entity : ledgerEntityList) { 114 | Map map = new HashMap<>(); 115 | map.put("entity", entity); 116 | map.put("text1", "总金额:" + String.format("%.2f", entity.getTotalAmount())); 117 | map.put("text2", entity.getDate()); 118 | map.put("text3", "总重量:" + String.format("%.2f", entity.getTotalWeight()) + "斤"); 119 | map.put("text4", "单价:" + String.format("%.2f", (entity.getTotalAmount() / entity.getTotalWeight())) + "元/斤"); 120 | dataList.add(map); 121 | } 122 | } 123 | ledgerList.setAdapter(new SimpleAdapter(LedgerMainActivity.this, dataList, R.layout.item_horizontal_text3_line2, new String[]{"text1", "text2", "text3", "text4"}, new int[]{R.id.textView1, R.id.textView2, R.id.textView3,R.id.textView4})); 124 | 125 | } 126 | }.execute(); 127 | } 128 | @Override 129 | public boolean onKeyDown(int keyCode, KeyEvent event) { 130 | if (keyCode == KeyEvent.KEYCODE_BACK) { 131 | this.finish(); 132 | return true; 133 | } 134 | return super.onKeyDown(keyCode, event); 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/ic_menu_camera.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/ic_menu_gallery.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/ic_menu_manage.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/ic_menu_send.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/ic_menu_share.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/ic_menu_slideshow.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_dashboard_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_home_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_info_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_notifications_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_sync_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/side_nav_bar.xml: -------------------------------------------------------------------------------- 1 | 3 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_add_leger.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | 22 | 23 | 27 | 28 | 33 | 34 | 37 | 38 | 48 | 49 | 50 | 51 | 52 | 55 | 56 | 66 | 67 | 68 | 69 | 72 | 73 | 84 | 85 | 86 | 87 |