diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a4c7838 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures +.externalNativeBuild diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..96cc43e --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..c7d1c5a --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..97626ba --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..fe72da5 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..fbb6828 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..f5e9010 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..7f68460 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..3543521 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..d5b0147 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,41 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 24 + buildToolsVersion "24.0.2" + defaultConfig { + applicationId "com.projects.mdb.mdbsocials" + minSdkVersion 14 + targetSdkVersion 24 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + exclude group: 'com.android.support', module: 'support-annotations' + }) + compile 'com.android.support:appcompat-v7:24.2.1' + compile 'com.android.support:design:24.2.1' + testCompile 'junit:junit:4.12' + compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha8' + compile 'com.android.support:cardview-v7:24.2.1' + compile 'com.android.support:recyclerview-v7:24.2.1' + compile 'com.google.firebase:firebase-core:9.6.1' + compile 'com.google.firebase:firebase-auth:9.6.1' + compile 'com.google.firebase:firebase-storage:9.6.1' + compile 'com.google.firebase:firebase-database:9.6.1' + compile 'com.github.bumptech.glide:glide:3.7.0' + compile 'com.android.support:palette-v7:24.2.1' +} + +apply plugin: 'com.google.gms.google-services' diff --git a/app/google-services.json b/app/google-services.json new file mode 100644 index 0000000..24f6b12 --- /dev/null +++ b/app/google-services.json @@ -0,0 +1,42 @@ +{ + "project_info": { + "project_number": "1082602066966", + "firebase_url": "https://mdbsocials-700a9.firebaseio.com", + "project_id": "mdbsocials-700a9", + "storage_bucket": "mdbsocials-700a9.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:1082602066966:android:e9cc6be388f1b08f", + "android_client_info": { + "package_name": "com.projects.mdb.mdbsocials" + } + }, + "oauth_client": [ + { + "client_id": "1082602066966-029arvecm878lbmc07aq41uefnov6p46.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyAzxt2y8QKqJ1_5GzDGeOsirnceXq8_C2s" + } + ], + "services": { + "analytics_service": { + "status": 1 + }, + "appinvite_service": { + "status": 1, + "other_platform_oauth_client": [] + }, + "ads_service": { + "status": 2 + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..8112cd7 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in C:\Users\Aayush\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/app/src/androidTest/java/com/projects/mdb/mdbsocials/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/projects/mdb/mdbsocials/ExampleInstrumentedTest.java new file mode 100644 index 0000000..1c0e3d1 --- /dev/null +++ b/app/src/androidTest/java/com/projects/mdb/mdbsocials/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.pokedex.mdb.mdbsocials; + +import android.content.Context; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumentation test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("com.pokedex.mdb.mdbsocials", appContext.getPackageName()); + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..b197872 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/projects/mdb/mdbsocials/InterestedActivity.java b/app/src/main/java/com/projects/mdb/mdbsocials/InterestedActivity.java new file mode 100644 index 0000000..f835794 --- /dev/null +++ b/app/src/main/java/com/projects/mdb/mdbsocials/InterestedActivity.java @@ -0,0 +1,22 @@ +package com.projects.mdb.mdbsocials; + +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.Log; + +public class InterestedActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_interested); + RecyclerView recyclegirl = (RecyclerView)findViewById(R.id.recyclegirl); + recyclegirl.setLayoutManager(new LinearLayoutManager(this)); + String[] people = getIntent().getStringArrayExtra("interestedPeople"); + if (people.length > 0) { + recyclegirl.setAdapter(new InterestedAdapter(getApplicationContext(), people)); + } + } +} diff --git a/app/src/main/java/com/projects/mdb/mdbsocials/InterestedAdapter.java b/app/src/main/java/com/projects/mdb/mdbsocials/InterestedAdapter.java new file mode 100644 index 0000000..80a1fe6 --- /dev/null +++ b/app/src/main/java/com/projects/mdb/mdbsocials/InterestedAdapter.java @@ -0,0 +1,57 @@ +package com.projects.mdb.mdbsocials; + +import android.content.Context; +import android.content.Intent; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import java.util.ArrayList; + +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; + +/** + * Created by Aayush on 10/12/2016. + */ + +public class InterestedAdapter extends RecyclerView.Adapter { + + private Context context; + private String[] emails; + + public InterestedAdapter(Context context, String[] emails) { + this.context = context; + this.emails = emails; + } + + + @Override + public CustomViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.interested_row_view, parent, false); + return new CustomViewHolder(view); + } + + + @Override + public void onBindViewHolder(CustomViewHolder holder, int position) { + holder.textView.setText(emails[position]); + } + + @Override + public int getItemCount() { + return emails.length; + } + + class CustomViewHolder extends RecyclerView.ViewHolder { + TextView textView; + + public CustomViewHolder(View view) { + super(view); + this.textView = (TextView) view.findViewById(R.id.interested_text_view); + } + } +} diff --git a/app/src/main/java/com/projects/mdb/mdbsocials/ListActivity.java b/app/src/main/java/com/projects/mdb/mdbsocials/ListActivity.java new file mode 100644 index 0000000..947e1c4 --- /dev/null +++ b/app/src/main/java/com/projects/mdb/mdbsocials/ListActivity.java @@ -0,0 +1,51 @@ +package com.projects.mdb.mdbsocials; + +import android.content.Intent; +import android.support.design.widget.FloatingActionButton; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.widget.Toast; + +public class ListActivity extends AppCompatActivity { + + private Socialist s; + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_list); + RecyclerView recycleman = (RecyclerView)findViewById(R.id.recycleman); + recycleman.setLayoutManager(new LinearLayoutManager(this)); + s = new Socialist(); + SocialsAdapter adapter = new SocialsAdapter(getApplicationContext(), s.getSocialist()); + recycleman.setAdapter(adapter); + + FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); + fab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent intent = new Intent(getApplicationContext(), NewSocialActivity.class); + + startActivity(intent); + } + }); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.menu_main, menu); + return super.onCreateOptionsMenu(menu); + } + + public void signout(MenuItem item) { + Intent intent = new Intent(getApplicationContext(), LoginActivity.class); + LoginActivity.getmAuth().signOut(); + startActivity(intent); + } +} diff --git a/app/src/main/java/com/projects/mdb/mdbsocials/LoginActivity.java b/app/src/main/java/com/projects/mdb/mdbsocials/LoginActivity.java new file mode 100644 index 0000000..2ad11c7 --- /dev/null +++ b/app/src/main/java/com/projects/mdb/mdbsocials/LoginActivity.java @@ -0,0 +1,460 @@ +package com.projects.mdb.mdbsocials; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.annotation.TargetApi; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.support.annotation.NonNull; +import android.support.design.widget.Snackbar; +import android.support.v7.app.AppCompatActivity; +import android.app.LoaderManager.LoaderCallbacks; + +import android.content.CursorLoader; +import android.content.Loader; +import android.database.Cursor; +import android.net.Uri; +import android.os.AsyncTask; + +import android.os.Build; +import android.os.Bundle; +import android.provider.ContactsContract; +import android.text.TextUtils; +import android.util.Log; +import android.view.KeyEvent; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.inputmethod.EditorInfo; +import android.widget.ArrayAdapter; +import android.widget.AutoCompleteTextView; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + +import com.google.android.gms.tasks.OnCompleteListener; +import com.google.android.gms.tasks.Task; +import com.google.firebase.auth.AuthResult; +import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.auth.FirebaseUser; +import com.google.firebase.auth.UserProfileChangeRequest; +import com.google.firebase.database.DataSnapshot; +import com.google.firebase.database.DatabaseError; +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.FirebaseDatabase; +import com.google.firebase.database.ValueEventListener; + +import java.util.ArrayList; +import java.util.List; + +import static android.Manifest.permission.READ_CONTACTS; + +/** + * A login screen that offers login via email/password. + */ +public class LoginActivity extends AppCompatActivity implements LoaderCallbacks { + + /** + * Id to identity READ_CONTACTS permission request. + */ + private static final int REQUEST_READ_CONTACTS = 0; + + + /** + * Keep track of the login task to ensure we can cancel it if requested. + */ + private UserLoginTask mAuthTask = null; + + // UI references. + private AutoCompleteTextView mEmailView; + private EditText mPasswordView; + private View mProgressView; + private View mLoginFormView; + private static FirebaseAuth mAuth; + private FirebaseAuth.AuthStateListener mAuthListener; + private DatabaseReference usersRef; + private static String name; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_login); + // Set up the login form. + mEmailView = (AutoCompleteTextView) findViewById(R.id.email); + populateAutoComplete(); + + mAuth = FirebaseAuth.getInstance(); + mPasswordView = (EditText) findViewById(R.id.password); + mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) { + if (id == R.id.login || id == EditorInfo.IME_NULL) { + loogin(); + return true; + } + return false; + } + }); + + Button mEmailSignInButton = (Button) findViewById(R.id.email_sign_in_button); + mEmailSignInButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + loogin(); + } + }); + + Button signUpButton = (Button) findViewById(R.id.sign_up_button); + signUpButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + attemptSignUp(); + } + }); + + mLoginFormView = findViewById(R.id.login_form); + mProgressView = findViewById(R.id.login_progress); + + usersRef = FirebaseDatabase.getInstance().getReference().child("user"); + + mAuthListener = new FirebaseAuth.AuthStateListener() { + @Override + public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) { + FirebaseUser user = firebaseAuth.getCurrentUser(); + if (user != null) { + // User is signed in + Log.d("ye", "onAuthStateChanged:signed_in:" + user.getUid()); + /*usersRef.child(user.getUid()).addListenerForSingleValueEvent(new ValueEventListener() { + @Override + public void onDataChange(DataSnapshot dataSnapshot) { + name = dataSnapshot.getValue().toString(); + Log.d("NOOO", name); + } + + @Override + public void onCancelled(DatabaseError databaseError) { + } + });*/ + + } else { + // User is signed out + Log.d("ye", "onAuthStateChanged:signed_out"); + } + // ... + } + }; + } + + public static FirebaseAuth getmAuth() { + return mAuth; + } + + private void populateAutoComplete() { + if (!mayRequestContacts()) { + return; + } + + getLoaderManager().initLoader(0, null, this); + } + + private boolean mayRequestContacts() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + return true; + } + if (checkSelfPermission(READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) { + return true; + } + if (shouldShowRequestPermissionRationale(READ_CONTACTS)) { + Snackbar.make(mEmailView, R.string.permission_rationale, Snackbar.LENGTH_INDEFINITE) + .setAction(android.R.string.ok, new View.OnClickListener() { + @Override + @TargetApi(Build.VERSION_CODES.M) + public void onClick(View v) { + requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS); + } + }); + } else { + requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS); + } + return false; + } + + /** + * Callback received when a permissions request has been completed. + */ + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, + @NonNull int[] grantResults) { + if (requestCode == REQUEST_READ_CONTACTS) { + if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + populateAutoComplete(); + } + } + } + + + private void attemptSignUp() { + String email = mEmailView.getText().toString(); + String password = mPasswordView.getText().toString(); + + mAuth.createUserWithEmailAndPassword(email, password) + .addOnCompleteListener(this, new OnCompleteListener() { + @Override + public void onComplete(@NonNull Task task) { + Log.d("ye", "createUserWithEmail:onComplete:" + task.isSuccessful()); + + // If sign in fails, display a message to the user. If sign in succeeds + // the auth state listener will be notified and logic to handle the + // signed in user can be handled in the listener. + if (!task.isSuccessful()) { + Toast.makeText(LoginActivity.this, "sign up failed", + Toast.LENGTH_SHORT).show(); + } + else { + Intent intent = new Intent(getApplicationContext(), ListActivity.class); + startActivity(intent); + } + // ... + } + }); + } + + private void loogin() { + String email = mEmailView.getText().toString(); + String password = mPasswordView.getText().toString(); + mAuth.signInWithEmailAndPassword(email, password) + .addOnCompleteListener(this, new OnCompleteListener() { + @Override + public void onComplete(@NonNull Task task) { + Log.d("ye", "signInWithEmail:onComplete:" + task.isSuccessful()); + + // If sign in fails, display a message to the user. If sign in succeeds + // the auth state listener will be notified and logic to handle the + // signed in user can be handled in the listener. + if (!task.isSuccessful()) { + Log.w("ye", "signInWithEmail:failed", task.getException()); + Toast.makeText(LoginActivity.this, "Sign in failed", + Toast.LENGTH_SHORT).show(); + } + + else { + Intent intent = new Intent(getApplicationContext(), ListActivity.class); + startActivity(intent); + } + } + }); + } + + public static String getName() {return name;} + + /** + * Attempts to sign in or register the account specified by the login form. + * If there are form errors (invalid email, missing fields, etc.), the + * errors are presented and no actual login attempt is made. + */ + private void attemptLogin() { + if (mAuthTask != null) { + return; + } + + // Reset errors. + mEmailView.setError(null); + mPasswordView.setError(null); + + // Store values at the time of the login attempt. + String email = mEmailView.getText().toString(); + String password = mPasswordView.getText().toString(); + + boolean cancel = false; + View focusView = null; + + // Check for a valid password, if the user entered one. + if (!TextUtils.isEmpty(password) && !isPasswordValid(password)) { + mPasswordView.setError(getString(R.string.error_invalid_password)); + focusView = mPasswordView; + cancel = true; + } + + // Check for a valid email address. + if (TextUtils.isEmpty(email)) { + mEmailView.setError(getString(R.string.error_field_required)); + focusView = mEmailView; + cancel = true; + } else if (!isEmailValid(email)) { + mEmailView.setError(getString(R.string.error_invalid_email)); + focusView = mEmailView; + cancel = true; + } + + if (cancel) { + // There was an error; don't attempt login and focus the first + // form field with an error. + focusView.requestFocus(); + } else { + // Show a progress spinner, and kick off a background task to + // perform the user login attempt. + showProgress(true); + mAuthTask = new UserLoginTask(email, password); + mAuthTask.execute((Void) null); + } + } + + private boolean isEmailValid(String email) { + //TODO: Replace this with your own logic + return email.contains("@"); + } + + private boolean isPasswordValid(String password) { + //TODO: Replace this with your own logic + return password.length() > 4; + } + + /** + * Shows the progress UI and hides the login form. + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) + private void showProgress(final boolean show) { + // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow + // for very easy animations. If available, use these APIs to fade-in + // the progress spinner. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) { + int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime); + + mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); + mLoginFormView.animate().setDuration(shortAnimTime).alpha( + show ? 0 : 1).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); + } + }); + + mProgressView.setVisibility(show ? View.VISIBLE : View.GONE); + mProgressView.animate().setDuration(shortAnimTime).alpha( + show ? 1 : 0).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mProgressView.setVisibility(show ? View.VISIBLE : View.GONE); + } + }); + } else { + // The ViewPropertyAnimator APIs are not available, so simply show + // and hide the relevant UI components. + mProgressView.setVisibility(show ? View.VISIBLE : View.GONE); + mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); + } + } + + @Override + public Loader onCreateLoader(int i, Bundle bundle) { + return new CursorLoader(this, + // Retrieve data rows for the device user's 'profile' contact. + Uri.withAppendedPath(ContactsContract.Profile.CONTENT_URI, + ContactsContract.Contacts.Data.CONTENT_DIRECTORY), ProfileQuery.PROJECTION, + + // Select only email addresses. + ContactsContract.Contacts.Data.MIMETYPE + + " = ?", new String[]{ContactsContract.CommonDataKinds.Email + .CONTENT_ITEM_TYPE}, + + // Show primary email addresses first. Note that there won't be + // a primary email address if the user hasn't specified one. + ContactsContract.Contacts.Data.IS_PRIMARY + " DESC"); + } + + @Override + public void onLoadFinished(Loader cursorLoader, Cursor cursor) { + List emails = new ArrayList<>(); + cursor.moveToFirst(); + while (!cursor.isAfterLast()) { + emails.add(cursor.getString(ProfileQuery.ADDRESS)); + cursor.moveToNext(); + } + + addEmailsToAutoComplete(emails); + } + + @Override + public void onLoaderReset(Loader cursorLoader) { + + } + + private void addEmailsToAutoComplete(List emailAddressCollection) { + //Create adapter to tell the AutoCompleteTextView what to show in its dropdown list. + ArrayAdapter adapter = + new ArrayAdapter<>(LoginActivity.this, + android.R.layout.simple_dropdown_item_1line, emailAddressCollection); + + mEmailView.setAdapter(adapter); + } + + + private interface ProfileQuery { + String[] PROJECTION = { + ContactsContract.CommonDataKinds.Email.ADDRESS, + ContactsContract.CommonDataKinds.Email.IS_PRIMARY, + }; + + int ADDRESS = 0; + int IS_PRIMARY = 1; + } + + /** + * Represents an asynchronous login/registration task used to authenticate + * the user. + */ + public class UserLoginTask extends AsyncTask { + + private final String mEmail; + private final String mPassword; + + UserLoginTask(String email, String password) { + mEmail = email; + mPassword = password; + } + + @Override + protected Boolean doInBackground(Void... params) { + // TODO: attempt authentication against a network service. + + try { + // Simulate network access. + Thread.sleep(2000); + } catch (InterruptedException e) { + return false; + } + + /*for (String credential : DUMMY_CREDENTIALS) { + String[] pieces = credential.split(":"); + if (pieces[0].equals(mEmail)) { + // Account exists, return true if the password matches. + return pieces[1].equals(mPassword); + } + }*/ + + // TODO: register the new account here. + return true; + } + + @Override + protected void onPostExecute(final Boolean success) { + mAuthTask = null; + showProgress(false); + + if (success) { + Intent intent = new Intent(getApplicationContext(), ListActivity.class); + startActivity(intent); + } else { + mPasswordView.setError(getString(R.string.error_incorrect_password)); + mPasswordView.requestFocus(); + } + } + + @Override + protected void onCancelled() { + mAuthTask = null; + showProgress(false); + } + } +} + diff --git a/app/src/main/java/com/projects/mdb/mdbsocials/NewSocialActivity.java b/app/src/main/java/com/projects/mdb/mdbsocials/NewSocialActivity.java new file mode 100644 index 0000000..3c12c80 --- /dev/null +++ b/app/src/main/java/com/projects/mdb/mdbsocials/NewSocialActivity.java @@ -0,0 +1,89 @@ +package com.projects.mdb.mdbsocials; + +import android.content.Intent; +import android.net.Uri; +import android.support.annotation.NonNull; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.Toast; + +import com.google.android.gms.tasks.OnFailureListener; +import com.google.android.gms.tasks.OnSuccessListener; +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.FirebaseDatabase; +import com.google.firebase.storage.FirebaseStorage; +import com.google.firebase.storage.StorageReference; +import com.google.firebase.storage.UploadTask; + +import java.util.ArrayList; + +public class NewSocialActivity extends AppCompatActivity { + + private Uri file; + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_new_social); + Button submitButton = (Button)findViewById(R.id.submitButton); + Button button2 = (Button)findViewById(R.id.button2); + final EditText nameEditText = (EditText)findViewById(R.id.nameEditText); + final EditText dateEditText = (EditText)findViewById(R.id.dateEditText); + final EditText descriptionEditText = (EditText)findViewById(R.id.descriptionEditText); + final EditText hostEditText = (EditText)findViewById(R.id.hostEditText); + button2.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.setType("image/*"); + startActivityForResult(intent, 1); + } + }); + submitButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + DatabaseReference ref = FirebaseDatabase.getInstance().getReference(); + String key = ref.child("socials").push().getKey(); + StorageReference storageRef = FirebaseStorage.getInstance().getReferenceFromUrl("gs://mdbsocials-700a9.appspot.com"); + StorageReference riversRef = storageRef.child(key + ".png"); + riversRef.putFile(file).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception exception) { + // Handle unsuccessful uploads + } + }).addOnSuccessListener(new OnSuccessListener() { + @Override + public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { + // taskSnapshot.getMetadata() contains file metadata such as size, content-type, and download URL. + Uri downloadUrl = taskSnapshot.getDownloadUrl(); + } + }); + String name = nameEditText.getText().toString(); + String date = dateEditText.getText().toString(); + String description = descriptionEditText.getText().toString(); + String creator = hostEditText.getText().toString(); + Intent intent = new Intent(getApplicationContext(), ListActivity.class); + ref.child("socials").child(key).child("name").setValue(name); + ref.child("socials").child(key).child("date").setValue(date); + ref.child("socials").child(key).child("description").setValue(description); + ref.child("socials").child(key).child("creator").setValue(creator); + ref.child("socials").child(key).child("interested").setValue(0); + ref.child("socials").child(key).child("interestedPeople").setValue(new ArrayList()); + startActivity(intent); + } + }); + + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode == RESULT_OK) { + if (requestCode == 1) { + file = data.getData(); + } + } + super.onActivityResult(requestCode, resultCode, data); + } +} diff --git a/app/src/main/java/com/projects/mdb/mdbsocials/ScrollingActivity.java b/app/src/main/java/com/projects/mdb/mdbsocials/ScrollingActivity.java new file mode 100644 index 0000000..ab30d85 --- /dev/null +++ b/app/src/main/java/com/projects/mdb/mdbsocials/ScrollingActivity.java @@ -0,0 +1,165 @@ +package com.projects.mdb.mdbsocials; + +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.design.widget.CollapsingToolbarLayout; +import android.support.design.widget.FloatingActionButton; +import android.support.design.widget.Snackbar; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.graphics.Palette; +import android.support.v7.widget.Toolbar; +import android.util.Log; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import com.bumptech.glide.Glide; +import com.google.android.gms.tasks.OnFailureListener; +import com.google.android.gms.tasks.OnSuccessListener; +import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.database.DataSnapshot; +import com.google.firebase.database.DatabaseError; +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.FirebaseDatabase; +import com.google.firebase.database.GenericTypeIndicator; +import com.google.firebase.database.ValueEventListener; +import com.google.firebase.storage.FirebaseStorage; + +import java.util.ArrayList; + +public class ScrollingActivity extends AppCompatActivity { + private Socialist.Social s; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_scrolling); + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + final CollapsingToolbarLayout collapsingToolbarLayout = (CollapsingToolbarLayout)findViewById(R.id.toolbar_layout); + final TextView textView = (TextView)findViewById(R.id.maintext); + final String id = getIntent().getStringExtra("id"); + DatabaseReference ref = FirebaseDatabase.getInstance().getReference("/socials/" + id); + ref.addListenerForSingleValueEvent(new ValueEventListener() { + @Override + public void onDataChange(DataSnapshot dataSnapshot) { + if (dataSnapshot.child("interestedPeople").getValue() == null) { + CollapsingToolbarLayout collapsingToolbarLayout = (CollapsingToolbarLayout)findViewById(R.id.toolbar_layout); + collapsingToolbarLayout.setTitle(dataSnapshot.child("name").getValue(String.class)); + FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); + fab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Toast.makeText(ScrollingActivity.this, "sorry no one likes you", Toast.LENGTH_SHORT).show(); + } + }); + } + else { + CollapsingToolbarLayout collapsingToolbarLayout = (CollapsingToolbarLayout)findViewById(R.id.toolbar_layout); + collapsingToolbarLayout.setTitle(dataSnapshot.child("name").getValue(String.class)); + final ArrayList jesus = dataSnapshot.child("interestedPeople").getValue(new GenericTypeIndicator>() {}); + FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); + fab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Intent intent = new Intent(getApplicationContext(), InterestedActivity.class); + String[] arr = new String[jesus.size()]; + for (int i = 0; i < jesus.size(); ++i) { + arr[i] = jesus.get(i); + } + intent.putExtra("interestedPeople", arr); + startActivity(intent); + } + }); + } + class DownloadFilesTask extends AsyncTask { + protected Bitmap doInBackground(String... strings) { + try {return Glide. + with(getApplicationContext()). + load(strings[0]). + asBitmap(). + into(100, 100). // Width and height + get();} + catch (Exception e) {return null;} + } + + protected void onProgressUpdate(Void... progress) {} + + protected void onPostExecute(Bitmap result) { + Palette.PaletteAsyncListener paletteListener = new Palette.PaletteAsyncListener() { + public void onGenerated(Palette palette) { + int defaulto = 0x000000; + collapsingToolbarLayout.setBackgroundColor(palette.getDominantColor(defaulto)); + } + }; + if (result != null && !result.isRecycled()) { + Palette.from(result).generate(paletteListener); + } + ((ImageView)findViewById(R.id.imageView2)).setImageBitmap(result); + } + } + + FirebaseStorage.getInstance().getReferenceFromUrl("gs://mdbsocials-700a9.appspot.com").child(id + ".png").getDownloadUrl().addOnSuccessListener(new OnSuccessListener() { + @Override + public void onSuccess(Uri uri) { + Log.d("ye", uri.toString()); + new DownloadFilesTask().execute(uri.toString()); + //Glide.with(context).load(uri.toString()).thumbnail(0.5f).crossFade().diskCacheStrategy(DiskCacheStrategy.ALL).into(holder.imageView); + } + }).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception exception) { + Log.d("sad", exception.toString()); + } + }); + textView.setText(dataSnapshot.child("description").getValue(String.class)); + } + + + + @Override + public void onCancelled(DatabaseError databaseError) { + + } + }); + FloatingActionButton fab2 = (FloatingActionButton) findViewById(R.id.fab2); + fab2.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + final String email = FirebaseAuth.getInstance().getCurrentUser().getEmail(); + final DatabaseReference ref = FirebaseDatabase.getInstance().getReference("/socials/" + getIntent().getStringExtra("id")); + ref.addListenerForSingleValueEvent(new ValueEventListener() { + @Override + public void onDataChange(DataSnapshot dataSnapshot) { + ref.child("interestedPeople").child("" + dataSnapshot.child("interested").getValue(Integer.class)).setValue(email); + ref.child("interested").setValue((int)(dataSnapshot.child("interested").getValue(Integer.class) + 1)); + } + + @Override + public void onCancelled(DatabaseError databaseError) { + + } + }); + } + }); + + + } +/* + @Override + public void onResume() { + super.onResume(); + Socialist soc = new Socialist(); + s = soc.getSocialByID(s.getID()); + Log.d("yo", s.getID()); + collapsingToolbarLayout.setTitle(s.getName()); + textView.setText(s.getDescription() + '\n' + s.getDate()); + } + */ + +} diff --git a/app/src/main/java/com/projects/mdb/mdbsocials/Socialist.java b/app/src/main/java/com/projects/mdb/mdbsocials/Socialist.java new file mode 100644 index 0000000..3be7f1e --- /dev/null +++ b/app/src/main/java/com/projects/mdb/mdbsocials/Socialist.java @@ -0,0 +1,140 @@ +package com.projects.mdb.mdbsocials; + +import android.graphics.Bitmap; +import android.util.Log; +import android.widget.Toast; + +import com.google.firebase.database.ChildEventListener; +import com.google.firebase.database.DataSnapshot; +import com.google.firebase.database.DatabaseError; +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.FirebaseDatabase; +import com.google.firebase.database.GenericTypeIndicator; +import com.google.firebase.database.ValueEventListener; + +import java.util.ArrayList; + +/** + * Created by Aayush on 10/4/2016. + */ + +//long live the revolution +public class Socialist { + private DatabaseReference ref; + private ArrayList socialist; + + public Socialist() { + socialist = new ArrayList(); + ref = FirebaseDatabase.getInstance().getReference("/socials"); + ref.orderByChild("date").addChildEventListener(new ChildEventListener() { + @Override + public void onChildAdded(DataSnapshot dataSnapshot, String s) { + Log.d(dataSnapshot.getKey(), dataSnapshot.getValue().toString()); + if (dataSnapshot.child("interested").getValue() == null) { + socialist.add(new Social(dataSnapshot.getKey(), + dataSnapshot.child("creator").getValue(String.class), + dataSnapshot.child("date").getValue(String.class), + dataSnapshot.child("description").getValue(String.class), + dataSnapshot.child("name").getValue(String.class))); + } + else { + socialist.add(new Social(dataSnapshot.getKey(), + dataSnapshot.child("creator").getValue(String.class), + dataSnapshot.child("date").getValue(String.class), + dataSnapshot.child("description").getValue(String.class), + dataSnapshot.child("interested").getValue(Integer.class), + dataSnapshot.child("name").getValue(String.class), + dataSnapshot.child("interestedPeople").getValue(new GenericTypeIndicator>() { + }))); + } + } + + @Override + public void onChildChanged(DataSnapshot dataSnapshot, String s) { + + } + + @Override + public void onChildRemoved(DataSnapshot dataSnapshot) { + + } + + @Override + public void onChildMoved(DataSnapshot dataSnapshot, String s) { + + } + + @Override + public void onCancelled(DatabaseError databaseError) { + + } + }); + + } + + public ArrayList getSocialist() { + return socialist; + } + + public class Social{ + private String creator; + private String date; + private String description; + private int interested = 0; + private String id; + private ArrayList interestedPeople = new ArrayList(); + //private Bitmap image; + private String name; + private String photoURL; + + public Social() {} + + public Social(String id, String creator, String date, String description, String name) { + this.name = name; + this.description = description; + this.creator = creator; + this.date = date; + this.id = id; + } + + public Social(String id, String creator, String date, String description, int interested, String name, ArrayList interestedPeople) { + this.name = name; + this.interested = interested; + this.description = description; + this.creator = creator; + this.date = date; + this.id = id; + this.interestedPeople = interestedPeople; + } + + public String getCreator() {return creator;} + public void setCreator(String creator) {this.creator = creator;} + public String getDate() {return date;} + public void setDate(String date) {this.date = date;} + public String getDescription() {return description;} + public void setDescription(String desc) {this.description = desc;} + public int getInterested() {return interested;} + public void setInterested(int n) {this.interested = n;} + public String[] getInterestedPeople() { + String[] s = new String[interestedPeople.size()]; + for(int i = 0; i < s.length; i++) { + s[i] = interestedPeople.get(i); + } + return s; + } + public void setInterestedPeople(ArrayList s) { + interestedPeople = new ArrayList(); + if (s != null) { + for(int i = 0; i < s.size(); ++i) { + interestedPeople.add(s.get(i)); + } + } + } + public String getName() {return name;} + public void setName(String name) {this.name = name;} + public String getID() {return id;} + public void setID(String id) {this.id = id;} + + public void setPhotoURL(String url) {photoURL = url;} + } +} diff --git a/app/src/main/java/com/projects/mdb/mdbsocials/SocialsAdapter.java b/app/src/main/java/com/projects/mdb/mdbsocials/SocialsAdapter.java new file mode 100644 index 0000000..2931f08 --- /dev/null +++ b/app/src/main/java/com/projects/mdb/mdbsocials/SocialsAdapter.java @@ -0,0 +1,166 @@ +package com.projects.mdb.mdbsocials; + +/** + * Created by Aayush on 10/3/2016. + */ + +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.AsyncTask; +import android.support.annotation.NonNull; +import android.support.v7.graphics.Palette; +import android.support.v7.widget.CardView; +import android.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.google.android.gms.tasks.OnFailureListener; +import com.google.android.gms.tasks.OnSuccessListener; +import com.google.firebase.storage.FirebaseStorage; + +import java.util.ArrayList; + +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; + + +/** + * Created by Aayush on 9/28/2016. + */ + +public class SocialsAdapter extends RecyclerView.Adapter { + + private Context context; + private ArrayList socialist; + + public SocialsAdapter(Context context, ArrayList socialist) { + this.context = context; + this.socialist = socialist; + } + + + @Override + public CustomViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.socials_row_view, parent, false); + return new CustomViewHolder(view); + } + + + @Override + public void onBindViewHolder(final CustomViewHolder holder, int position) { + Socialist.Social social = socialist.get(position); + holder.eventNameTextView.setText(social.getName()); + holder.numberTextView.setText("" + social.getInterested() + " people are interested"); + if (social.getInterested() == 1) {holder.numberTextView.setText("1 person is interested");} + holder.creatorTextView.setText("Created by " + social.getCreator() + " on " + social.getDate()); + + class DownloadFilesTask extends AsyncTask { + protected Bitmap doInBackground(String... strings) { + try {return Glide. + with(context). + load(strings[0]). + asBitmap(). + into(100, 100). // Width and height + get();} + catch (Exception e) {return null;} + } + + protected void onProgressUpdate(Void... progress) {} + + protected void onPostExecute(Bitmap result) { + Palette.PaletteAsyncListener paletteListener = new Palette.PaletteAsyncListener() { + public void onGenerated(Palette palette) { + int defaulto = 0x000000; + holder.cardView.setCardBackgroundColor(palette.getDominantColor(defaulto)); + } + }; + if (result != null && !result.isRecycled()) { + Palette.from(result).generate(paletteListener); + } + holder.imageView.setImageBitmap(result); + } + } + + FirebaseStorage.getInstance().getReferenceFromUrl("gs://mdbsocials-700a9.appspot.com").child(social.getID() + ".png").getDownloadUrl().addOnSuccessListener(new OnSuccessListener() { + @Override + public void onSuccess(Uri uri) { + Log.d("ye", uri.toString()); + new DownloadFilesTask().execute(uri.toString()); + //Glide.with(context).load(uri.toString()).thumbnail(0.5f).crossFade().diskCacheStrategy(DiskCacheStrategy.ALL).into(holder.imageView); + } + }).addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception exception) { + Log.d("sad", exception.toString()); + } + }); + } + + + @Override + public int getItemCount() { + return socialist.size(); + } + + class CustomViewHolder extends RecyclerView.ViewHolder { + TextView eventNameTextView; + ImageView imageView; + TextView numberTextView; + TextView creatorTextView; + CardView cardView; + + public CustomViewHolder (View view) { + super(view); + this.eventNameTextView = (TextView) view.findViewById(R.id.eventNameTextView); + this.imageView = (ImageView) view.findViewById(R.id.imageView); + this.numberTextView = (TextView) view.findViewById(R.id.numberTextView); + this.creatorTextView = (TextView) view.findViewById(R.id.creatorTextView); + this.cardView = (CardView) view.findViewById(R.id.card); + + view.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + /*Get adapter position is getting the number of the row that was clicked, + starting at 0 + */ + Toast.makeText(context, "yooooooo", Toast.LENGTH_SHORT).show(); + Socialist.Social s = socialist.get(getAdapterPosition()); + Intent intent = new Intent(context, ScrollingActivity.class); + String[] info = {(String)creatorTextView.getText(), + s.getDescription(), + (String)numberTextView.getText(), + (String)eventNameTextView.getText()}; + intent.putExtra("id", s.getID()); + intent.addFlags(FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } + }); + } + } + + private class DownloadFilesTask extends AsyncTask { + protected Bitmap doInBackground(String... strings) { + try {return Glide. + with(context). + load(strings[0]). + asBitmap(). + into(100, 100). // Width and height + get();} + catch (Exception e) {return null;} + } + + protected void onProgressUpdate(Void... progress) {} + + protected void onPostExecute(Bitmap result) { + + } + } +} diff --git a/app/src/main/java/com/projects/mdb/mdbsocials/SplashActivity.java b/app/src/main/java/com/projects/mdb/mdbsocials/SplashActivity.java new file mode 100644 index 0000000..b28b63a --- /dev/null +++ b/app/src/main/java/com/projects/mdb/mdbsocials/SplashActivity.java @@ -0,0 +1,21 @@ +package com.projects.mdb.mdbsocials; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; + +/** + * Created by Aayush on 10/15/2016. + */ + +public class SplashActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Intent intent = new Intent(this, LoginActivity.class); + startActivity(intent); + finish(); + } +} diff --git a/app/src/main/res/drawable/background_splash.xml b/app/src/main/res/drawable/background_splash.xml new file mode 100644 index 0000000..cc5d298 --- /dev/null +++ b/app/src/main/res/drawable/background_splash.xml @@ -0,0 +1,13 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_add_black_24dp.xml b/app/src/main/res/drawable/ic_add_black_24dp.xml new file mode 100644 index 0000000..b9b8eca --- /dev/null +++ b/app/src/main/res/drawable/ic_add_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_people_black_24dp.xml b/app/src/main/res/drawable/ic_people_black_24dp.xml new file mode 100644 index 0000000..4cfd869 --- /dev/null +++ b/app/src/main/res/drawable/ic_people_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_star_black_24dp.xml b/app/src/main/res/drawable/ic_star_black_24dp.xml new file mode 100644 index 0000000..fa1c999 --- /dev/null +++ b/app/src/main/res/drawable/ic_star_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_interested.xml b/app/src/main/res/layout/activity_interested.xml new file mode 100644 index 0000000..38fd67c --- /dev/null +++ b/app/src/main/res/layout/activity_interested.xml @@ -0,0 +1,24 @@ + + + + + diff --git a/app/src/main/res/layout/activity_list.xml b/app/src/main/res/layout/activity_list.xml new file mode 100644 index 0000000..7b71fa8 --- /dev/null +++ b/app/src/main/res/layout/activity_list.xml @@ -0,0 +1,39 @@ + + + + + + + + diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml new file mode 100644 index 0000000..dbe4049 --- /dev/null +++ b/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + +