├── .classpath
├── .gitignore
├── .project
├── .settings
└── org.eclipse.jdt.core.prefs
├── AndroidManifest.xml
├── LICENSE-2.0.txt
├── README.md
├── ic_launcher-web.png
├── libs
├── Parse-1.1.11.jar
└── android-support-v4.jar
├── proguard-project.txt
├── project.properties
├── res
├── drawable-hdpi
│ └── ic_launcher.png
├── drawable-ldpi
│ └── ic_launcher.png
├── drawable-mdpi
│ └── ic_launcher.png
├── drawable-xhdpi
│ └── ic_launcher.png
├── layout
│ ├── activity_login.xml
│ ├── activity_main.xml
│ ├── dialog_forgot_password.xml
│ ├── fragment_create_account.xml
│ ├── fragment_login.xml
│ ├── layout_default_status.xml
│ └── view_status.xml
├── menu
│ └── activity_login.xml
├── values-large
│ └── styles.xml
├── values-v11
│ └── styles.xml
├── values-v14
│ └── styles.xml
└── values
│ ├── colors.xml
│ ├── strings.xml
│ ├── strings_activity_login.xml
│ └── styles.xml
└── src
└── com
├── keyconsultant
└── parse
│ └── logintutorial
│ ├── CreateAccountFragment.java
│ ├── LoginActivity.java
│ ├── LoginFragment.java
│ ├── MainActivity.java
│ ├── activity
│ ├── BaseActivity.java
│ └── StatusView.java
│ ├── error
│ └── UnknownErrorDialogFactory.java
│ ├── event
│ └── ErrorEvent.java
│ ├── forgotpassword
│ └── ForgotPasswordDialogFragment.java
│ ├── fragment
│ └── BaseFragment.java
│ └── model
│ ├── manager
│ └── BaseManager.java
│ └── user
│ ├── User.java
│ ├── UserManager.java
│ └── authenticate
│ ├── AuthenticateUserErrorEvent.java
│ ├── AuthenticateUserStartEvent.java
│ ├── AuthenticateUserSuccessEvent.java
│ ├── UserForgotPasswordErrorEvent.java
│ ├── UserForgotPasswordStartEvent.java
│ └── UserForgotPasswordSuccessEvent.java
└── squareup
└── otto
├── AnnotatedHandlerFinder.java
├── Bus.java
├── BusProvider.java
├── DeadEvent.java
├── EventHandler.java
├── EventProducer.java
├── HandlerFinder.java
├── Produce.java
├── Subscribe.java
└── ThreadEnforcer.java
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # built application files
2 | *.apk
3 | *.ap_
4 |
5 | # files for the dex VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # generated files
12 | bin/
13 | gen/
14 |
15 | # Local configuration file (sdk path, etc)
16 | local.properties
17 |
18 | # Eclipse project files
19 | .classpath
20 | .project
21 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | LoginAndSignupTutorial
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
3 | org.eclipse.jdt.core.compiler.compliance=1.6
4 | org.eclipse.jdt.core.compiler.source=1.6
5 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
13 |
14 |
19 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/LICENSE-2.0.txt:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | <<<<<<< HEAD
191 | Copyright [yyyy] [name of copyright owner]
192 | =======
193 | Copyright 2013 Trey Robinson
194 | >>>>>>> Added apache license.
195 |
196 | Licensed under the Apache License, Version 2.0 (the "License");
197 | you may not use this file except in compliance with the License.
198 | You may obtain a copy of the License at
199 |
200 | http://www.apache.org/licenses/LICENSE-2.0
201 |
202 | Unless required by applicable law or agreed to in writing, software
203 | distributed under the License is distributed on an "AS IS" BASIS,
204 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
205 | See the License for the specific language governing permissions and
206 | limitations under the License.
207 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | LoginAndSignupTutorial
2 | ======================
3 |
4 | Login and Signup Android tutorial using Parse API and Otto.
5 |
6 | This project is part of a tutorial at http://www.thekeyconsultant.com/2013/02/parse-for-android-simple-login-starter.html
7 |
8 |
--------------------------------------------------------------------------------
/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rdrobinson3/LoginAndSignupTutorial/b1ca51ca44037b48a74e11f50cc39a8091bce113/ic_launcher-web.png
--------------------------------------------------------------------------------
/libs/Parse-1.1.11.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rdrobinson3/LoginAndSignupTutorial/b1ca51ca44037b48a74e11f50cc39a8091bce113/libs/Parse-1.1.11.jar
--------------------------------------------------------------------------------
/libs/android-support-v4.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rdrobinson3/LoginAndSignupTutorial/b1ca51ca44037b48a74e11f50cc39a8091bce113/libs/android-support-v4.jar
--------------------------------------------------------------------------------
/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-17
15 |
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rdrobinson3/LoginAndSignupTutorial/b1ca51ca44037b48a74e11f50cc39a8091bce113/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rdrobinson3/LoginAndSignupTutorial/b1ca51ca44037b48a74e11f50cc39a8091bce113/res/drawable-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rdrobinson3/LoginAndSignupTutorial/b1ca51ca44037b48a74e11f50cc39a8091bce113/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rdrobinson3/LoginAndSignupTutorial/b1ca51ca44037b48a74e11f50cc39a8091bce113/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/layout/activity_login.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
15 |
16 |
--------------------------------------------------------------------------------
/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
14 |
--------------------------------------------------------------------------------
/res/layout/dialog_forgot_password.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
15 |
16 |
23 |
24 |
--------------------------------------------------------------------------------
/res/layout/fragment_create_account.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
14 |
15 |
24 |
25 |
35 |
36 |
45 |
46 |
54 |
55 |
--------------------------------------------------------------------------------
/res/layout/fragment_login.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
25 |
26 |
33 |
34 |
35 |
43 |
44 |
51 |
--------------------------------------------------------------------------------
/res/layout/layout_default_status.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
--------------------------------------------------------------------------------
/res/layout/view_status.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
16 |
17 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/res/menu/activity_login.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/res/values-large/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
--------------------------------------------------------------------------------
/res/values-v11/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/res/values-v14/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #000000
5 |
6 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | LoginAndSignupTutorial
5 | Loading…
6 |
7 |
8 | Sign In
9 | Submit
10 | Or
11 | Create Account
12 | Email
13 | Username
14 | Password
15 | Confirm Password
16 | LauncherActivity
17 | Changing Fragments
18 | CreateAccountActivity
19 | Hello world!
20 | Settings
21 | MainActivity
22 |
23 |
--------------------------------------------------------------------------------
/res/values/strings_activity_login.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Email
5 | Password
6 | Sign in or register
7 | Sign in
8 | Create Account
9 | Recover lost password
10 | Signing in…
11 | Sending email…
12 | This email address is invalid
13 | This email address is already in use
14 | This username is already in use
15 | This password is incorrect
16 | This password is too short
17 | Passwords do not match
18 | This field is required
19 |
20 |
--------------------------------------------------------------------------------
/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
15 |
16 |
17 |
20 |
21 |
26 |
27 |
--------------------------------------------------------------------------------
/src/com/keyconsultant/parse/logintutorial/CreateAccountFragment.java:
--------------------------------------------------------------------------------
1 | package com.keyconsultant.parse.logintutorial;
2 |
3 | import java.util.Locale;
4 |
5 | import android.os.Bundle;
6 | import android.text.TextUtils;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.View.OnClickListener;
10 | import android.view.ViewGroup;
11 | import android.widget.Button;
12 | import android.widget.EditText;
13 |
14 | import com.keyconsultant.parse.logintutorial.error.UnknownErrorDialogFactory;
15 | import com.keyconsultant.parse.logintutorial.fragment.BaseFragment;
16 | import com.keyconsultant.parse.logintutorial.model.user.UserManager;
17 | import com.keyconsultant.parse.logintutorial.model.user.authenticate.AuthenticateUserErrorEvent;
18 | import com.parse.ParseException;
19 | import com.squareup.otto.Subscribe;
20 |
21 | /**
22 | * Create an Account. Username is the primary method of login. Email is used for forgotten password recovery.
23 | *
24 | * @author Trey Robinson
25 | *
26 | */
27 | public class CreateAccountFragment extends BaseFragment implements OnClickListener {
28 |
29 | protected static final String EXTRA_EMAIL = "com.keyconsultant.parse.logintutorial.fragment.extra.EMAIL";
30 | protected static final String EXTRA_USERNAME = "com.keyconsultant.parse.logintutorial.fragment.extra.USERNAME";
31 | protected static final String EXTRA_PASSWORD = "com.keyconsultant.parse.logintutorial.fragment.extra.PASSWORD";
32 | protected static final String EXTRA_CONFIRM = "com.keyconsultant.parse.logintutorial.fragment.extra.CONFIRMPASSWORD";
33 |
34 | private EditText mUserNameEditText;
35 | private EditText mEmailEditText;
36 | private EditText mPasswordEditText;
37 | private EditText mConfirmPasswordEditText;
38 | private Button mCreateAccountButton;
39 |
40 | private String mEmail;
41 | private String mUsername;
42 | private String mPassword;
43 | private String mConfirmPassword;
44 |
45 | /**
46 | * Factory method for creating fragment instances.
47 | * @return
48 | */
49 | public static CreateAccountFragment newInstance(){
50 | return new CreateAccountFragment();
51 | }
52 |
53 | @Override
54 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
55 | Bundle savedInstanceState) {
56 |
57 | View view = inflater.inflate(R.layout.fragment_create_account, container, false);
58 |
59 | mUserNameEditText = (EditText)view.findViewById(R.id.etUsername);
60 | mEmailEditText = (EditText)view.findViewById(R.id.etEmail);
61 | mPasswordEditText = (EditText)view.findViewById(R.id.etPassword);
62 | mConfirmPasswordEditText = (EditText)view.findViewById(R.id.etPasswordConfirm);
63 |
64 | mCreateAccountButton = (Button)view.findViewById(R.id.btnCreateAccount);
65 | mCreateAccountButton.setOnClickListener(this);
66 | return view;
67 | }
68 |
69 |
70 | @Override
71 | public void onActivityCreated(Bundle savedInstanceState) {
72 | super.onActivityCreated(savedInstanceState);
73 |
74 | if(savedInstanceState != null){
75 | mEmailEditText.setText(savedInstanceState.getString(EXTRA_EMAIL));
76 | mUserNameEditText.setText(savedInstanceState.getString(EXTRA_USERNAME));
77 | mPasswordEditText.setText(savedInstanceState.getString(EXTRA_PASSWORD));
78 | mConfirmPasswordEditText.setText(savedInstanceState.getString(EXTRA_CONFIRM));
79 | }
80 | }
81 |
82 | @Override
83 | public void onSaveInstanceState(Bundle outState) {
84 | super.onSaveInstanceState(outState);
85 | outState.putString(EXTRA_EMAIL, mEmailEditText.getText().toString());
86 | outState.putString(EXTRA_USERNAME, mUserNameEditText.getText().toString());
87 | outState.putString(EXTRA_PASSWORD, mPasswordEditText.getText().toString());
88 | outState.putString(EXTRA_CONFIRM, mConfirmPasswordEditText.getText().toString());
89 | }
90 |
91 | @Override
92 | public void onClick(View v) {
93 | switch (v.getId()) {
94 | case R.id.btnCreateAccount:
95 | createAccount();
96 | break;
97 |
98 | default:
99 | break;
100 | }
101 | }
102 |
103 | /**
104 | * Some front end validation is done that is not monitored by the service.
105 | * If the form is complete then the information is passed to the service.
106 | */
107 | private void createAccount(){
108 | clearErrors();
109 |
110 | boolean cancel = false;
111 | View focusView = null;
112 |
113 | // Store values at the time of the login attempt.
114 | mEmail = mEmailEditText.getText().toString();
115 | mUsername = mUserNameEditText.getText().toString();
116 | mPassword = mPasswordEditText.getText().toString();
117 | mConfirmPassword = mConfirmPasswordEditText.getText().toString();
118 |
119 | // Check for a valid confirm password.
120 | if (TextUtils.isEmpty(mConfirmPassword)) {
121 | mConfirmPasswordEditText.setError(getString(R.string.error_field_required));
122 | focusView = mConfirmPasswordEditText;
123 | cancel = true;
124 | } else if (mPassword != null && !mConfirmPassword.equals(mPassword)) {
125 | mPasswordEditText.setError(getString(R.string.error_invalid_confirm_password));
126 | focusView = mPasswordEditText;
127 | cancel = true;
128 | }
129 | // Check for a valid password.
130 | if (TextUtils.isEmpty(mPassword)) {
131 | mPasswordEditText.setError(getString(R.string.error_field_required));
132 | focusView = mPasswordEditText;
133 | cancel = true;
134 | } else if (mPassword.length() < 4) {
135 | mPasswordEditText.setError(getString(R.string.error_invalid_password));
136 | focusView = mPasswordEditText;
137 | cancel = true;
138 | }
139 |
140 | // Check for a valid email address.
141 | if (TextUtils.isEmpty(mEmail)) {
142 | mEmailEditText.setError(getString(R.string.error_field_required));
143 | focusView = mEmailEditText;
144 | cancel = true;
145 | } else if (!mEmail.contains("@")) {
146 | mEmailEditText.setError(getString(R.string.error_invalid_email));
147 | focusView = mEmailEditText;
148 | cancel = true;
149 | }
150 |
151 | if (cancel) {
152 | // There was an error; don't attempt login and focus the first
153 | // form field with an error.
154 | focusView.requestFocus();
155 | } else {
156 | // Show a progress spinner, and kick off a background task to
157 | // perform the user login attempt.
158 | UserManager.getInstance().signUp(mUsername.toLowerCase(Locale.getDefault()), mEmail, mPassword);
159 |
160 | }
161 |
162 | }
163 |
164 | /**
165 | * Remove error messages from all fields.
166 | */
167 | private void clearErrors(){ mEmailEditText.setError(null);
168 | mUserNameEditText.setError(null);
169 | mPasswordEditText.setError(null);
170 | mConfirmPasswordEditText.setError(null);
171 | }
172 |
173 | @Subscribe
174 | public void onSignInError(AuthenticateUserErrorEvent event){
175 | clearErrors();
176 | switch (event.getErrorCode()) {
177 | case ParseException.INVALID_EMAIL_ADDRESS:
178 | mEmailEditText.setError(getString(R.string.error_invalid_email));
179 | mEmailEditText.requestFocus();
180 | break;
181 | case ParseException.EMAIL_TAKEN:
182 | mEmailEditText.setError(getString(R.string.error_duplicate_email));
183 | mEmailEditText.requestFocus();
184 | break;
185 | case ParseException.USERNAME_TAKEN:
186 | mUserNameEditText.setError(getString(R.string.error_duplicate_username));
187 | mUserNameEditText.requestFocus();
188 | break;
189 | default:
190 | UnknownErrorDialogFactory.createUnknownErrorDialog(this.getActivity()).show();
191 | break;
192 | }
193 | }
194 |
195 | }
196 |
--------------------------------------------------------------------------------
/src/com/keyconsultant/parse/logintutorial/LoginActivity.java:
--------------------------------------------------------------------------------
1 | package com.keyconsultant.parse.logintutorial;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.support.v4.app.FragmentManager;
6 | import android.support.v4.app.FragmentTransaction;
7 | import android.view.Menu;
8 | import android.view.MenuItem;
9 | import android.widget.Toast;
10 |
11 | import com.keyconsultant.parse.logintutorial.activity.BaseActivity;
12 | import com.keyconsultant.parse.logintutorial.forgotpassword.ForgotPasswordDialogFragment;
13 | import com.keyconsultant.parse.logintutorial.model.user.authenticate.AuthenticateUserErrorEvent;
14 | import com.keyconsultant.parse.logintutorial.model.user.authenticate.AuthenticateUserStartEvent;
15 | import com.keyconsultant.parse.logintutorial.model.user.authenticate.AuthenticateUserSuccessEvent;
16 | import com.keyconsultant.parse.logintutorial.model.user.authenticate.UserForgotPasswordErrorEvent;
17 | import com.keyconsultant.parse.logintutorial.model.user.authenticate.UserForgotPasswordStartEvent;
18 | import com.keyconsultant.parse.logintutorial.model.user.authenticate.UserForgotPasswordSuccessEvent;
19 | import com.parse.Parse;
20 | import com.squareup.otto.Subscribe;
21 |
22 | /**
23 | * Activity which displays a login screen to the user, offering registration as
24 | * well. Based loosley on the default Login template.
25 | *
26 | * @author Trey Robinson
27 | */
28 | public class LoginActivity extends BaseActivity {
29 |
30 | @Override
31 | protected void onCreate(Bundle savedInstanceState) {
32 | super.onCreate(savedInstanceState);
33 | setContentView(R.layout.activity_login);
34 |
35 |
36 | Parse.initialize(this, "Your App Id", "Your Client ID");
37 |
38 | FragmentManager fragmentManager = getSupportFragmentManager();
39 | FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
40 | fragmentTransaction.replace(R.id.main_view, LoginFragment.newInstance());
41 | fragmentTransaction.commit();
42 |
43 | }
44 |
45 | @Override
46 | public boolean onCreateOptionsMenu(Menu menu) {
47 | super.onCreateOptionsMenu(menu);
48 | getMenuInflater().inflate(R.menu.activity_login, menu);
49 | return true;
50 | }
51 |
52 | @Override
53 | public boolean onOptionsItemSelected(MenuItem item) {
54 | // Handle item selection
55 | switch (item.getItemId()) {
56 | case R.id.menu_forgot_password:
57 | forgotPassword();
58 | return true;
59 | default:
60 | return super.onOptionsItemSelected(item);
61 | }
62 | }
63 |
64 | /**
65 | * Open the forgotPassword dialog
66 | */
67 | private void forgotPassword(){
68 | FragmentManager fm = getSupportFragmentManager();
69 | ForgotPasswordDialogFragment forgotPasswordDialog = new ForgotPasswordDialogFragment();
70 | forgotPasswordDialog.show(fm, null);
71 | }
72 |
73 |
74 | @Subscribe
75 | public void onSignInStart(AuthenticateUserStartEvent event){
76 | showProgress(true, getString(R.string.login_progress_signing_in));
77 | }
78 |
79 | @Subscribe
80 | public void onSignInSuccess(AuthenticateUserSuccessEvent event){
81 | showProgress(false, getString(R.string.login_progress_signing_in));
82 | Intent loginSuccess = new Intent(this, MainActivity.class);
83 | startActivity(loginSuccess);
84 | finish();
85 | }
86 |
87 | @Subscribe
88 | public void onSignInError(AuthenticateUserErrorEvent event){
89 | showProgress(false, getString(R.string.login_progress_signing_in));
90 | }
91 |
92 | @Subscribe
93 | public void onForgotPasswordStart(UserForgotPasswordStartEvent event){
94 | showProgress(true, getString(R.string.login_progress_signing_in));
95 | }
96 |
97 | @Subscribe
98 | public void onForgotPasswordSuccess(UserForgotPasswordSuccessEvent event){
99 | showProgress(false,getString(R.string.login_progress_signing_in));
100 | Toast toast =Toast.makeText(this, "A password reset email has been sent.", Toast.LENGTH_LONG);
101 | toast.show();
102 | }
103 |
104 | @Subscribe
105 | public void onForgotPasswordError(UserForgotPasswordErrorEvent event){
106 | showProgress(false, getString(R.string.login_progress_signing_in));
107 | Toast toast =Toast.makeText(this, "An error has occured. Please try again.", Toast.LENGTH_LONG);
108 | toast.show();
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/com/keyconsultant/parse/logintutorial/LoginFragment.java:
--------------------------------------------------------------------------------
1 | package com.keyconsultant.parse.logintutorial;
2 |
3 | import java.util.Locale;
4 |
5 | import android.os.Bundle;
6 | import android.support.v4.app.FragmentManager;
7 | import android.support.v4.app.FragmentTransaction;
8 | import android.text.TextUtils;
9 | import android.view.KeyEvent;
10 | import android.view.LayoutInflater;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.view.inputmethod.EditorInfo;
14 | import android.widget.EditText;
15 | import android.widget.TextView;
16 |
17 | import com.keyconsultant.parse.logintutorial.error.UnknownErrorDialogFactory;
18 | import com.keyconsultant.parse.logintutorial.fragment.BaseFragment;
19 | import com.keyconsultant.parse.logintutorial.model.user.UserManager;
20 | import com.keyconsultant.parse.logintutorial.model.user.authenticate.AuthenticateUserErrorEvent;
21 | import com.parse.ParseException;
22 | import com.squareup.otto.Subscribe;
23 |
24 | /**
25 | * Fragment for logging in. Includes button for loading the Create account view.
26 | *
27 | * @author Trey Robinson
28 | *
29 | */
30 | public class LoginFragment extends BaseFragment {
31 |
32 | public static final String EXTRA_USERNAME = "com.keyconsultant.parse.logintutorial.activity.extra.USERNAME";
33 | public static final String EXTRA_PASSWORD = "com.keyconsultant.parse.logintutorial.activity.extra.PASSWORD";
34 |
35 | // UI references.
36 | private EditText mUserNameEditText;
37 | private EditText mPasswordEditText;
38 |
39 | /**
40 | * Factory method for creating new fragments
41 | * @return
42 | */
43 | public static LoginFragment newInstance(){
44 | return new LoginFragment();
45 | }
46 |
47 | @Override
48 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
49 | Bundle savedInstanceState) {
50 |
51 |
52 | View view = inflater.inflate(R.layout.fragment_login, container, false);
53 | mUserNameEditText = (EditText) view.findViewById(R.id.username);
54 |
55 | mPasswordEditText = (EditText) view.findViewById(R.id.password);
56 | mPasswordEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
57 | @Override
58 | public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
59 | if (id == R.id.login || id == EditorInfo.IME_NULL) {
60 | attemptLogin();
61 | return true;
62 | }
63 | return false;
64 | }
65 | });
66 |
67 | view.findViewById(R.id.sign_in_button).setOnClickListener(new View.OnClickListener() {
68 | @Override
69 | public void onClick(View view) {
70 | attemptLogin();
71 | }
72 | });
73 |
74 | view.findViewById(R.id.register_button).setOnClickListener(new View.OnClickListener() {
75 | @Override
76 | public void onClick(View view) {
77 | createAccount();
78 | }
79 | });
80 | return view;
81 | }
82 |
83 | @Override
84 | public void onActivityCreated(Bundle savedInstanceState) {
85 | super.onActivityCreated(savedInstanceState);
86 | if(savedInstanceState != null){
87 | mUserNameEditText.setText(savedInstanceState.getString(EXTRA_USERNAME));
88 | mPasswordEditText.setText(savedInstanceState.getString(EXTRA_PASSWORD));
89 | }
90 | }
91 |
92 | @Override
93 | public void onSaveInstanceState(Bundle outState) {
94 | super.onSaveInstanceState(outState);
95 | outState.putString(EXTRA_USERNAME, mUserNameEditText.getText().toString());
96 | outState.putString(EXTRA_PASSWORD, mPasswordEditText.getText().toString());
97 | }
98 |
99 | /**
100 | * Attempts to sign in or register the account specified by the login form.
101 | * If there are form errors (invalid email, missing fields, etc.), the
102 | * errors are presented and no actual login attempt is made.
103 | */
104 | public void attemptLogin() {
105 |
106 | clearErrors();
107 |
108 | // Store values at the time of the login attempt.
109 | String username = mUserNameEditText.getText().toString();
110 | String password = mPasswordEditText.getText().toString();
111 |
112 | boolean cancel = false;
113 | View focusView = null;
114 |
115 | // Check for a valid password.
116 | if (TextUtils.isEmpty(password)) {
117 | mPasswordEditText.setError(getString(R.string.error_field_required));
118 | focusView = mPasswordEditText;
119 | cancel = true;
120 | } else if (password.length() < 4) {
121 | mPasswordEditText.setError(getString(R.string.error_invalid_password));
122 | focusView =mPasswordEditText;
123 | cancel = true;
124 | }
125 |
126 | // Check for a valid email address.
127 | if (TextUtils.isEmpty(username)) {
128 | mUserNameEditText.setError(getString(R.string.error_field_required));
129 | focusView = mUserNameEditText;
130 | cancel = true;
131 | }
132 |
133 | if (cancel) {
134 | // There was an error; don't attempt login and focus the first
135 | // form field with an error.
136 | focusView.requestFocus();
137 | } else {
138 | // perform the user login attempt.
139 | UserManager.getInstance().authenticate(username.toLowerCase(Locale.getDefault()), password);
140 | }
141 | }
142 |
143 | /**
144 | * Load the create account view.
145 | */
146 | private void createAccount(){
147 | FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
148 | FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
149 | fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
150 | fragmentTransaction.replace(((ViewGroup)getView().getParent()).getId(), CreateAccountFragment.newInstance());
151 | fragmentTransaction.addToBackStack(null);
152 | fragmentTransaction.commit();
153 | }
154 |
155 | /**
156 | * Remove all edit text errors
157 | */
158 | private void clearErrors(){
159 | mUserNameEditText.setError(null);
160 | mPasswordEditText.setError(null);
161 | }
162 |
163 | @Subscribe
164 | public void onSignInError(AuthenticateUserErrorEvent event){
165 | clearErrors();
166 | switch (event.getErrorCode()) {
167 | case ParseException.OBJECT_NOT_FOUND:
168 | mPasswordEditText.setError(getString(R.string.error_incorrect_password));
169 | mPasswordEditText.requestFocus();
170 | break;
171 | default:
172 | UnknownErrorDialogFactory.createUnknownErrorDialog(this.getActivity()).show();
173 | break;
174 | }
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/src/com/keyconsultant/parse/logintutorial/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.keyconsultant.parse.logintutorial;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.widget.TextView;
6 |
7 | import com.keyconsultant.parse.logintutorial.model.user.UserManager;
8 |
9 | public class MainActivity extends Activity {
10 |
11 | private TextView currentUserTextView;
12 |
13 | @Override
14 | protected void onCreate(Bundle savedInstanceState) {
15 | super.onCreate(savedInstanceState);
16 | setContentView(R.layout.activity_main);
17 |
18 | currentUserTextView = (TextView)findViewById(R.id.tvCurrentUsername);
19 | }
20 |
21 | @Override
22 | protected void onResume() {
23 | super.onResume();
24 |
25 | currentUserTextView.setText("Welcome, " + UserManager.getInstance().getUser().getUsername());
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/com/keyconsultant/parse/logintutorial/activity/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package com.keyconsultant.parse.logintutorial.activity;
2 |
3 | import android.animation.Animator;
4 | import android.animation.AnimatorListenerAdapter;
5 | import android.annotation.TargetApi;
6 | import android.os.Build;
7 | import android.support.v4.app.FragmentActivity;
8 | import android.view.View;
9 |
10 | import com.keyconsultant.parse.logintutorial.R;
11 | import com.squareup.otto.BusProvider;
12 |
13 | /**
14 | * Base activity class that handles the interaction of the Activity with the service bus. Also includes
15 | * an application wide implementation of the progress dialog.
16 | *
17 | *
18 | * @author Trey Robinson
19 | *
20 | */
21 | public class BaseActivity extends FragmentActivity {
22 |
23 | protected StatusView mStatusView;
24 | protected View mMainView;
25 |
26 | @Override
27 | protected void onResume() {
28 | super.onResume();
29 | //register the activity to the service bus
30 | BusProvider.getInstance().register(this);
31 |
32 | //required view components for the loading screen
33 | mMainView = findViewById(R.id.main_view);
34 | mStatusView = (StatusView) findViewById(R.id.statusView);
35 | }
36 |
37 | @Override
38 | protected void onPause() {
39 | /**activity must be removed from the service bus in onPause or an error will occur
40 | when the bus attempts to dispatch an event to the paused activity. **/
41 | BusProvider.getInstance().unregister(this);
42 | super.onPause();
43 | }
44 |
45 | /**
46 | * Post the event to the service bus
47 | * @param event
48 | * The event to dispatch on the service bus
49 | */
50 | protected void postEvent(Object event) {
51 | BusProvider.getInstance().post(event);
52 | }
53 |
54 | /**
55 | * Shows the progress UI and hides the login form.
56 | */
57 | @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
58 | protected void showProgress(final boolean show, String message) {
59 | // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow
60 | // for very easy animations. If available, use these APIs to fade-in
61 | // the progress spinner.
62 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
63 | int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime);
64 |
65 | mStatusView.setVisibility(View.VISIBLE);
66 | mStatusView.animate()
67 | .setDuration(shortAnimTime)
68 | .alpha(show ? 1 : 0)
69 | .setListener(new AnimatorListenerAdapter() {
70 | @Override
71 | public void onAnimationEnd(Animator animation) {
72 | mStatusView.setVisibility(show ? View.VISIBLE : View.GONE);
73 | }
74 | });
75 |
76 | mMainView.setVisibility(View.VISIBLE);
77 | mMainView.animate()
78 | .setDuration(shortAnimTime)
79 | .alpha(show ? 0 : 1)
80 | .setListener(new AnimatorListenerAdapter() {
81 | @Override
82 | public void onAnimationEnd(Animator animation) {
83 | mMainView.setVisibility(show ? View.GONE : View.VISIBLE);
84 | }
85 | });
86 | } else {
87 | // The ViewPropertyAnimator APIs are not available, so simply show
88 | // and hide the relevant UI components.
89 | mStatusView.setVisibility(show ? View.VISIBLE : View.GONE);
90 | mMainView.setVisibility(show ? View.GONE : View.VISIBLE);
91 | }
92 | }
93 |
94 | }
95 |
--------------------------------------------------------------------------------
/src/com/keyconsultant/parse/logintutorial/activity/StatusView.java:
--------------------------------------------------------------------------------
1 | package com.keyconsultant.parse.logintutorial.activity;
2 |
3 | import com.keyconsultant.parse.logintutorial.R;
4 |
5 | import android.content.Context;
6 | import android.util.AttributeSet;
7 | import android.view.LayoutInflater;
8 | import android.widget.LinearLayout;
9 | import android.widget.TextView;
10 |
11 | /**
12 | * Loading view with nondeterminate spinner
13 | *
14 | * @author Trey Robinson
15 | *
16 | */
17 | public class StatusView extends LinearLayout {
18 |
19 | private TextView mStatusMessage;
20 |
21 | public StatusView(Context context) {
22 | super(context);
23 | initViews(context);
24 | }
25 |
26 | public StatusView(Context context, AttributeSet attrs) {
27 | super(context, attrs);
28 | initViews(context);
29 | }
30 |
31 | public StatusView(Context context, AttributeSet attrs, int defStyle) {
32 | super(context, attrs, defStyle);
33 | initViews(context);
34 | }
35 |
36 | private void initViews(Context context){
37 |
38 | LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
39 | inflater.inflate(R.layout.view_status, this, true);
40 | mStatusMessage = (TextView)findViewById(R.id.status_message);
41 | }
42 |
43 | /**
44 | * Set the message to display on the loading screen.
45 | * @param statusMessage
46 | * Message to display on the loading screen
47 | */
48 | public void setStatusMessage(String statusMessage){
49 | mStatusMessage.setText(statusMessage);
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/com/keyconsultant/parse/logintutorial/error/UnknownErrorDialogFactory.java:
--------------------------------------------------------------------------------
1 | package com.keyconsultant.parse.logintutorial.error;
2 |
3 | import android.app.AlertDialog;
4 | import android.content.Context;
5 | import android.content.DialogInterface;
6 |
7 | /**
8 | * Dialog factory for unknown errors. (Lazy :-) ).
9 | *
10 | * @author Trey Robinson
11 | *
12 | */
13 | public class UnknownErrorDialogFactory {
14 |
15 | public static AlertDialog createUnknownErrorDialog(Context context){
16 | AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context);
17 |
18 | // set title
19 | alertDialogBuilder.setTitle("Error");
20 |
21 | // set dialog message
22 | alertDialogBuilder
23 | .setMessage("An Unknown Error Occured.")
24 | .setCancelable(false)
25 | .setPositiveButton("OK :-( ",new DialogInterface.OnClickListener() {
26 | public void onClick(DialogInterface dialog,int id) {
27 | // if this button is clicked, close
28 | // current activity
29 | dialog.cancel();
30 | }
31 | });
32 |
33 | // create alert dialog
34 | AlertDialog alertDialog = alertDialogBuilder.create();
35 |
36 | return alertDialog;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/com/keyconsultant/parse/logintutorial/event/ErrorEvent.java:
--------------------------------------------------------------------------------
1 | package com.keyconsultant.parse.logintutorial.event;
2 |
3 | import com.parse.ParseException;
4 |
5 | /**
6 | * Event that provides access to internal exception details
7 | *
8 | * @author Trey Robinson
9 | *
10 | */
11 | public class ErrorEvent {
12 |
13 | private ParseException exception;
14 |
15 | public ErrorEvent(ParseException exception){
16 | this.exception = exception;
17 | }
18 |
19 | /**
20 | * @return
21 | * Code for the error.
22 | */
23 | public int getErrorCode(){
24 | return exception.getCode();
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/com/keyconsultant/parse/logintutorial/forgotpassword/ForgotPasswordDialogFragment.java:
--------------------------------------------------------------------------------
1 | package com.keyconsultant.parse.logintutorial.forgotpassword;
2 |
3 | import com.keyconsultant.parse.logintutorial.R;
4 | import com.keyconsultant.parse.logintutorial.model.user.UserManager;
5 |
6 | import android.os.Bundle;
7 | import android.support.v4.app.DialogFragment;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.View.OnClickListener;
11 | import android.view.ViewGroup;
12 | import android.widget.EditText;
13 |
14 | /**
15 | * Simple dialog for entering the email for a forgotten password.
16 | *
17 | * @author Trey Robinson
18 | *
19 | */
20 | public class ForgotPasswordDialogFragment extends DialogFragment implements OnClickListener{
21 | private EditText mEmailEditText;
22 |
23 | public ForgotPasswordDialogFragment (){
24 |
25 | }
26 |
27 | @Override
28 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
29 | Bundle savedInstanceState) {
30 | View view = inflater.inflate(R.layout.dialog_forgot_password, container);
31 | mEmailEditText = (EditText) view.findViewById(R.id.etEmail);
32 | view.findViewById(R.id.submitButton).setOnClickListener(this);
33 |
34 |
35 | getDialog().setTitle("Enter Email Below.");
36 |
37 | return view;
38 | }
39 |
40 | @Override
41 | public void onClick(View v) {
42 | switch (v.getId()) {
43 | case R.id.submitButton:
44 | submitForgotPassword();
45 | break;
46 | default:
47 | break;
48 | }
49 | }
50 |
51 | private void submitForgotPassword(){
52 |
53 |
54 | UserManager.getInstance().forgotPassword(mEmailEditText.getText().toString());
55 | this.dismiss();
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/com/keyconsultant/parse/logintutorial/fragment/BaseFragment.java:
--------------------------------------------------------------------------------
1 | package com.keyconsultant.parse.logintutorial.fragment;
2 |
3 | import com.squareup.otto.BusProvider;
4 |
5 | import android.support.v4.app.Fragment;
6 |
7 | /**
8 | * Fragment class that resolves basic service bus requirement (Register, deregister, Event Posting)
9 | *
10 | * @author Trey Robinson
11 | *
12 | */
13 | public class BaseFragment extends Fragment {
14 | @Override
15 | public void onResume() {
16 | super.onResume();
17 | BusProvider.getInstance().register(this);
18 |
19 | }
20 |
21 | @Override
22 | public void onPause() {
23 | /**fragment must be removed from the service bus in onPause or an error will occur
24 | when the bus attempts to dispatch an event to the paused activity. **/
25 | BusProvider.getInstance().unregister(this);
26 | super.onPause();
27 | }
28 |
29 | /**
30 | * Post the event to the service bus
31 | * @param event
32 | * The event to dispatch on the service bus
33 | */
34 | protected void postEvent(Object event) {
35 | BusProvider.getInstance().post(event);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/com/keyconsultant/parse/logintutorial/model/manager/BaseManager.java:
--------------------------------------------------------------------------------
1 | package com.keyconsultant.parse.logintutorial.model.manager;
2 |
3 | import com.squareup.otto.BusProvider;
4 |
5 | /**
6 | * Base manager handles bus related functions.
7 | *
8 | * @author Trey Robinson
9 | *
10 | */
11 | public class BaseManager {
12 |
13 | protected void postEvent(Object event){
14 | BusProvider.getInstance().post(event);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/com/keyconsultant/parse/logintutorial/model/user/User.java:
--------------------------------------------------------------------------------
1 | package com.keyconsultant.parse.logintutorial.model.user;
2 |
3 | import com.parse.ParseUser;
4 |
5 | public class User extends ParseUser{
6 |
7 | public User(String username, String email, String password){
8 | this.setEmail(email);
9 | this.setUsername(username);
10 | this.setPassword(password);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/src/com/keyconsultant/parse/logintutorial/model/user/UserManager.java:
--------------------------------------------------------------------------------
1 | package com.keyconsultant.parse.logintutorial.model.user;
2 |
3 | import com.keyconsultant.parse.logintutorial.model.manager.BaseManager;
4 | import com.keyconsultant.parse.logintutorial.model.user.authenticate.AuthenticateUserErrorEvent;
5 | import com.keyconsultant.parse.logintutorial.model.user.authenticate.AuthenticateUserStartEvent;
6 | import com.keyconsultant.parse.logintutorial.model.user.authenticate.AuthenticateUserSuccessEvent;
7 | import com.keyconsultant.parse.logintutorial.model.user.authenticate.UserForgotPasswordErrorEvent;
8 | import com.keyconsultant.parse.logintutorial.model.user.authenticate.UserForgotPasswordStartEvent;
9 | import com.keyconsultant.parse.logintutorial.model.user.authenticate.UserForgotPasswordSuccessEvent;
10 | import com.parse.LogInCallback;
11 | import com.parse.ParseException;
12 | import com.parse.ParseUser;
13 | import com.parse.RequestPasswordResetCallback;
14 | import com.parse.SignUpCallback;
15 | import com.squareup.otto.Produce;
16 |
17 | /**
18 | * Manages user authentication and account updates
19 | *
20 | * @author Trey Robinson
21 | *
22 | */
23 | public class UserManager extends BaseManager {
24 |
25 | private static UserManager mManager;
26 |
27 | public static UserManager getInstance() {
28 | if (mManager == null) {
29 | mManager = new UserManager();
30 | }
31 |
32 | return mManager;
33 | }
34 |
35 | /**
36 | * @return Current user
37 | */
38 | public ParseUser getUser() {
39 | return ParseUser.getCurrentUser();
40 | }
41 |
42 | /**
43 | * Authenticate the user
44 | *
45 | * @param username
46 | * Username for the account
47 | * @param password
48 | * Password for the account
49 | */
50 | public void authenticate(String username, String password) {
51 | postEvent(produceUserSignInStartEvent());
52 | ParseUser.logInInBackground(username, password, new UserLogInCallback());
53 | }
54 |
55 | /**
56 | * Create new user. If the user email already exists an error will be
57 | * dispatched via the service bus
58 | *
59 | * @param email
60 | * The user email for the new account
61 | * @param password
62 | * Password for the new account
63 | */
64 | public void signUp(String username, String email, String password) {
65 | User newUser = new User(username, email, password);
66 | postEvent(produceUserSignInStartEvent());
67 | newUser.signUpInBackground(new UserSignUpCallback(newUser));
68 | }
69 |
70 | /**
71 | * Dispatches a forgot password request. The request will send an email to the address if an account for that address exists.
72 | * @param email
73 | * Email for the account that has forgotten their password
74 | */
75 | public void forgotPassword(String email) {
76 | postEvent(new UserForgotPasswordStartEvent());
77 | ParseUser.requestPasswordResetInBackground(email, new UserForgotPasswordCallback());
78 | }
79 |
80 | /**
81 | * Creates an event notifying that authentication has begun
82 | *
83 | * @return
84 | */
85 | @Produce
86 | public AuthenticateUserStartEvent produceUserSignInStartEvent() {
87 | return new AuthenticateUserStartEvent();
88 | }
89 |
90 | /**
91 | * Creates an event containing the signed in user
92 | *
93 | * @param user
94 | * User currently signed in
95 | * @return
96 | */
97 | @Produce
98 | public AuthenticateUserSuccessEvent produceUserSignInSuccessEvent(
99 | ParseUser user) {
100 | return new AuthenticateUserSuccessEvent(user);
101 | }
102 |
103 | /**
104 | * Creates an even for sign in errors
105 | *
106 | * @return
107 | */
108 | @Produce
109 | public AuthenticateUserErrorEvent produceUserSignInErrorEvent(
110 | ParseException exception) {
111 | return new AuthenticateUserErrorEvent(exception);
112 | }
113 |
114 | /**
115 | * Creates an event notifying that forgot password has begun
116 | *
117 | * @return
118 | */
119 | @Produce
120 | public UserForgotPasswordStartEvent produceUserForgotPasswordStartEvent() {
121 | return new UserForgotPasswordStartEvent();
122 | }
123 |
124 | /**
125 | * Creates an event for successful Forgot Password request
126 | *
127 | * @param user
128 | * User currently signed in
129 | * @return
130 | */
131 | @Produce
132 | public UserForgotPasswordSuccessEvent produceUserForgotPasswordSuccessEvent() {
133 | return new UserForgotPasswordSuccessEvent();
134 | }
135 |
136 | /**
137 | * Creates an event for failed Forgot Password attempt
138 | *
139 | * @return
140 | */
141 | @Produce
142 | public UserForgotPasswordErrorEvent produceUserForgotPasswordErrorEvent(ParseException exception) {
143 | return new UserForgotPasswordErrorEvent(exception);
144 | }
145 |
146 | private class UserSignUpCallback extends SignUpCallback {
147 |
148 | User user;
149 |
150 | public UserSignUpCallback(User user) {
151 | this.user = user;
152 | }
153 |
154 | @Override
155 | public void done(ParseException e) {
156 | if (e == null) {
157 | postEvent(produceUserSignInSuccessEvent(user));
158 | } else {
159 | postEvent(produceUserSignInErrorEvent(e));
160 | }
161 |
162 | }
163 | }
164 |
165 | private class UserLogInCallback extends LogInCallback {
166 |
167 | public UserLogInCallback() {
168 | super();
169 | }
170 |
171 | @Override
172 | public void done(ParseUser user, ParseException e) {
173 | if (e == null) {
174 | postEvent(produceUserSignInSuccessEvent(user));
175 | } else {
176 | postEvent(produceUserSignInErrorEvent(e));
177 | }
178 | }
179 | }
180 |
181 | private class UserForgotPasswordCallback extends RequestPasswordResetCallback{
182 | public UserForgotPasswordCallback(){
183 | super();
184 | }
185 |
186 | @Override
187 | public void done(ParseException e) {
188 | if (e == null) {
189 | postEvent(produceUserForgotPasswordSuccessEvent());
190 | } else {
191 | postEvent(produceUserForgotPasswordErrorEvent(e));
192 | }
193 | }
194 | }
195 | }
196 |
--------------------------------------------------------------------------------
/src/com/keyconsultant/parse/logintutorial/model/user/authenticate/AuthenticateUserErrorEvent.java:
--------------------------------------------------------------------------------
1 | package com.keyconsultant.parse.logintutorial.model.user.authenticate;
2 |
3 | import com.keyconsultant.parse.logintutorial.event.ErrorEvent;
4 | import com.parse.ParseException;
5 |
6 | public class AuthenticateUserErrorEvent extends ErrorEvent {
7 |
8 | public AuthenticateUserErrorEvent(ParseException exception) {
9 | super(exception);
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/com/keyconsultant/parse/logintutorial/model/user/authenticate/AuthenticateUserStartEvent.java:
--------------------------------------------------------------------------------
1 | package com.keyconsultant.parse.logintutorial.model.user.authenticate;
2 |
3 | public class AuthenticateUserStartEvent {
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/src/com/keyconsultant/parse/logintutorial/model/user/authenticate/AuthenticateUserSuccessEvent.java:
--------------------------------------------------------------------------------
1 | package com.keyconsultant.parse.logintutorial.model.user.authenticate;
2 |
3 | import com.parse.ParseUser;
4 |
5 |
6 | public class AuthenticateUserSuccessEvent {
7 |
8 | public ParseUser user;
9 | public AuthenticateUserSuccessEvent(ParseUser user) {
10 | super();
11 |
12 | this.user = user;
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/src/com/keyconsultant/parse/logintutorial/model/user/authenticate/UserForgotPasswordErrorEvent.java:
--------------------------------------------------------------------------------
1 | package com.keyconsultant.parse.logintutorial.model.user.authenticate;
2 |
3 | import com.keyconsultant.parse.logintutorial.event.ErrorEvent;
4 | import com.parse.ParseException;
5 |
6 | /**
7 | * Event for forgot password errors.
8 | * @author Trey Robinson
9 | *
10 | */
11 | public class UserForgotPasswordErrorEvent extends ErrorEvent {
12 |
13 | public UserForgotPasswordErrorEvent(ParseException exception) {
14 | super(exception);
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/src/com/keyconsultant/parse/logintutorial/model/user/authenticate/UserForgotPasswordStartEvent.java:
--------------------------------------------------------------------------------
1 | package com.keyconsultant.parse.logintutorial.model.user.authenticate;
2 |
3 | /**
4 | * Event for forgot password request starting.
5 | * @author Trey Robinson
6 | *
7 | */
8 | public class UserForgotPasswordStartEvent {
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/com/keyconsultant/parse/logintutorial/model/user/authenticate/UserForgotPasswordSuccessEvent.java:
--------------------------------------------------------------------------------
1 | package com.keyconsultant.parse.logintutorial.model.user.authenticate;
2 |
3 | /**
4 | * Event for forgot password email dispatch success.
5 | * @author Trey Robinson
6 | *
7 | */
8 | public class UserForgotPasswordSuccessEvent {
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/src/com/squareup/otto/AnnotatedHandlerFinder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 Square, Inc.
3 | * Copyright (C) 2007 The Guava Authors
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.squareup.otto;
19 |
20 | import java.lang.reflect.Method;
21 | import java.lang.reflect.Modifier;
22 | import java.util.HashMap;
23 | import java.util.HashSet;
24 | import java.util.Map;
25 | import java.util.Set;
26 |
27 | /**
28 | * Helper methods for finding methods annotated with {@link Produce} and {@link Subscribe}.
29 | *
30 | * @author Cliff Biffle
31 | * @author Louis Wasserman
32 | * @author Jake Wharton
33 | */
34 | final class AnnotatedHandlerFinder {
35 |
36 | /** Cache event bus producer methods for each class. */
37 | private static final Map, Map, Method>> PRODUCERS_CACHE =
38 | new HashMap, Map, Method>>();
39 |
40 | /** Cache event bus subscriber methods for each class. */
41 | private static final Map, Map, Set>> SUBSCRIBERS_CACHE =
42 | new HashMap, Map, Set>>();
43 |
44 | /**
45 | * Load all methods annotated with {@link Produce} or {@link Subscribe} into their respective caches for the
46 | * specified class.
47 | */
48 | private static void loadAnnotatedMethods(Class> listenerClass) {
49 | Map, Set> subscriberMethods = new HashMap, Set>();
50 | Map, Method> producerMethods = new HashMap, Method>();
51 |
52 | for (Method method : listenerClass.getDeclaredMethods()) {
53 | if (method.isAnnotationPresent(Subscribe.class)) {
54 | Class>[] parameterTypes = method.getParameterTypes();
55 | if (parameterTypes.length != 1) {
56 | throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation but requires "
57 | + parameterTypes.length + " arguments. Methods must require a single argument.");
58 | }
59 |
60 | Class> eventType = parameterTypes[0];
61 | if (eventType.isInterface()) {
62 | throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation on " + eventType
63 | + " which is an interface. Subscription must be on a concrete class type.");
64 | }
65 |
66 | if ((method.getModifiers() & Modifier.PUBLIC) == 0) {
67 | throw new IllegalArgumentException("Method " + method + " has @Subscribe annotation on " + eventType
68 | + " but is not 'public'.");
69 | }
70 |
71 | Set methods = subscriberMethods.get(eventType);
72 | if (methods == null) {
73 | methods = new HashSet();
74 | subscriberMethods.put(eventType, methods);
75 | }
76 | methods.add(method);
77 | } else if (method.isAnnotationPresent(Produce.class)) {
78 | Class>[] parameterTypes = method.getParameterTypes();
79 | if (parameterTypes.length != 0) {
80 | throw new IllegalArgumentException("Method " + method + "has @Produce annotation but requires "
81 | + parameterTypes.length + " arguments. Methods must require zero arguments.");
82 | }
83 | if (method.getReturnType() == Void.class) {
84 | throw new IllegalArgumentException("Method " + method
85 | + " has a return type of void. Must declare a non-void type.");
86 | }
87 |
88 | Class> eventType = method.getReturnType();
89 | if (eventType.isInterface()) {
90 | throw new IllegalArgumentException("Method " + method + " has @Produce annotation on " + eventType
91 | + " which is an interface. Producers must return a concrete class type.");
92 | }
93 | if (eventType.equals(Void.TYPE)) {
94 | throw new IllegalArgumentException("Method " + method + " has @Produce annotation but has no return type.");
95 | }
96 |
97 | if ((method.getModifiers() & Modifier.PUBLIC) == 0) {
98 | throw new IllegalArgumentException("Method " + method + " has @Produce annotation on " + eventType
99 | + " but is not 'public'.");
100 | }
101 |
102 | if (producerMethods.containsKey(eventType)) {
103 | throw new IllegalArgumentException("Producer for type " + eventType + " has already been registered.");
104 | }
105 | producerMethods.put(eventType, method);
106 | }
107 | }
108 |
109 | PRODUCERS_CACHE.put(listenerClass, producerMethods);
110 | SUBSCRIBERS_CACHE.put(listenerClass, subscriberMethods);
111 | }
112 |
113 | /** This implementation finds all methods marked with a {@link Produce} annotation. */
114 | static Map, EventProducer> findAllProducers(Object listener) {
115 | final Class> listenerClass = listener.getClass();
116 | Map, EventProducer> handlersInMethod = new HashMap, EventProducer>();
117 |
118 | if (!PRODUCERS_CACHE.containsKey(listenerClass)) {
119 | loadAnnotatedMethods(listenerClass);
120 | }
121 | Map, Method> methods = PRODUCERS_CACHE.get(listenerClass);
122 | if (!methods.isEmpty()) {
123 | for (Map.Entry, Method> e : methods.entrySet()) {
124 | EventProducer producer = new EventProducer(listener, e.getValue());
125 | handlersInMethod.put(e.getKey(), producer);
126 | }
127 | }
128 |
129 | return handlersInMethod;
130 | }
131 |
132 | /** This implementation finds all methods marked with a {@link Subscribe} annotation. */
133 | static Map, Set> findAllSubscribers(Object listener) {
134 | Class> listenerClass = listener.getClass();
135 | Map, Set> handlersInMethod = new HashMap, Set>();
136 |
137 | if (!SUBSCRIBERS_CACHE.containsKey(listenerClass)) {
138 | loadAnnotatedMethods(listenerClass);
139 | }
140 | Map, Set> methods = SUBSCRIBERS_CACHE.get(listenerClass);
141 | if (!methods.isEmpty()) {
142 | for (Map.Entry, Set> e : methods.entrySet()) {
143 | Set handlers = new HashSet();
144 | for (Method m : e.getValue()) {
145 | handlers.add(new EventHandler(listener, m));
146 | }
147 | handlersInMethod.put(e.getKey(), handlers);
148 | }
149 | }
150 |
151 | return handlersInMethod;
152 | }
153 |
154 | private AnnotatedHandlerFinder() {
155 | // No instances.
156 | }
157 |
158 | }
159 |
--------------------------------------------------------------------------------
/src/com/squareup/otto/Bus.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 Square, Inc.
3 | * Copyright (C) 2007 The Guava Authors
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.squareup.otto;
19 |
20 | import java.lang.reflect.InvocationTargetException;
21 | import java.util.Collection;
22 | import java.util.HashMap;
23 | import java.util.HashSet;
24 | import java.util.LinkedList;
25 | import java.util.List;
26 | import java.util.Map;
27 | import java.util.Set;
28 | import java.util.concurrent.ConcurrentHashMap;
29 | import java.util.concurrent.ConcurrentLinkedQueue;
30 | import java.util.concurrent.ConcurrentMap;
31 | import java.util.concurrent.CopyOnWriteArraySet;
32 |
33 |
34 | /**
35 | * Dispatches events to listeners, and provides ways for listeners to register themselves.
36 | *
37 | *
The Bus allows publish-subscribe-style communication between components without requiring the components to
38 | * explicitly register with one another (and thus be aware of each other). It is designed exclusively to replace
39 | * traditional Android in-process event distribution using explicit registration or listeners. It is not a
40 | * general-purpose publish-subscribe system, nor is it intended for interprocess communication.
41 | *
42 | *
Receiving Events
43 | * To receive events, an object should:
44 | *
45 | *
Expose a public method, known as the event handler, which accepts a single argument of the type of event
46 | * desired;
47 | *
Mark it with a {@link com.squareup.otto.Subscribe} annotation;
48 | *
Pass itself to an Bus instance's {@link #register(Object)} method.
49 | *
50 | *
51 | *
52 | *
Posting Events
53 | * To post an event, simply provide the event object to the {@link #post(Object)} method. The Bus instance will
54 | * determine the type of event and route it to all registered listeners.
55 | *
56 | *
Events are routed based on their type — an event will be delivered to any handler for any type to which the
57 | * event is assignable. This includes implemented interfaces, all superclasses, and all interfaces implemented
58 | * by superclasses.
59 | *
60 | *
When {@code post} is called, all registered handlers for an event are run in sequence, so handlers should be
61 | * reasonably quick. If an event may trigger an extended process (such as a database load), spawn a thread or queue it
62 | * for later.
63 | *
64 | *
Handler Methods
65 | * Event handler methods must accept only one argument: the event.
66 | *
67 | *
Handlers should not, in general, throw. If they do, the Bus will wrap the exception and
68 | * re-throw it.
69 | *
70 | *
The Bus by default enforces that all interactions occur on the main thread. You can provide an alternate
71 | * enforcement by passing a {@link ThreadEnforcer} to the constructor.
72 | *
73 | *
Producer Methods
74 | * Producer methods should accept no arguments and return their event type. When a subscriber is registered for a type
75 | * that a producer is also already registered for, the subscriber will be called with the return value from the
76 | * producer.
77 | *
78 | *
Dead Events
79 | * If an event is posted, but no registered handlers can accept it, it is considered "dead." To give the system a
80 | * second chance to handle dead events, they are wrapped in an instance of {@link com.squareup.otto.DeadEvent} and
81 | * reposted.
82 | *
83 | *
This class is safe for concurrent use.
84 | *
85 | * @author Cliff Biffle
86 | * @author Jake Wharton
87 | */
88 | public class Bus {
89 | public static final String DEFAULT_IDENTIFIER = "default";
90 |
91 | /** All registered event handlers, indexed by event type. */
92 | private final ConcurrentMap, Set> handlersByType =
93 | new ConcurrentHashMap, Set>();
94 |
95 | /** All registered event producers, index by event type. */
96 | private final ConcurrentMap, EventProducer> producersByType =
97 | new ConcurrentHashMap, EventProducer>();
98 |
99 | /** Identifier used to differentiate the event bus instance. */
100 | private final String identifier;
101 |
102 | /** Thread enforcer for register, unregister, and posting events. */
103 | private final ThreadEnforcer enforcer;
104 |
105 | /** Used to find handler methods in register and unregister. */
106 | private final HandlerFinder handlerFinder;
107 |
108 | /** Queues of events for the current thread to dispatch. */
109 | private final ThreadLocal> eventsToDispatch =
110 | new ThreadLocal>() {
111 | @Override protected ConcurrentLinkedQueue initialValue() {
112 | return new ConcurrentLinkedQueue();
113 | }
114 | };
115 |
116 | /** True if the current thread is currently dispatching an event. */
117 | private final ThreadLocal isDispatching = new ThreadLocal() {
118 | @Override protected Boolean initialValue() {
119 | return false;
120 | }
121 | };
122 |
123 | /** Creates a new Bus named "default" that enforces actions on the main thread. */
124 | public Bus() {
125 | this(DEFAULT_IDENTIFIER);
126 | }
127 |
128 | /**
129 | * Creates a new Bus with the given {@code identifier} that enforces actions on the main thread.
130 | *
131 | * @param identifier a brief name for this bus, for debugging purposes. Should be a valid Java identifier.
132 | */
133 | public Bus(String identifier) {
134 | this(ThreadEnforcer.MAIN, identifier);
135 | }
136 |
137 | /**
138 | * Creates a new Bus named "default" with the given {@code enforcer} for actions.
139 | *
140 | * @param enforcer Thread enforcer for register, unregister, and post actions.
141 | */
142 | public Bus(ThreadEnforcer enforcer) {
143 | this(enforcer, DEFAULT_IDENTIFIER);
144 | }
145 |
146 | /**
147 | * Creates a new Bus with the given {@code enforcer} for actions and the given {@code identifier}.
148 | *
149 | * @param enforcer Thread enforcer for register, unregister, and post actions.
150 | * @param identifier A brief name for this bus, for debugging purposes. Should be a valid Java identifier.
151 | */
152 | public Bus(ThreadEnforcer enforcer, String identifier) {
153 | this(enforcer, identifier, HandlerFinder.ANNOTATED);
154 | }
155 |
156 | /**
157 | * Test constructor which allows replacing the default {@code HandlerFinder}.
158 | *
159 | * @param enforcer Thread enforcer for register, unregister, and post actions.
160 | * @param identifier A brief name for this bus, for debugging purposes. Should be a valid Java identifier.
161 | * @param handlerFinder Used to discover event handlers and producers when registering/unregistering an object.
162 | */
163 | Bus(ThreadEnforcer enforcer, String identifier, HandlerFinder handlerFinder) {
164 | this.enforcer = enforcer;
165 | this.identifier = identifier;
166 | this.handlerFinder = handlerFinder;
167 | }
168 |
169 | @Override public String toString() {
170 | return "[Bus \"" + identifier + "\"]";
171 | }
172 |
173 | /**
174 | * Registers all handler methods on {@code object} to receive events and producer methods to provide events.
175 | *
176 | * If any subscribers are registering for types which already have a producer they will be called immediately
177 | * with the result of calling that producer.
178 | *
179 | * If any producers are registering for types which already have subscribers, each subscriber will be called with
180 | * the value from the result of calling the producer.
181 | *
182 | * @param object object whose handler methods should be registered.
183 | */
184 | public void register(Object object) {
185 | enforcer.enforce(this);
186 |
187 | Map, EventProducer> foundProducers = handlerFinder.findAllProducers(object);
188 | for (Class> type : foundProducers.keySet()) {
189 |
190 | final EventProducer producer = foundProducers.get(type);
191 | EventProducer previousProducer = producersByType.putIfAbsent(type, producer);
192 | //checking if the previous producer existed
193 | if (previousProducer != null) {
194 | throw new IllegalArgumentException("Producer method for type " + type + " already registered.");
195 | }
196 | Set handlers = handlersByType.get(type);
197 | if (handlers != null && !handlers.isEmpty()) {
198 | for (EventHandler handler : handlers) {
199 | dispatchProducerResultToHandler(handler, producer);
200 | }
201 | }
202 | }
203 |
204 | Map, Set> foundHandlersMap = handlerFinder.findAllSubscribers(object);
205 | for (Class> type : foundHandlersMap.keySet()) {
206 | Set handlers = handlersByType.get(type);
207 | if (handlers == null) {
208 | //concurrent put if absent
209 | Set handlersCreation = new CopyOnWriteArraySet();
210 | handlers = handlersByType.putIfAbsent(type, handlersCreation);
211 | if (handlers == null) {
212 | handlers = handlersCreation;
213 | }
214 | }
215 | final Set foundHandlers = foundHandlersMap.get(type);
216 | handlers.addAll(foundHandlers);
217 | }
218 |
219 | for (Map.Entry, Set> entry : foundHandlersMap.entrySet()) {
220 | Class> type = entry.getKey();
221 | EventProducer producer = producersByType.get(type);
222 | if (producer != null && producer.isValid()) {
223 | Set foundHandlers = entry.getValue();
224 | for (EventHandler foundHandler : foundHandlers) {
225 | if (!producer.isValid()) {
226 | break;
227 | }
228 | if (foundHandler.isValid()) {
229 | dispatchProducerResultToHandler(foundHandler, producer);
230 | }
231 | }
232 | }
233 | }
234 | }
235 |
236 | private void dispatchProducerResultToHandler(EventHandler handler, EventProducer producer) {
237 | Object event = null;
238 | try {
239 | event = producer.produceEvent();
240 | } catch (InvocationTargetException e) {
241 | throw new RuntimeException("Producer " + producer + " threw an exception.", e);
242 | }
243 | if (event == null) {
244 | return;
245 | }
246 | dispatch(event, handler);
247 | }
248 |
249 | /**
250 | * Unregisters all producer and handler methods on a registered {@code object}.
251 | *
252 | * @param object object whose producer and handler methods should be unregistered.
253 | * @throws IllegalArgumentException if the object was not previously registered.
254 | */
255 | public void unregister(Object object) {
256 | enforcer.enforce(this);
257 |
258 | Map, EventProducer> producersInListener = handlerFinder.findAllProducers(object);
259 | for (Map.Entry, EventProducer> entry : producersInListener.entrySet()) {
260 | final Class> key = entry.getKey();
261 | EventProducer producer = getProducerForEventType(key);
262 | EventProducer value = entry.getValue();
263 |
264 | if (value == null || !value.equals(producer)) {
265 | throw new IllegalArgumentException(
266 | "Missing event producer for an annotated method. Is " + object.getClass()
267 | + " registered?");
268 | }
269 | producersByType.remove(key).invalidate();
270 | }
271 |
272 | Map, Set> handlersInListener = handlerFinder.findAllSubscribers(object);
273 | for (Map.Entry, Set> entry : handlersInListener.entrySet()) {
274 | Set currentHandlers = getHandlersForEventType(entry.getKey());
275 | Collection eventMethodsInListener = entry.getValue();
276 |
277 | if (currentHandlers == null || !currentHandlers.containsAll(eventMethodsInListener)) {
278 | throw new IllegalArgumentException(
279 | "Missing event handler for an annotated method. Is " + object.getClass()
280 | + " registered?");
281 | }
282 |
283 | for (EventHandler handler : currentHandlers) {
284 | if (eventMethodsInListener.contains(handler)) {
285 | handler.invalidate();
286 | }
287 | }
288 | currentHandlers.removeAll(eventMethodsInListener);
289 | }
290 | }
291 |
292 | /**
293 | * Posts an event to all registered handlers. This method will return successfully after the event has been posted to
294 | * all handlers, and regardless of any exceptions thrown by handlers.
295 | *
296 | *
If no handlers have been subscribed for {@code event}'s class, and {@code event} is not already a
297 | * {@link DeadEvent}, it will be wrapped in a DeadEvent and reposted.
298 | *
299 | * @param event event to post.
300 | */
301 | public void post(Object event) {
302 | enforcer.enforce(this);
303 |
304 | Set> dispatchTypes = flattenHierarchy(event.getClass());
305 |
306 | boolean dispatched = false;
307 | for (Class> eventType : dispatchTypes) {
308 | Set wrappers = getHandlersForEventType(eventType);
309 |
310 | if (wrappers != null && !wrappers.isEmpty()) {
311 | dispatched = true;
312 | for (EventHandler wrapper : wrappers) {
313 | enqueueEvent(event, wrapper);
314 | }
315 | }
316 | }
317 |
318 | if (!dispatched && !(event instanceof DeadEvent)) {
319 | post(new DeadEvent(this, event));
320 | }
321 |
322 | dispatchQueuedEvents();
323 | }
324 |
325 | /**
326 | * Queue the {@code event} for dispatch during {@link #dispatchQueuedEvents()}. Events are queued in-order of
327 | * occurrence so they can be dispatched in the same order.
328 | */
329 | protected void enqueueEvent(Object event, EventHandler handler) {
330 | eventsToDispatch.get().offer(new EventWithHandler(event, handler));
331 | }
332 |
333 | /**
334 | * Drain the queue of events to be dispatched. As the queue is being drained, new events may be posted to the end of
335 | * the queue.
336 | */
337 | protected void dispatchQueuedEvents() {
338 | // don't dispatch if we're already dispatching, that would allow reentrancy and out-of-order events. Instead, leave
339 | // the events to be dispatched after the in-progress dispatch is complete.
340 | if (isDispatching.get()) {
341 | return;
342 | }
343 |
344 | isDispatching.set(true);
345 | try {
346 | while (true) {
347 | EventWithHandler eventWithHandler = eventsToDispatch.get().poll();
348 | if (eventWithHandler == null) {
349 | break;
350 | }
351 |
352 | if (eventWithHandler.handler.isValid()) {
353 | dispatch(eventWithHandler.event, eventWithHandler.handler);
354 | }
355 | }
356 | } finally {
357 | isDispatching.set(false);
358 | }
359 | }
360 |
361 | /**
362 | * Dispatches {@code event} to the handler in {@code wrapper}. This method is an appropriate override point for
363 | * subclasses that wish to make event delivery asynchronous.
364 | *
365 | * @param event event to dispatch.
366 | * @param wrapper wrapper that will call the handler.
367 | */
368 | protected void dispatch(Object event, EventHandler wrapper) {
369 | try {
370 | wrapper.handleEvent(event);
371 | } catch (InvocationTargetException e) {
372 | throw new RuntimeException(
373 | "Could not dispatch event: " + event.getClass() + " to handler " + wrapper, e);
374 | }
375 | }
376 |
377 | /**
378 | * Retrieves a mutable set of the currently registered producers for {@code type}. If no producers are currently
379 | * registered for {@code type}, this method will return {@code null}.
380 | *
381 | * @param type type of producers to retrieve.
382 | * @return currently registered producer, or {@code null}.
383 | */
384 | EventProducer getProducerForEventType(Class> type) {
385 | return producersByType.get(type);
386 | }
387 |
388 | /**
389 | * Retrieves a mutable set of the currently registered handlers for {@code type}. If no handlers are currently
390 | * registered for {@code type}, this method may either return {@code null} or an empty set.
391 | *
392 | * @param type type of handlers to retrieve.
393 | * @return currently registered handlers, or {@code null}.
394 | */
395 | Set getHandlersForEventType(Class> type) {
396 | return handlersByType.get(type);
397 | }
398 |
399 | /**
400 | * Flattens a class's type hierarchy into a set of Class objects. The set will include all superclasses
401 | * (transitively), and all interfaces implemented by these superclasses.
402 | *
403 | * @param concreteClass class whose type hierarchy will be retrieved.
404 | * @return {@code clazz}'s complete type hierarchy, flattened and uniqued.
405 | */
406 | Set> flattenHierarchy(Class> concreteClass) {
407 | Set> classes = flattenHierarchyCache.get(concreteClass);
408 | if (classes == null) {
409 | classes = getClassesFor(concreteClass);
410 | flattenHierarchyCache.put(concreteClass, classes);
411 | }
412 |
413 | return classes;
414 | }
415 |
416 | private Set> getClassesFor(Class> concreteClass) {
417 | List> parents = new LinkedList>();
418 | Set> classes = new HashSet>();
419 |
420 | parents.add(concreteClass);
421 |
422 | while (!parents.isEmpty()) {
423 | Class> clazz = parents.remove(0);
424 | classes.add(clazz);
425 |
426 | Class> parent = clazz.getSuperclass();
427 | if (parent != null) {
428 | parents.add(parent);
429 | }
430 | }
431 | return classes;
432 | }
433 |
434 | private final Map, Set>> flattenHierarchyCache =
435 | new HashMap, Set>>();
436 |
437 | /** Simple struct representing an event and its handler. */
438 | static class EventWithHandler {
439 | final Object event;
440 | final EventHandler handler;
441 |
442 | public EventWithHandler(Object event, EventHandler handler) {
443 | this.event = event;
444 | this.handler = handler;
445 | }
446 | }
447 | }
448 |
--------------------------------------------------------------------------------
/src/com/squareup/otto/BusProvider.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.squareup.otto;
18 |
19 | import com.squareup.otto.Bus;
20 |
21 | /**
22 | * Maintains a singleton instance for obtaining the bus. Ideally this would be replaced with a more efficient means
23 | * such as through injection directly into interested classes.
24 | */
25 | public final class BusProvider {
26 | private static final Bus BUS = new Bus();
27 |
28 | public static Bus getInstance() {
29 | return BUS;
30 | }
31 |
32 | private BusProvider() {
33 | // No instances.
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/com/squareup/otto/DeadEvent.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2007 The Guava Authors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.squareup.otto;
18 |
19 | /**
20 | * Wraps an event that was posted, but which had no subscribers and thus could not be delivered.
21 | *
22 | *
Subscribing a DeadEvent handler is useful for debugging or logging, as it can detect misconfigurations in a
23 | * system's event distribution.
24 | *
25 | * @author Cliff Biffle
26 | */
27 | public class DeadEvent {
28 |
29 | public final Object source;
30 | public final Object event;
31 |
32 | /**
33 | * Creates a new DeadEvent.
34 | *
35 | * @param source object broadcasting the DeadEvent (generally the {@link Bus}).
36 | * @param event the event that could not be delivered.
37 | */
38 | public DeadEvent(Object source, Object event) {
39 | this.source = source;
40 | this.event = event;
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/com/squareup/otto/EventHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 Square, Inc.
3 | * Copyright (C) 2007 The Guava Authors
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package com.squareup.otto;
19 |
20 | import java.lang.reflect.InvocationTargetException;
21 | import java.lang.reflect.Method;
22 |
23 | /**
24 | * Wraps a single-argument 'handler' method on a specific object.
25 | *
26 | *
This class only verifies the suitability of the method and event type if something fails. Callers are expected t
27 | * verify their uses of this class.
28 | *
29 | *
Two EventHandlers are equivalent when they refer to the same method on the same object (not class). This
30 | * property is used to ensure that no handler method is registered more than once.
31 | *
32 | * @author Cliff Biffle
33 | */
34 | class EventHandler {
35 |
36 | /** Object sporting the handler method. */
37 | private final Object target;
38 | /** Handler method. */
39 | private final Method method;
40 | /** Object hash code. */
41 | private final int hashCode;
42 | /** Should this handler receive events? */
43 | private boolean valid = true;
44 |
45 | EventHandler(Object target, Method method) {
46 | if (target == null) {
47 | throw new NullPointerException("EventHandler target cannot be null.");
48 | }
49 | if (method == null) {
50 | throw new NullPointerException("EventHandler method cannot be null.");
51 | }
52 |
53 | this.target = target;
54 | this.method = method;
55 | method.setAccessible(true);
56 |
57 | // Compute hash code eagerly since we know it will be used frequently and we cannot estimate the runtime of the
58 | // target's hashCode call.
59 | final int prime = 31;
60 | hashCode = (prime + method.hashCode()) * prime + target.hashCode();
61 | }
62 |
63 | public boolean isValid() {
64 | return valid;
65 | }
66 |
67 | /**
68 | * If invalidated, will subsequently refuse to handle events.
69 | *
70 | * Should be called when the wrapped object is unregistered from the Bus.
71 | */
72 | public void invalidate() {
73 | valid = false;
74 | }
75 |
76 | /**
77 | * Invokes the wrapped handler method to handle {@code event}.
78 | *
79 | * @param event event to handle
80 | * @Throws java.lang.IllegalStateException if previously invalidated.
81 | * @throws java.lang.reflect.InvocationTargetException if the wrapped method throws any {@link Throwable} that is not
82 | * an {@link Error} ({@code Error}s are propagated as-is).
83 | */
84 | public void handleEvent(Object event) throws InvocationTargetException {
85 | if (!valid) {
86 | throw new IllegalStateException(toString() + " has been invalidated and can no longer handle events.");
87 | }
88 | try {
89 | method.invoke(target, new Object[] {event});
90 | } catch (IllegalAccessException e) {
91 | throw new AssertionError(e);
92 | } catch (InvocationTargetException e) {
93 | if (e.getCause() instanceof Error) {
94 | throw (Error) e.getCause();
95 | }
96 | throw e;
97 | }
98 | }
99 |
100 | @Override public String toString() {
101 | return "[EventHandler " + method + "]";
102 | }
103 |
104 | @Override public int hashCode() {
105 | return hashCode;
106 | }
107 |
108 | @Override public boolean equals(Object obj) {
109 | if (this == obj) {
110 | return true;
111 | }
112 |
113 | if (obj == null) {
114 | return false;
115 | }
116 |
117 | if (getClass() != obj.getClass()) {
118 | return false;
119 | }
120 |
121 | final EventHandler other = (EventHandler) obj;
122 |
123 | return method.equals(other.method) && target == other.target;
124 | }
125 |
126 | }
127 |
--------------------------------------------------------------------------------
/src/com/squareup/otto/EventProducer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.squareup.otto;
18 |
19 | import java.lang.reflect.InvocationTargetException;
20 | import java.lang.reflect.Method;
21 |
22 | /**
23 | * Wraps a 'producer' method on a specific object.
24 | *
25 | *
This class only verifies the suitability of the method and event type if something fails. Callers are expected
26 | * to verify their uses of this class.
27 | *
28 | * @author Jake Wharton
29 | */
30 | class EventProducer {
31 |
32 | /** Object sporting the producer method. */
33 | private final Object target;
34 | /** Producer method. */
35 | private final Method method;
36 | /** Object hash code. */
37 | private final int hashCode;
38 | /** Should this producer produce events? */
39 | private boolean valid = true;
40 |
41 | EventProducer(Object target, Method method) {
42 | if (target == null) {
43 | throw new NullPointerException("EventProducer target cannot be null.");
44 | }
45 | if (method == null) {
46 | throw new NullPointerException("EventProducer method cannot be null.");
47 | }
48 |
49 | this.target = target;
50 | this.method = method;
51 | method.setAccessible(true);
52 |
53 | // Compute hash code eagerly since we know it will be used frequently and we cannot estimate the runtime of the
54 | // target's hashCode call.
55 | final int prime = 31;
56 | hashCode = (prime + method.hashCode()) * prime + target.hashCode();
57 | }
58 |
59 | public boolean isValid() {
60 | return valid;
61 | }
62 |
63 | /**
64 | * If invalidated, will subsequently refuse to produce events.
65 | *
66 | * Should be called when the wrapped object is unregistered from the Bus.
67 | */
68 | public void invalidate() {
69 | valid = false;
70 | }
71 |
72 | /**
73 | * Invokes the wrapped producer method.
74 | *
75 | * @throws java.lang.IllegalStateException if previously invalidated.
76 | * @throws java.lang.reflect.InvocationTargetException if the wrapped method throws any {@link Throwable} that is not
77 | * an {@link Error} ({@code Error}s are propagated as-is).
78 | */
79 | public Object produceEvent() throws InvocationTargetException {
80 | if (!valid) {
81 | throw new IllegalStateException(toString() + " has been invalidated and can no longer produce events.");
82 | }
83 | try {
84 | return method.invoke(target);
85 | } catch (IllegalAccessException e) {
86 | throw new AssertionError(e);
87 | } catch (InvocationTargetException e) {
88 | if (e.getCause() instanceof Error) {
89 | throw (Error) e.getCause();
90 | }
91 | throw e;
92 | }
93 | }
94 |
95 | @Override public String toString() {
96 | return "[EventProducer " + method + "]";
97 | }
98 |
99 | @Override public int hashCode() {
100 | return hashCode;
101 | }
102 |
103 | @Override public boolean equals(Object obj) {
104 | if (this == obj) {
105 | return true;
106 | }
107 |
108 | if (obj == null) {
109 | return false;
110 | }
111 |
112 | if (getClass() != obj.getClass()) {
113 | return false;
114 | }
115 |
116 | final EventProducer other = (EventProducer) obj;
117 |
118 | return method.equals(other.method) && target == other.target;
119 | }
120 |
121 | }
122 |
--------------------------------------------------------------------------------
/src/com/squareup/otto/HandlerFinder.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.squareup.otto;
18 |
19 | import java.util.Map;
20 | import java.util.Set;
21 |
22 | /** Finds producer and subscriber methods. */
23 | interface HandlerFinder {
24 |
25 | Map, EventProducer> findAllProducers(Object listener);
26 |
27 | Map, Set> findAllSubscribers(Object listener);
28 |
29 |
30 | HandlerFinder ANNOTATED = new HandlerFinder() {
31 | @Override
32 | public Map, EventProducer> findAllProducers(Object listener) {
33 | return AnnotatedHandlerFinder.findAllProducers(listener);
34 | }
35 |
36 | @Override
37 | public Map, Set> findAllSubscribers(Object listener) {
38 | return AnnotatedHandlerFinder.findAllSubscribers(listener);
39 | }
40 | };
41 | }
42 |
--------------------------------------------------------------------------------
/src/com/squareup/otto/Produce.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.squareup.otto;
18 |
19 | import java.lang.annotation.ElementType;
20 | import java.lang.annotation.Retention;
21 | import java.lang.annotation.RetentionPolicy;
22 | import java.lang.annotation.Target;
23 |
24 | /**
25 | * Marks a method as an instance producer, as used by {@link AnnotatedHandlerFinder} and {@link Bus}.
26 | *
27 | * Otto infers the instance type from the annotated method's return type. Producer methods may return null when there is
28 | * no appropriate value to share. The calling {@link Bus} ignores such returns and posts nothing.
29 | *
30 | * @author Jake Wharton
31 | */
32 | @Retention(RetentionPolicy.RUNTIME)
33 | @Target(ElementType.METHOD)
34 | public @interface Produce {
35 | }
36 |
--------------------------------------------------------------------------------
/src/com/squareup/otto/Subscribe.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2007 The Guava Authors
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.squareup.otto;
18 |
19 | import java.lang.annotation.ElementType;
20 | import java.lang.annotation.Retention;
21 | import java.lang.annotation.RetentionPolicy;
22 | import java.lang.annotation.Target;
23 |
24 | /**
25 | * Marks a method as an event handler, as used by {@link AnnotatedHandlerFinder} and {@link Bus}.
26 | *
27 | *
The method's first (and only) parameter defines the event type.
28 | *
If this annotation is applied to methods with zero parameters or more than one parameter, the object containing
29 | * the method will not be able to register for event delivery from the {@link Bus}. Otto fails fast by throwing
30 | * runtime exceptions in these cases.
31 | *
32 | * @author Cliff Biffle
33 | */
34 | @Retention(RetentionPolicy.RUNTIME)
35 | @Target(ElementType.METHOD)
36 | public @interface Subscribe {
37 | }
38 |
--------------------------------------------------------------------------------
/src/com/squareup/otto/ThreadEnforcer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 Square, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.squareup.otto;
18 |
19 | import android.os.Looper;
20 |
21 | /**
22 | * Enforces a thread confinement policy for methods on a particular event bus.
23 | *
24 | * @author Jake Wharton
25 | */
26 | public interface ThreadEnforcer {
27 |
28 | /**
29 | * Enforce a valid thread for the given {@code bus}. Implementations may throw any runtime exception.
30 | *
31 | * @param bus Event bus instance on which an action is being performed.
32 | */
33 | void enforce(Bus bus);
34 |
35 |
36 | /** A {@link ThreadEnforcer} that does no verification or enforcement for any action. */
37 | ThreadEnforcer NONE = new ThreadEnforcer() {
38 | @Override public void enforce(Bus bus) {
39 | // Allow any thread.
40 | }
41 | };
42 |
43 | /** A {@link ThreadEnforcer} that confines {@link Bus} methods to the main thread. */
44 | ThreadEnforcer MAIN = new ThreadEnforcer() {
45 | @Override public void enforce(Bus bus) {
46 | if (Looper.myLooper() != Looper.getMainLooper()) {
47 | throw new IllegalStateException("Event bus " + bus + " accessed from non-main thread " + Looper.myLooper());
48 | }
49 | }
50 | };
51 |
52 | }
53 |
--------------------------------------------------------------------------------