diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..190b7ec Binary files /dev/null and b/.DS_Store differ diff --git a/README.md b/README.md index e69de29..16eca09 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,39 @@ +# Ponila (پونیلا) + +Android app for content discovery and recommendation. Ponila helps users find, save, and share interesting content based on their interests. + +## Features + +- **Content Recommendations**: Personalized content suggestions based on user interests +- **Collections**: Organize and manage content collections ("دفتر") +- **Social Features**: Follow users, like posts, comment, and share +- **Search**: Search across posts, collections, and members +- **Offline Support**: Browse cached content offline +- **Push Notifications**: Real-time notifications for activities + +## Tech Stack + +- **Language**: Java +- **Database**: DBFlow (SQLite) +- **Networking**: Retrofit + OkHttp +- **Image Loading**: Picasso +- **Notifications**: OneSignal +- **Analytics**: Google Analytics, Crashlytics +- **Build**: Gradle + +## Setup + +1. Clone the repository +2. Open in Android Studio +3. Configure `google-services.json` and `fabric.properties` +4. Set up signing configuration for release builds +5. Build and run + +## Project Structure + +``` +src/main/java/com/shaya/poinila/ +├── android/presentation/ # UI layer (Activities, Fragments, ViewHolders) +├── data/ # Data layer (Models, Network, Database) +└── manager/ # Business logic (DataRepository, DBFacade) +``` diff --git a/build.gradle b/build.gradle new file mode 100755 index 0000000..e85f975 --- /dev/null +++ b/build.gradle @@ -0,0 +1,136 @@ +buildscript { + repositories { + maven { url 'https://maven.fabric.io/public' } + } + + dependencies { + classpath 'io.fabric.tools:gradle:1.+' + } +} +apply plugin: 'com.android.application' +apply plugin: 'io.fabric' + +repositories { + maven { url 'https://maven.fabric.io/public' } +} + +apply plugin: 'com.neenbedankt.android-apt' + +android { + compileSdkVersion project.ext.global_compileSdkVersion + buildToolsVersion project.ext.global_buildToolsVersion + defaultConfig { + applicationId "com.shaya.poinila" + minSdkVersion project.ext.global_minSdkVersion + targetSdkVersion project.ext.global_targetSdkVersion +// maxSdkVersion project.ext.global_maxSdkVersion + versionCode project.ext.global_versionCode + versionName project.ext.global_versionName + setProperty("archivesBaseName", "Ponila-$versionName".replace("-release", "")) + + manifestPlaceholders = [manifestApplicationId: "${applicationId}", + onesignal_app_id: "ec125f7c-f3d9-48c9-ba01-79699f7e7118", + onesignal_google_project_number: "167856249045"] + } + signingConfigs { + release { + storeFile file("myreleasekey.keystore") + storePassword System.getenv("KEYSTOREPWD") + keyAlias "MyReleaseKey" + keyPassword System.getenv("KEYPWD") + } + + debug { + storeFile file('/home/hossein/android/keystores/poinila.jks') + storePassword '2d858dce8c0b9a6bb9f411d0a891fb8dc7718ea6' + keyAlias 'PoinilaReleaseKey' + keyPassword '36f4f28b92e39ff719e1e261f67fe75825c552f6' + } + } + + + buildTypes { + release { + signingConfig signingConfigs.release + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + zipAlignEnabled true + debuggable false + } + + compileOptions { + encoding "UTF-8" + } +// debug { +// versionNameSuffix "-DEBUG" +// applicationIdSuffix ".debug" +// } + } + lintOptions { + disable 'InvalidPackage' + disable "ResourceType" + } + productFlavors { + } + + aaptOptions{ + cruncherEnabled = false + } + + // butterknife and parceler conflict + packagingOptions { + exclude 'META-INF/services/javax.annotation.processing.Processor' // butterknife + } +} + +def dbflow_version = "3.1.1" + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar', '*.aar']) + compile project(':util') +// compile project(':downloadmanager') + compile 'com.jakewharton:butterknife:7.0.1' + compile 'de.hdodenhof:circleimageview:1.3.0' + compile 'org.apmem.tools:layouts:1.10@aar' + compile 'com.squareup.picasso:picasso:2.5.2' + compile 'com.android.support:cardview-v7:' + project.ext.supportLibVersion + compile 'com.android.support:recyclerview-v7:' + project.ext.supportLibVersion + compile 'com.android.support:design:' + project.ext.supportLibVersion + //compile 'com.android.support:percent:' + project.ext.supportLibVersion + compile 'com.mobsandgeeks:android-saripaar:2.0.3' + compile 'com.getbase:floatingactionbutton:1.10.1' + //debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1' + //releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' + compile 'com.yqritc:recyclerview-flexibledivider:1.2.8' + compile 'com.isseiaoki:simplecropview:1.0.16' + compile 'com.makeramen:roundedimageview:2.2.1' + compile 'me.relex:circleindicator:1.1.8@aar' + compile 'uk.co.chrisjenx:calligraphy:2.1.0' + compile 'ru.noties:debug:2.0.1' + compile 'com.squareup.retrofit:retrofit:1.9.0' + compile 'com.squareup.okhttp:okhttp:2.4.0' + //debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1' + //releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' + compile "org.parceler:parceler-api:1.1.5" + apt "org.parceler:parceler:1.1.5" + + apt "com.github.Raizlabs.DBFlow:dbflow-processor:${dbflow_version}" + // use kapt for kotlin apt + compile "com.github.Raizlabs.DBFlow:dbflow-core:${dbflow_version}" + compile "com.github.Raizlabs.DBFlow:dbflow:${dbflow_version}" + + + compile 'com.onesignal:OneSignal:2.+@aar' + compile 'com.google.android.gms:play-services-gcm:9.0.2' + compile 'com.google.android.gms:play-services-analytics:9.0.2' + compile "com.google.android.gms:play-services-location:9.0.2" + compile 'com.google.android.gms:play-services-auth:9.0.2' + compile 'com.google.android.gms:play-services-ads:9.0.2' + + compile 'com.android.support:customtabs:23.3.0' + compile('com.crashlytics.sdk.android:crashlytics:2.5.7@aar') { + transitive = true; + } + compile "com.tapstream.sdk:tapstream-android:3.1.0" +} + diff --git a/fabric.properties b/fabric.properties new file mode 100755 index 0000000..7462d04 --- /dev/null +++ b/fabric.properties @@ -0,0 +1,3 @@ +#Contains API Secret used to validate your application. Commit to internal source control; avoid making secret public. +#Sat Jul 02 09:01:21 IRDT 2016 +apiSecret=7a1d121a5dfbd03c3068c40fcb308e1ae13bffa2a3e1a172b4e9e92bd90b52d6 diff --git a/google-services.json b/google-services.json new file mode 100755 index 0000000..a66e500 --- /dev/null +++ b/google-services.json @@ -0,0 +1,48 @@ +{ + "project_info": { + "project_number": "167856249045", + "project_id": "ponila-official" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:167856249045:android:55a0071f579cd514", + "android_client_info": { + "package_name": "com.shaya.poinila" + } + }, + "oauth_client": [ + { + "client_id": "167856249045-k57ge2m42jpeds4bmsrf97ncr44aoj0u.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.shaya.poinila", + "certificate_hash": "EE4EA336D24FE4723DB34BF0CA9BF152A559AF5A" + } + }, + { + "client_id": "167856249045-bt5tjuk0io8tgq64m5d1uprdh6vb563t.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyAKNoFedZSzmvVSXbMba07n4e_Z4wszEpc" + } + ], + "services": { + "analytics_service": { + "status": 1 + }, + "appinvite_service": { + "status": 1, + "other_platform_oauth_client": [] + }, + "ads_service": { + "status": 1 + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/proguard-rules.pro b/proguard-rules.pro new file mode 100755 index 0000000..3f97896 --- /dev/null +++ b/proguard-rules.pro @@ -0,0 +1,55 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in D:\Android\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 *; +#} + +# Picasso +-dontwarn com.squareup.okhttp.** + +# Butter knife library +-keep class butterknife.** { *; } +-dontwarn butterknife.internal.** +-keep class **$$ViewBinder { *; } + +-keepclasseswithmembernames class * { + @butterknife.* ; +} + +-keepclasseswithmembernames class * { + @butterknife.* ; +} + +# Saripaar Form Validation +-keep class com.mobsandgeeks.saripaar.** {*;} +-keep class commons.validator.routines.** {*;} +-keep @com.mobsandgeeks.saripaar.annotation.ValidateUsing class * {*;} + +# LeakCanary +-keep class org.eclipse.mat.** { *; } +-keep class com.squareup.leakcanary.** { *; } + +# parcelable error (Unmarshalling unknown type code) +-keepclassmembers class * implements android.os.Parcelable { + static ** CREATOR; +} + +# Fabric +-keepattributes *Annotation* +-keepattributes SourceFile,LineNumberTable +-keep public class * extends java.lang.Exception + +# Tap Stream +-keep class com.google.android.gms.ads.identifier.** { *; } \ No newline at end of file diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000..e08e6d4 Binary files /dev/null and b/src/.DS_Store differ diff --git a/src/androidTest/java/com/shaya/poinila/android/presentation/ApplicationTest.java b/src/androidTest/java/com/shaya/poinila/android/presentation/ApplicationTest.java new file mode 100755 index 0000000..9fc2562 --- /dev/null +++ b/src/androidTest/java/com/shaya/poinila/android/presentation/ApplicationTest.java @@ -0,0 +1,13 @@ +package com.shaya.poinila.android.presentation; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/src/main/.DS_Store b/src/main/.DS_Store new file mode 100644 index 0000000..cb8615d Binary files /dev/null and b/src/main/.DS_Store differ diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml new file mode 100755 index 0000000..35dc91b --- /dev/null +++ b/src/main/AndroidManifest.xml @@ -0,0 +1,273 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/assets/.DS_Store b/src/main/assets/.DS_Store new file mode 100644 index 0000000..8ddc26d Binary files /dev/null and b/src/main/assets/.DS_Store differ diff --git a/src/main/assets/fonts/iransans.ttf b/src/main/assets/fonts/iransans.ttf new file mode 100755 index 0000000..5f7d44e Binary files /dev/null and b/src/main/assets/fonts/iransans.ttf differ diff --git a/src/main/ic_launcher-web.png b/src/main/ic_launcher-web.png new file mode 100755 index 0000000..5e583e3 Binary files /dev/null and b/src/main/ic_launcher-web.png differ diff --git a/src/main/java/.DS_Store b/src/main/java/.DS_Store new file mode 100644 index 0000000..3cd01eb Binary files /dev/null and b/src/main/java/.DS_Store differ diff --git a/src/main/java/android/.DS_Store b/src/main/java/android/.DS_Store new file mode 100644 index 0000000..a4d103a Binary files /dev/null and b/src/main/java/android/.DS_Store differ diff --git a/src/main/java/android/support/.DS_Store b/src/main/java/android/support/.DS_Store new file mode 100644 index 0000000..059ec74 Binary files /dev/null and b/src/main/java/android/support/.DS_Store differ diff --git a/src/main/java/android/support/design/.DS_Store b/src/main/java/android/support/design/.DS_Store new file mode 100644 index 0000000..e71cb39 Binary files /dev/null and b/src/main/java/android/support/design/.DS_Store differ diff --git a/src/main/java/android/support/design/widget/PoinilaTextInputLayout.java b/src/main/java/android/support/design/widget/PoinilaTextInputLayout.java new file mode 100755 index 0000000..3b758eb --- /dev/null +++ b/src/main/java/android/support/design/widget/PoinilaTextInputLayout.java @@ -0,0 +1,768 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.support.design.widget; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Typeface; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.StyleRes; +import android.support.v4.view.AccessibilityDelegateCompat; +import android.support.v4.view.ViewCompat; +import android.support.v4.view.ViewPropertyAnimatorListenerAdapter; +import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; +import android.support.v4.widget.Space; +import android.text.Editable; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; +import android.view.animation.AccelerateInterpolator; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.TextView; + +import android.support.design.R; + +import com.shaya.poinila.android.util.ResourceUtils; + +import uk.co.chrisjenx.calligraphy.CalligraphyConfig; +import uk.co.chrisjenx.calligraphy.CalligraphyUtils; + +import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; + +/** + * This class is a copy of Android support design library intending to use for rtl languages. + * @author Alireza Farahani. contact me at ar.d.farahani@gmail.com + * + * Layout which wraps an {@link android.widget.EditText} (or descendant) to show a floating label + * when the hint is hidden due to the user inputting text. + *

+ * Also supports showing an error via {@link #setErrorEnabled(boolean)} and + * {@link #setError(CharSequence)}. + */ +public class PoinilaTextInputLayout extends LinearLayout { + + private static final int ANIMATION_DURATION = 200; + private static final int INVALID_MAX_LENGTH = -1; + + private EditText mEditText; + private CharSequence mHint; + + private Paint mTmpPaint; + + private LinearLayout mIndicatorArea; + + private boolean mErrorEnabled; + private TextView mErrorView; + private int mErrorTextAppearance; + private boolean mErrorShown; + + private boolean mCounterEnabled; + private TextView mCounterView; + private int mCounterMaxLength; + private int mCounterTextAppearance; + private int mCounterOverflowTextAppearance; + private boolean mCounterOverflowed; + // alireza farahani + private boolean mHasSimpleBackground = false; // temporary solution for handling edittext color change when having error + + private ColorStateList mDefaultTextColor; + private ColorStateList mFocusedTextColor; + + private final CollapsingTextHelper mCollapsingTextHelper = new CollapsingTextHelper(this); + + private boolean mHintAnimationEnabled; + private ValueAnimatorCompat mAnimator; + + public PoinilaTextInputLayout(Context context) { + this(context, null); + } + + public PoinilaTextInputLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public PoinilaTextInputLayout(Context context, AttributeSet attrs, int defStyleAttr) { + // Can't call through to super(Context, AttributeSet, int) since it doesn't exist on API 10 + super(context, attrs); + + ThemeUtils.checkAppCompatTheme(context); + + setOrientation(VERTICAL); + setWillNotDraw(false); + setAddStatesFromChildren(true); + + mCollapsingTextHelper.setTextSizeInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR); + mCollapsingTextHelper.setPositionInterpolator(new AccelerateInterpolator()); + mCollapsingTextHelper.setCollapsedTextGravity(Gravity.TOP | Gravity.RIGHT); // alireza farahani + + final TypedArray a = context.obtainStyledAttributes(attrs, + R.styleable.TextInputLayout, defStyleAttr, R.style.Widget_Design_TextInputLayout); + setHint(a.getText(R.styleable.TextInputLayout_android_hint)); + mHintAnimationEnabled = a.getBoolean( + R.styleable.TextInputLayout_hintAnimationEnabled, true); + + if (a.hasValue(R.styleable.TextInputLayout_android_textColorHint)) { + mDefaultTextColor = mFocusedTextColor = + a.getColorStateList(R.styleable.TextInputLayout_android_textColorHint); + } + + final int hintAppearance = a.getResourceId( + R.styleable.TextInputLayout_hintTextAppearance, -1); + if (hintAppearance != -1) { + setHintTextAppearance( + a.getResourceId(R.styleable.TextInputLayout_hintTextAppearance, 0)); + } + + mErrorTextAppearance = a.getResourceId(R.styleable.TextInputLayout_errorTextAppearance, 0); + final boolean errorEnabled = a.getBoolean(R.styleable.TextInputLayout_errorEnabled, false); + + final boolean counterEnabled = a.getBoolean( + R.styleable.TextInputLayout_counterEnabled, false); + setCounterMaxLength( + a.getInt(R.styleable.TextInputLayout_counterMaxLength, INVALID_MAX_LENGTH)); + mCounterTextAppearance = a.getResourceId( + R.styleable.TextInputLayout_counterTextAppearance, 0); + mCounterOverflowTextAppearance = a.getResourceId( + R.styleable.TextInputLayout_counterOverflowTextAppearance, 0); + + // alireza farahani + final TypedArray poinila_a = context.obtainStyledAttributes(attrs, com.shaya.poinila.android.presentation.R.styleable.PoinilaTextInputLayout); + mHasSimpleBackground = poinila_a.getBoolean(com.shaya.poinila.android.presentation.R.styleable.PoinilaTextInputLayout_hasSimpleBackground, false); + poinila_a.recycle(); + + a.recycle(); + + setErrorEnabled(errorEnabled); + setCounterEnabled(counterEnabled); + + if (ViewCompat.getImportantForAccessibility(this) + == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_AUTO) { + // Make sure we're important for accessibility if we haven't been explicitly not + ViewCompat.setImportantForAccessibility(this, + ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES); + } + + ViewCompat.setAccessibilityDelegate(this, new TextInputAccessibilityDelegate()); + } + + @Override + public void addView(View child, int index, ViewGroup.LayoutParams params) { + if (child instanceof EditText) { + setEditText((EditText) child); + super.addView(child, 0, updateEditTextMargin(params)); + } else { + // Carry on adding the View... + super.addView(child, index, params); + } + } + + /** + * Set the typeface to use for both the expanded and floating hint. + * + * @param typeface typeface to use, or {@code null} to use the default. + */ + public void setTypeface(@Nullable Typeface typeface) { + mCollapsingTextHelper.setTypefaces(typeface); + } + + /** + * Returns the typeface used for both the expanded and floating hint. + */ + @NonNull + public Typeface getTypeface() { + // This could be either the collapsed or expanded + return mCollapsingTextHelper.getCollapsedTypeface(); + } + + private void setEditText(EditText editText) { + // If we already have an EditText, throw an exception + if (mEditText != null) { + throw new IllegalArgumentException("We already have an EditText, can only have one"); + } + mEditText = editText; + + // Use the EditText's typeface, and it's text size for our expanded text + mCollapsingTextHelper.setTypefaces(mEditText.getTypeface()); + mCollapsingTextHelper.setExpandedTextSize(mEditText.getTextSize()); + mCollapsingTextHelper.setExpandedTextGravity(mEditText.getGravity()); + + // Add a TextWatcher so that we know when the text input has changed + mEditText.addTextChangedListener(new TextWatcher() { + @Override + public void afterTextChanged(Editable s) { + updateLabelVisibility(true); + if (mCounterEnabled) { + updateCounter(s.length()); + } + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + }); + + // Use the EditText's hint colors if we don't have one set + if (mDefaultTextColor == null) { + mDefaultTextColor = mEditText.getHintTextColors(); + } + + // If we do not have a valid hint, try and retrieve it from the EditText + if (TextUtils.isEmpty(mHint)) { + setHint(mEditText.getHint()); + // Clear the EditText's hint as we will display it ourselves + mEditText.setHint(null); + } + + if (mCounterView != null) { + updateCounter(mEditText.getText().length()); + } + + if (mIndicatorArea != null) { + adjustIndicatorPadding(); + } + + // Update the label visibility with no animation + updateLabelVisibility(false); + } + + private LayoutParams updateEditTextMargin(ViewGroup.LayoutParams lp) { + // Create/update the LayoutParams so that we can add enough top margin + // to the EditText so make room for the label + LayoutParams llp = lp instanceof LayoutParams ? (LayoutParams) lp : new LayoutParams(lp); + + if (mTmpPaint == null) { + mTmpPaint = new Paint(); + } + mTmpPaint.setTypeface(mCollapsingTextHelper.getCollapsedTypeface()); + mTmpPaint.setTextSize(mCollapsingTextHelper.getCollapsedTextSize()); + llp.topMargin = (int) -mTmpPaint.ascent(); + + return llp; + } + + private void updateLabelVisibility(boolean animate) { + final boolean hasText = mEditText != null && !TextUtils.isEmpty(mEditText.getText()); + final boolean isFocused = arrayContains(getDrawableState(), android.R.attr.state_focused); + final boolean isErrorShowing = !TextUtils.isEmpty(getError()); + + if (mDefaultTextColor != null) { + mCollapsingTextHelper.setExpandedTextColor(mDefaultTextColor.getDefaultColor()); + } + + if (mCounterOverflowed && mCounterView != null) { + mCollapsingTextHelper.setCollapsedTextColor(mCounterView.getCurrentTextColor()); + } else if (isErrorShowing && mErrorView != null) { + mCollapsingTextHelper.setCollapsedTextColor(mErrorView.getCurrentTextColor()); + } else if (isFocused && mFocusedTextColor != null) { + mCollapsingTextHelper.setCollapsedTextColor(mFocusedTextColor.getDefaultColor()); + } else if (mDefaultTextColor != null) { + mCollapsingTextHelper.setCollapsedTextColor(mDefaultTextColor.getDefaultColor()); + } + + if (hasText || isFocused || isErrorShowing) { + // We should be showing the label so do so if it isn't already + collapseHint(animate); + } else { + // We should not be showing the label so hide it + expandHint(animate); + } + } + + /** + * Returns the {@link android.widget.EditText} used for text input. + */ + @Nullable + public EditText getEditText() { + return mEditText; + } + + /** + * Set the hint to be displayed in the floating label + * + * @attr ref android.support.design.R.styleable#TextInputLayout_android_hint + */ + public void setHint(@Nullable CharSequence hint) { + mHint = hint; + mCollapsingTextHelper.setText(hint); + + sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); + } + + /** + * Returns the hint which is displayed in the floating label. + * + * @attr ref android.support.design.R.styleable#TextInputLayout_android_hint + */ + @Nullable + public CharSequence getHint() { + return mHint; + } + + /** + * Sets the hint text color, size, style from the specified TextAppearance resource. + * + * @attr ref android.support.design.R.styleable#TextInputLayout_hintTextAppearance + */ + public void setHintTextAppearance(@StyleRes int resId) { + mCollapsingTextHelper.setCollapsedTextAppearance(resId); + mFocusedTextColor = ColorStateList.valueOf(mCollapsingTextHelper.getCollapsedTextColor()); + + if (mEditText != null) { + updateLabelVisibility(false); + + // Text size might have changed so update the top margin + LayoutParams lp = updateEditTextMargin(mEditText.getLayoutParams()); + mEditText.setLayoutParams(lp); + mEditText.requestLayout(); + } + } + + + private void addIndicator(TextView indicator, int index) { + if (mIndicatorArea == null) { + mIndicatorArea = new LinearLayout(getContext()); + /*RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, + RelativeLayout.LayoutParams.WRAP_CONTENT); + mIndicatorArea.setGravity(Gravity.RIGHT);*/ + mIndicatorArea.setOrientation(LinearLayout.HORIZONTAL); + /* alireza farahani */ + mIndicatorArea.setGravity(Gravity.RIGHT); + addView(mIndicatorArea, LinearLayout.LayoutParams.MATCH_PARENT, + WRAP_CONTENT); + /* alireza farahani */ + + // Add a flexible spacer in the middle so that the left/right views stay pinned + final Space spacer = new Space(getContext()); + + /* alireza farahani */ + final LinearLayout.LayoutParams spacerLp = new LinearLayout.LayoutParams(0, + ((int) ResourceUtils.getDimen(com.shaya.poinila.android.presentation.R.dimen.margin_lvl2)));//0, 1f); + + mIndicatorArea.addView(spacer, spacerLp); + + if (mEditText != null) { + adjustIndicatorPadding(); + } + } + /* alireza farahani */ + mIndicatorArea.setVisibility(View.VISIBLE); + if (index == Integer.MAX_VALUE) { // adding error to right of layout + index = mIndicatorArea.getChildCount(); + mIndicatorArea.addView(indicator, index); + } else { // adding counter + mIndicatorArea.addView(indicator, new LinearLayout.LayoutParams(0, WRAP_CONTENT, 1f)); + } + /* alireza farahani */ + } + + private void adjustIndicatorPadding() { + // Add padding to the error and character counter so that they match the EditText + ViewCompat.setPaddingRelative(mIndicatorArea, ViewCompat.getPaddingStart(mEditText), + 0, ViewCompat.getPaddingEnd(mEditText), mEditText.getPaddingBottom()); + } + + private void removeIndicator(TextView indicator) { + if (mIndicatorArea != null) { + mIndicatorArea.removeView(indicator); + if (mIndicatorArea.getChildCount() == 0) { + mIndicatorArea.setVisibility(View.GONE); + } + } + } + + /** + * Whether the error functionality is enabled or not in this layout. Enabling this + * functionality before setting an error message via {@link #setError(CharSequence)}, will mean + * that this layout will not change size when an error is displayed. + * + * @attr ref android.support.design.R.styleable#TextInputLayout_errorEnabled + */ + public void setErrorEnabled(boolean enabled) { + if (mErrorEnabled != enabled) { + if (mErrorView != null) { + ViewCompat.animate(mErrorView).cancel(); + } + + if (enabled) { + mErrorView = new TextView(getContext()); + mErrorView.setTextAppearance(getContext(), mErrorTextAppearance); + + //alireza farahani + CalligraphyUtils.applyFontToTextView(mErrorView.getContext(), mErrorView, CalligraphyConfig.get().getFontPath()); + + mErrorView.setVisibility(INVISIBLE); + ViewCompat.setAccessibilityLiveRegion(mErrorView, + ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE); + addIndicator(mErrorView, Integer.MAX_VALUE); + } else { + mErrorShown = false; + updateEditTextBackground(); + removeIndicator(mErrorView); + mErrorView = null; + } + mErrorEnabled = enabled; + } + } + + /** + * Returns whether the error functionality is enabled or not in this layout. + * + * @attr ref android.support.design.R.styleable#TextInputLayout_errorEnabled + * @see #setErrorEnabled(boolean) + */ + public boolean isErrorEnabled() { + return mErrorEnabled; + } + + /** + * Sets an error message that will be displayed below our {@link EditText}. If the + * {@code error} is {@code null}, the error message will be cleared. + *

+ * If the error functionality has not been enabled via {@link #setErrorEnabled(boolean)}, then + * it will be automatically enabled if {@code error} is not empty. + * + * @param error Error message to display, or null to clear + * @see #getError() + */ + public void setError(@Nullable CharSequence error) { + if (!mErrorEnabled) { + if (TextUtils.isEmpty(error)) { + // If error isn't enabled, and the error is empty, just return + return; + } + // Else, we'll assume that they want to enable the error functionality + setErrorEnabled(true); + } + + if (!TextUtils.isEmpty(error)) { + ViewCompat.setAlpha(mErrorView, 0f); + mErrorView.setText(error); + ViewCompat.animate(mErrorView) + .alpha(1f) + .setDuration(ANIMATION_DURATION) + .setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR) + .setListener(new ViewPropertyAnimatorListenerAdapter() { + @Override + public void onAnimationStart(View view) { + view.setVisibility(VISIBLE); + } + }) + .start(); + + // Set the EditText's background tint to the error color + mErrorShown = true; + updateEditTextBackground(); + updateLabelVisibility(true); + } else { + if (mErrorView.getVisibility() == VISIBLE) { + ViewCompat.animate(mErrorView) + .alpha(0f) + .setDuration(ANIMATION_DURATION) + .setInterpolator(AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR) + .setListener(new ViewPropertyAnimatorListenerAdapter() { + @Override + public void onAnimationEnd(View view) { + view.setVisibility(INVISIBLE); + + updateLabelVisibility(true); + } + }).start(); + + // Restore the 'original' tint, using colorControlNormal and colorControlActivated + mErrorShown = false; + updateEditTextBackground(); + } + } + } + + /** + * Whether the character counter functionality is enabled or not in this layout. + * + * @attr ref android.support.design.R.styleable#TextInputLayout_counterEnabled + */ + public void setCounterEnabled(boolean enabled) { + if (mCounterEnabled != enabled) { + if (enabled) { + mCounterView = new TextView(getContext()); + mCounterView.setGravity(Gravity.LEFT); + mCounterView.setMaxLines(1); + mCounterView.setTextAppearance(getContext(), mCounterTextAppearance); + ViewCompat.setAccessibilityLiveRegion(mCounterView, + ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE); + + // alireza farahani + addIndicator(mCounterView, 0); + CalligraphyUtils.applyFontToTextView(mCounterView.getContext(), mCounterView, CalligraphyConfig.get().getFontPath()); + + if (mEditText == null) { + updateCounter(0); + } else { + updateCounter(mEditText.getText().length()); + } + } else { + removeIndicator(mCounterView); + mCounterView = null; + } + mCounterEnabled = enabled; + } + } + + /** + * Sets the max length to display at the character counter. + * + * @param maxLength maxLength to display. Any value less than or equal to 0 will not be shown. + */ + public void setCounterMaxLength(int maxLength) { + if (mCounterMaxLength != maxLength) { + if (maxLength > 0) { + mCounterMaxLength = maxLength; + } else { + mCounterMaxLength = INVALID_MAX_LENGTH; + } + if (mCounterEnabled) { + updateCounter(mEditText == null ? 0 : mEditText.getText().length()); + } + } + } + + /** + * Returns the max length shown at the character counter. + */ + public int getCounterMaxLength() { + return mCounterMaxLength; + } + + private void updateCounter(int length) { + boolean wasCounterOverflowed = mCounterOverflowed; + if (mCounterMaxLength == INVALID_MAX_LENGTH) { + mCounterView.setText(String.valueOf(length)); + mCounterOverflowed = false; + } else { + mCounterOverflowed = length > mCounterMaxLength; + if (wasCounterOverflowed != mCounterOverflowed) { + mCounterView.setTextAppearance(getContext(), mCounterOverflowed ? + mCounterOverflowTextAppearance : mCounterTextAppearance); + // alireza farahani + CalligraphyUtils.applyFontToTextView(mCounterView.getContext(), mCounterView, CalligraphyConfig.get().getFontPath()); + } + mCounterView.setText(getContext().getString(R.string.character_counter_pattern, + length, mCounterMaxLength)); + } + if (mEditText != null && wasCounterOverflowed != mCounterOverflowed) { + updateLabelVisibility(false); + updateEditTextBackground(); + } + } + + private void updateEditTextBackground() { // alireza farahani. handling cases in which edittext has custom background + if (mErrorShown && mErrorView != null && mHasSimpleBackground) { + // Set the EditText's background tint to the error color + ViewCompat.setBackgroundTintList(mEditText, + ColorStateList.valueOf(mErrorView.getCurrentTextColor())); + } else if (mCounterOverflowed && mCounterView != null && mHasSimpleBackground) { + ViewCompat.setBackgroundTintList(mEditText, + ColorStateList.valueOf(mCounterView.getCurrentTextColor())); + } else if (mHasSimpleBackground) { +// final TintManager tintManager = TintManager.get(getContext()); +// ViewCompat.setBackgroundTintList(mEditText, +// tintManager.getTintList(R.drawable.abc_edit_text_material)); + } + } + + /** + * Returns the error message that was set to be displayed with + * {@link #setError(CharSequence)}, or null if no error was set + * or if error displaying is not enabled. + * + * @see #setError(CharSequence) + */ + @Nullable + public CharSequence getError() { + if (mErrorEnabled && mErrorView != null && mErrorView.getVisibility() == VISIBLE) { + return mErrorView.getText(); + } + return null; + } + + /** + * Returns whether any hint state changes, due to being focused or non-empty text, are + * animated. + * + * @attr ref android.support.design.R.styleable#TextInputLayout_hintAnimationEnabled + * @see #setHintAnimationEnabled(boolean) + */ + public boolean isHintAnimationEnabled() { + return mHintAnimationEnabled; + } + + /** + * Set whether any hint state changes, due to being focused or non-empty text, are + * animated. + * + * @attr ref android.support.design.R.styleable#TextInputLayout_hintAnimationEnabled + * @see #isHintAnimationEnabled() + */ + public void setHintAnimationEnabled(boolean enabled) { + mHintAnimationEnabled = enabled; + } + + @Override + public void draw(Canvas canvas) { + super.draw(canvas); + mCollapsingTextHelper.draw(canvas); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + + if (mEditText != null) { + final int l = mEditText.getLeft() + mEditText.getCompoundPaddingLeft(); + final int r = mEditText.getRight() - mEditText.getCompoundPaddingRight(); + + mCollapsingTextHelper.setExpandedBounds(l, + mEditText.getTop() + mEditText.getCompoundPaddingTop(), + r, mEditText.getBottom() - mEditText.getCompoundPaddingBottom()); + + // Set the collapsed bounds to be the the full height (minus padding) to match the + // EditText's editable area + mCollapsingTextHelper.setCollapsedBounds(l, getPaddingTop(), + r, bottom - top - getPaddingBottom()); + + mCollapsingTextHelper.recalculate(); + } + } + + @Override + public void refreshDrawableState() { + super.refreshDrawableState(); + // Drawable state has changed so see if we need to update the label + updateLabelVisibility(ViewCompat.isLaidOut(this)); + } + + private void collapseHint(boolean animate) { + if (mAnimator != null && mAnimator.isRunning()) { + mAnimator.cancel(); + } + if (animate && mHintAnimationEnabled) { + animateToExpansionFraction(1f); + } else { + mCollapsingTextHelper.setExpansionFraction(1f); + } + } + + private void expandHint(boolean animate) { + if (mAnimator != null && mAnimator.isRunning()) { + mAnimator.cancel(); + } + if (animate && mHintAnimationEnabled) { + animateToExpansionFraction(0f); + } else { + mCollapsingTextHelper.setExpansionFraction(0f); + } + } + + private void animateToExpansionFraction(final float target) { + if (mCollapsingTextHelper.getExpansionFraction() == target) { + return; + } + if (mAnimator == null) { + mAnimator = ViewUtils.createAnimator(); + mAnimator.setInterpolator(AnimationUtils.LINEAR_INTERPOLATOR); + mAnimator.setDuration(ANIMATION_DURATION); + mAnimator.setUpdateListener(new ValueAnimatorCompat.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimatorCompat animator) { + mCollapsingTextHelper.setExpansionFraction(animator.getAnimatedFloatValue()); + } + }); + } + mAnimator.setFloatValues(mCollapsingTextHelper.getExpansionFraction(), target); + mAnimator.start(); + } + + private int getThemeAttrColor(int attr) { + TypedValue tv = new TypedValue(); + if (getContext().getTheme().resolveAttribute(attr, tv, true)) { + return tv.data; + } else { + return Color.MAGENTA; + } + } + + private class TextInputAccessibilityDelegate extends AccessibilityDelegateCompat { + @Override + public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) { + super.onInitializeAccessibilityEvent(host, event); + event.setClassName(TextInputLayout.class.getSimpleName()); + } + + @Override + public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) { + super.onPopulateAccessibilityEvent(host, event); + + final CharSequence text = mCollapsingTextHelper.getText(); + if (!TextUtils.isEmpty(text)) { + event.getText().add(text); + } + } + + @Override + public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { + super.onInitializeAccessibilityNodeInfo(host, info); + info.setClassName(TextInputLayout.class.getSimpleName()); + + final CharSequence text = mCollapsingTextHelper.getText(); + if (!TextUtils.isEmpty(text)) { + info.setText(text); + } + if (mEditText != null) { + info.setLabelFor(mEditText); + } + final CharSequence error = mErrorView != null ? mErrorView.getText() : null; + if (!TextUtils.isEmpty(error)) { + info.setContentInvalid(true); + info.setError(error); + } + } + } + + private static boolean arrayContains(int[] array, int value) { + for (int v : array) { + if (v == value) { + return true; + } + } + return false; + } + +} \ No newline at end of file diff --git a/src/main/java/com/shaya/poinila/android/NotificationService.java b/src/main/java/com/shaya/poinila/android/NotificationService.java new file mode 100755 index 0000000..4841fbb --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/NotificationService.java @@ -0,0 +1,93 @@ +package com.shaya.poinila.android; + +import android.support.v4.app.NotificationCompat; +import android.util.Log; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.onesignal.NotificationExtenderService; +import com.onesignal.OSNotificationPayload; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.utils.NotificationQueue; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.List; + +import data.model.Member; +import data.model.Post; +import manager.DBFacade; + +import static com.shaya.poinila.android.utils.PushNotificationUtils.*; + +/** + * Created by iran on 6/7/2016. + */ +public class NotificationService extends NotificationExtenderService { + + + + @Override + protected boolean onNotificationProcessing(final OSNotificationPayload notification) { + + Log.i(getClass().getName(), "additionalData : " + notification.additionalData); + + OverrideSettings overrideSettings = new OverrideSettings(); + overrideSettings.extender = new NotificationCompat.Extender() { + @Override + public NotificationCompat.Builder extend(NotificationCompat.Builder builder) { + +// String message = createNotificationMessage(notification.additionalData); + +// builder.setStyle(new NotificationCompat.BigTextStyle().bigText(message)); +// builder.setContentText(message); +// builder.setTicker(message); + + NOTIFICATION_TYPE group = getNotificationType(notification.additionalData); + JSONArray data = NotificationQueue.getInstance().get(group.toString().toLowerCase()); + + if(!group.equals(NOTIFICATION_TYPE.FRIENDSHIP_REQUEST) + && !group.equals(NOTIFICATION_TYPE.FRIENDSHIP_ANSWER) + && !group.equals(NOTIFICATION_TYPE.POST_SUGGESTION)){ + JSONObject jsonModel = notification.additionalData.optJSONObject("object"); + + Logger.log("push data additionalData = " + notification.additionalData, Logger.LEVEL_INFO); + + + if(data == null) + data = new JSONArray(); + try { + JSONObject ownerJs = new JSONObject(new Gson().toJson(DBFacade.getCachedMyInfo(), Member.class)); + switch (group){ + case FOLLOW: + JSONObject collectionJs = jsonModel.optJSONObject("collection"); + collectionJs.put("owner", ownerJs); + data.put(collectionJs); + break; + default: + JSONObject postJs = jsonModel.optJSONObject("post"); + postJs.put("poster", ownerJs); + JSONObject collJs = jsonModel.optJSONObject("post_collection"); + postJs.put("collection", collJs); + data.put(postJs); + } + } catch (JSONException e) { + e.printStackTrace(); + } + NotificationQueue.getInstance().put(group.toString().toLowerCase(), data); + } + + return builder; + + } + }; + + displayNotification(overrideSettings); + + return true; + } + + +} diff --git a/src/main/java/com/shaya/poinila/android/authentication/AuthenticationService.java b/src/main/java/com/shaya/poinila/android/authentication/AuthenticationService.java new file mode 100755 index 0000000..28cd72f --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/authentication/AuthenticationService.java @@ -0,0 +1,17 @@ +package com.shaya.poinila.android.authentication; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import android.support.annotation.Nullable; + +/** + * Created by iran on 5/30/2016. + */ +public class AuthenticationService extends Service { + + @Override + public IBinder onBind(Intent intent) { + return new Authenticator(this).getIBinder(); + } +} diff --git a/src/main/java/com/shaya/poinila/android/authentication/Authenticator.java b/src/main/java/com/shaya/poinila/android/authentication/Authenticator.java new file mode 100755 index 0000000..65c585b --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/authentication/Authenticator.java @@ -0,0 +1,119 @@ +package com.shaya.poinila.android.authentication; + +import android.accounts.AbstractAccountAuthenticator; +import android.accounts.Account; +import android.accounts.AccountAuthenticatorResponse; +import android.accounts.AccountManager; +import android.accounts.NetworkErrorException; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.text.TextUtils; + +import com.shaya.poinila.android.presentation.view.activity.SignUpLoginActivity; + +/** + * Created by iran on 5/30/2016. + */ +public class Authenticator extends AbstractAccountAuthenticator { + + Context mContext; + + String TAG = this.getClass().getSimpleName(); + + public Authenticator(Context context) { + super(context); + mContext = context; + } + + @Override + public Bundle addAccount(AccountAuthenticatorResponse response, + String accountType, String authTokenType, + String[] requiredFeatures, Bundle options) + throws NetworkErrorException { + + final Intent intent = new Intent(mContext, SignUpLoginActivity.class); +// intent.putExtra(AuthenticationActivity.ARG_ACCOUNT_TYPE, accountType); +// intent.putExtra(AuthenticationActivity.ARG_AUTH_TYPE, authTokenType); +// intent.putExtra(AuthenticationActivity.ARG_IS_ADDING_NEW_ACCOUNT, true); + intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response); + + final Bundle bundle = new Bundle(); + + bundle.putParcelable(AccountManager.KEY_INTENT, intent); + + return bundle; + } + + + + @Override + public Bundle getAuthToken(AccountAuthenticatorResponse response, + Account account, String authTokenType, Bundle options) + throws NetworkErrorException { + + // Extract the username and password from the Account Manager + final AccountManager am = AccountManager.get(mContext); + String authToken = am.peekAuthToken(account, authTokenType); + + // Let's give another try to authenticate the user + if (TextUtils.isEmpty(authToken)) { + final String password = am.getPassword(account); + + if (password != null) { +// authToken = ServerConnector.SignIn(account.name, password); + } + } + else{ + final Bundle result = new Bundle(); + result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); + result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type); + result.putString(AccountManager.KEY_AUTHTOKEN, authToken); + return result; + } + + // If we get here, then we couldn't access the user's password - so we + // need to re-prompt them for their credentials. We do that by creating + // an intent to display our AuthenticatorActivity. + final Intent intent = new Intent(mContext, SignUpLoginActivity.class); + intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response); +// intent.putExtra(AuthenticationActivity.ARG_ACCOUNT_TYPE, account.type); +// intent.putExtra(AuthenticationActivity.ARG_AUTH_TYPE, authTokenType); + + final Bundle bundle = new Bundle(); + bundle.putParcelable(AccountManager.KEY_INTENT, intent); + + return bundle; + } + + @Override + public String getAuthTokenLabel(String authTokenType) { + return null; + } + + @Override + public Bundle hasFeatures(AccountAuthenticatorResponse response, + Account account, String[] features) throws NetworkErrorException { + return null; + } + + @Override + public Bundle updateCredentials(AccountAuthenticatorResponse response, + Account account, String authTokenType, Bundle options) + throws NetworkErrorException { + return null; + } + + + @Override + public Bundle confirmCredentials(AccountAuthenticatorResponse response, + Account account, Bundle options) throws NetworkErrorException { + return null; + } + + @Override + public Bundle editProperties(AccountAuthenticatorResponse response, + String accountType) { + return null; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/AndroidUtilities.java b/src/main/java/com/shaya/poinila/android/presentation/AndroidUtilities.java new file mode 100755 index 0000000..b677aa1 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/AndroidUtilities.java @@ -0,0 +1,1118 @@ +package com.shaya.poinila.android.presentation; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.provider.DocumentsContract; +import android.provider.MediaStore; +import android.view.View; +import android.view.inputmethod.InputMethodManager; +import com.shaya.poinila.android.util.ContextHolder; +import com.shaya.poinila.android.util.Logger; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.List; + +import data.model.SystemPreferences; + +/** + * Created by iran on 12/8/2015. + */ +public class AndroidUtilities { + private static boolean waitingForSms = false; + private static final Object smsLock = new Object(); + /*private static final Hashtable typefaceCache = new Hashtable<>(); + private static int prevOrientation = -10; + + + public static int statusBarHeight = 0; + public static float density = 1; + public static Point displaySize = new Point(); + public static Integer photoSize = null; + public static DisplayMetrics displayMetrics = new DisplayMetrics(); + public static int leftBaseline; + public static boolean usingHardwareInput; + private static Boolean isTablet = null; + private static int adjustOwnerClassGuid = 0; + + public static Pattern WEB_URL = null; + static { + try { + final String GOOD_IRI_CHAR = "a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF"; + final Pattern IP_ADDRESS = Pattern.compile( + "((25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(25[0-5]|2[0-4]" + + "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]" + + "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}" + + "|[1-9][0-9]|[0-9]))"); + final String IRI = "[" + GOOD_IRI_CHAR + "]([" + GOOD_IRI_CHAR + "\\-]{0,61}[" + GOOD_IRI_CHAR + "]){0,1}"; + final String GOOD_GTLD_CHAR = "a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF"; + final String GTLD = "[" + GOOD_GTLD_CHAR + "]{2,63}"; + final String HOST_NAME = "(" + IRI + "\\.)+" + GTLD; + final Pattern DOMAIN_NAME = Pattern.compile("(" + HOST_NAME + "|" + IP_ADDRESS + ")"); + WEB_URL = Pattern.compile( + "((?:(http|https|Http|Https):\\/\\/(?:(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)" + + "\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\$\\-\\_" + + "\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@)?)?" + + "(?:" + DOMAIN_NAME + ")" + + "(?:\\:\\d{1,5})?)" // plus option port number + + "(\\/(?:(?:[" + GOOD_IRI_CHAR + "\\;\\/\\?\\:\\@\\&\\=\\#\\~" // plus option query params + + "\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])|(?:\\%[a-fA-F0-9]{2}))*)?" + + "(?:\\b|$)"); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + + + + static { + density = ApplicationLoader.applicationContext.getResources().getDisplayMetrics().density; + leftBaseline = isTablet() ? 80 : 72; + checkDisplaySize(); + } + + public static void requestAdjustResize(Activity activity, int classGuid) { + if (activity == null || isTablet()) { + return; + } + activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); + adjustOwnerClassGuid = classGuid; + } + + public static void removeAdjustResize(Activity activity, int classGuid) { + if (activity == null || isTablet()) { + return; + } + if (adjustOwnerClassGuid == classGuid) { + activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); + } + } + + public static void lockOrientation(Activity activity) { + if (activity == null || prevOrientation != -10 || Build.VERSION.SDK_INT < 9) { + return; + } + try { + prevOrientation = activity.getRequestedOrientation(); + WindowManager manager = (WindowManager)activity.getSystemService(Activity.WINDOW_SERVICE); + if (manager != null && manager.getDefaultDisplay() != null) { + int rotation = manager.getDefaultDisplay().getRotation(); + int orientation = activity.getResources().getConfiguration().orientation; + int SCREEN_ORIENTATION_REVERSE_LANDSCAPE = 8; + int SCREEN_ORIENTATION_REVERSE_PORTRAIT = 9; + if (Build.VERSION.SDK_INT < 9) { + SCREEN_ORIENTATION_REVERSE_LANDSCAPE = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; + SCREEN_ORIENTATION_REVERSE_PORTRAIT = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; + } + + if (rotation == Surface.ROTATION_270) { + if (orientation == Configuration.ORIENTATION_PORTRAIT) { + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + } else { + activity.setRequestedOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE); + } + } else if (rotation == Surface.ROTATION_90) { + if (orientation == Configuration.ORIENTATION_PORTRAIT) { + activity.setRequestedOrientation(SCREEN_ORIENTATION_REVERSE_PORTRAIT); + } else { + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + } + } else if (rotation == Surface.ROTATION_0) { + if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + } else { + activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + } + } else { + if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + activity.setRequestedOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE); + } else { + activity.setRequestedOrientation(SCREEN_ORIENTATION_REVERSE_PORTRAIT); + } + } + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + + public static void unlockOrientation(Activity activity) { + if (activity == null || Build.VERSION.SDK_INT < 9) { + return; + } + try { + if (prevOrientation != -10) { + activity.setRequestedOrientation(prevOrientation); + prevOrientation = -10; + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + + public static Typeface getTypeface(String assetPath) { + synchronized (typefaceCache) { + if (!typefaceCache.containsKey(assetPath)) { + try { + Typeface t = Typeface.createFromAsset(ApplicationLoader.applicationContext.getAssets(), assetPath); + typefaceCache.put(assetPath, t); + } catch (Exception e) { + FileLog.e("Typefaces", "Could not get typeface '" + assetPath + "' because " + e.getMessage()); + return null; + } + } + return typefaceCache.get(assetPath); + } + } + + public static File getCacheDir() { + String state = null; + try { + state = Environment.getExternalStorageState(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + if (state == null || state.startsWith(Environment.MEDIA_MOUNTED)) { + try { + File file = ApplicationLoader.applicationContext.getExternalCacheDir(); + if (file != null) { + return file; + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + try { + File file = ApplicationLoader.applicationContext.getCacheDir(); + if (file != null) { + return file; + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + return new File(""); + } + + public static int dp(float value) { + if (value == 0) { + return 0; + } + return (int) Math.ceil(density * value); + } + + public static int compare(int lhs, int rhs) { + if (lhs == rhs) { + return 0; + } else if (lhs > rhs) { + return 1; + } + return -1; + } + + public static float dpf2(float value) { + if (value == 0) { + return 0; + } + return density * value; + } + + public static void checkDisplaySize() { + try { + Configuration configuration = ApplicationLoader.applicationContext.getResources().getConfiguration(); + usingHardwareInput = configuration.keyboard != Configuration.KEYBOARD_NOKEYS && configuration.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO; + WindowManager manager = (WindowManager) ApplicationLoader.applicationContext.getSystemService(Context.WINDOW_SERVICE); + if (manager != null) { + Display display = manager.getDefaultDisplay(); + if (display != null) { + display.getMetrics(displayMetrics); + if (android.os.Build.VERSION.SDK_INT < 13) { + displaySize.set(display.getWidth(), display.getHeight()); + } else { + display.getSize(displaySize); + } + FileLog.e("tmessages", "display size = " + displaySize.x + " " + displaySize.y + " " + displayMetrics.xdpi + "x" + displayMetrics.ydpi); + } + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + + public static float getPixelsInCM(float cm, boolean isX) { + return (cm / 2.54f) * (isX ? displayMetrics.xdpi : displayMetrics.ydpi); + } + + public static long makeBroadcastId(int id) { + return 0x0000000100000000L | ((long)id & 0x00000000FFFFFFFFL); + } + + public static int getMyLayerVersion(int layer) { + return layer & 0xffff; + } + + public static int getPeerLayerVersion(int layer) { + return (layer >> 16) & 0xffff; + } + + public static int setMyLayerVersion(int layer, int version) { + return layer & 0xffff0000 | version; + } + + public static int setPeerLayerVersion(int layer, int version) { + return layer & 0x0000ffff | (version << 16); + } + + public static void runOnUIThread(Runnable runnable) { + runOnUIThread(runnable, 0); + } + + public static void runOnUIThread(Runnable runnable, long delay) { + if (delay == 0) { + ApplicationLoader.applicationHandler.post(runnable); + } else { + ApplicationLoader.applicationHandler.postDelayed(runnable, delay); + } + } + + public static void cancelRunOnUIThread(Runnable runnable) { + ApplicationLoader.applicationHandler.removeCallbacks(runnable); + } + + public static boolean isTablet() { + if (isTablet == null) { + isTablet = ApplicationLoader.applicationContext.getResources().getBoolean(R.bool.isTablet); + } + return isTablet; + } + + public static boolean isSmallTablet() { + float minSide = Math.min(displaySize.x, displaySize.y) / density; + return minSide <= 700; + } + + public static int getMinTabletSide() { + if (!isSmallTablet()) { + int smallSide = Math.min(displaySize.x, displaySize.y); + int leftSide = smallSide * 35 / 100; + if (leftSide < dp(320)) { + leftSide = dp(320); + } + return smallSide - leftSide; + } else { + int smallSide = Math.min(displaySize.x, displaySize.y); + int maxSide = Math.max(displaySize.x, displaySize.y); + int leftSide = maxSide * 35 / 100; + if (leftSide < dp(320)) { + leftSide = dp(320); + } + return Math.min(smallSide, maxSide - leftSide); + } + } + + public static int getPhotoSize() { + if (photoSize == null) { + if (Build.VERSION.SDK_INT >= 16) { + photoSize = 1280; + } else { + photoSize = 800; + } + } + return photoSize; + } + + public static String formatTTLString(int ttl) { + if (ttl < 60) { + return LocaleController.formatPluralString("Seconds", ttl); + } else if (ttl < 60 * 60) { + return LocaleController.formatPluralString("Minutes", ttl / 60); + } else if (ttl < 60 * 60 * 24) { + return LocaleController.formatPluralString("Hours", ttl / 60 / 60); + } else if (ttl < 60 * 60 * 24 * 7) { + return LocaleController.formatPluralString("Days", ttl / 60 / 60 / 24); + } else { + int days = ttl / 60 / 60 / 24; + if (ttl % 7 == 0) { + return LocaleController.formatPluralString("Weeks", days / 7); + } else { + return String.format("%s %s", LocaleController.formatPluralString("Weeks", days / 7), LocaleController.formatPluralString("Days", days % 7)); + } + } + } + + public static AlertDialog.Builder buildTTLAlert(final Context context, final TLRPC.EncryptedChat encryptedChat) { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(LocaleController.getString("MessageLifetime", R.string.MessageLifetime)); + final NumberPicker numberPicker = new NumberPicker(context); + numberPicker.setMinValue(0); + numberPicker.setMaxValue(20); + if (encryptedChat.ttl > 0 && encryptedChat.ttl < 16) { + numberPicker.setValue(encryptedChat.ttl); + } else if (encryptedChat.ttl == 30) { + numberPicker.setValue(16); + } else if (encryptedChat.ttl == 60) { + numberPicker.setValue(17); + } else if (encryptedChat.ttl == 60 * 60) { + numberPicker.setValue(18); + } else if (encryptedChat.ttl == 60 * 60 * 24) { + numberPicker.setValue(19); + } else if (encryptedChat.ttl == 60 * 60 * 24 * 7) { + numberPicker.setValue(20); + } else if (encryptedChat.ttl == 0) { + numberPicker.setValue(0); + } + numberPicker.setFormatter(new NumberPicker.Formatter() { + @Override + public String format(int value) { + if (value == 0) { + return LocaleController.getString("ShortMessageLifetimeForever", R.string.ShortMessageLifetimeForever); + } else if (value >= 1 && value < 16) { + return AndroidUtilities.formatTTLString(value); + } else if (value == 16) { + return AndroidUtilities.formatTTLString(30); + } else if (value == 17) { + return AndroidUtilities.formatTTLString(60); + } else if (value == 18) { + return AndroidUtilities.formatTTLString(60 * 60); + } else if (value == 19) { + return AndroidUtilities.formatTTLString(60 * 60 * 24); + } else if (value == 20) { + return AndroidUtilities.formatTTLString(60 * 60 * 24 * 7); + } + return ""; + } + }); + builder.setView(numberPicker); + builder.setNegativeButton(LocaleController.getString("Done", R.string.Done), new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + int oldValue = encryptedChat.ttl; + which = numberPicker.getValue(); + if (which >= 0 && which < 16) { + encryptedChat.ttl = which; + } else if (which == 16) { + encryptedChat.ttl = 30; + } else if (which == 17) { + encryptedChat.ttl = 60; + } else if (which == 18) { + encryptedChat.ttl = 60 * 60; + } else if (which == 19) { + encryptedChat.ttl = 60 * 60 * 24; + } else if (which == 20) { + encryptedChat.ttl = 60 * 60 * 24 * 7; + } + if (oldValue != encryptedChat.ttl) { + SecretChatHelper.getInstance().sendTTLMessage(encryptedChat, null); + MessagesStorage.getInstance().updateEncryptedChatTTL(encryptedChat); + } + } + }); + return builder; + } + + public static void clearCursorDrawable(EditText editText) { + if (editText == null || Build.VERSION.SDK_INT < 12) { + return; + } + try { + Field mCursorDrawableRes = TextView.class.getDeclaredField("mCursorDrawableRes"); + mCursorDrawableRes.setAccessible(true); + mCursorDrawableRes.setInt(editText, 0); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + + public static void setProgressBarAnimationDuration(ProgressBar progressBar, int duration) { + if (progressBar == null) { + return; + } + try { + Field mCursorDrawableRes = ProgressBar.class.getDeclaredField("mDuration"); + mCursorDrawableRes.setAccessible(true); + mCursorDrawableRes.setInt(progressBar, duration); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + + public static int getViewInset(View view) { + if (view == null || Build.VERSION.SDK_INT < 21) { + return 0; + } + try { + Field mAttachInfoField = View.class.getDeclaredField("mAttachInfo"); + mAttachInfoField.setAccessible(true); + Object mAttachInfo = mAttachInfoField.get(view); + if (mAttachInfo != null) { + Field mStableInsetsField = mAttachInfo.getClass().getDeclaredField("mStableInsets"); + mStableInsetsField.setAccessible(true); + Rect insets = (Rect)mStableInsetsField.get(mAttachInfo); + return insets.bottom; + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + return 0; + } + + public static Point getRealScreenSize() { + Point size = new Point(); + try { + WindowManager windowManager = (WindowManager) ApplicationLoader.applicationContext.getSystemService(Context.WINDOW_SERVICE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + windowManager.getDefaultDisplay().getRealSize(size); + } else { + try { + Method mGetRawW = Display.class.getMethod("getRawWidth"); + Method mGetRawH = Display.class.getMethod("getRawHeight"); + size.set((Integer) mGetRawW.invoke(windowManager.getDefaultDisplay()), (Integer) mGetRawH.invoke(windowManager.getDefaultDisplay())); + } catch (Exception e) { + size.set(windowManager.getDefaultDisplay().getWidth(), windowManager.getDefaultDisplay().getHeight()); + FileLog.e("tmessages", e); + } + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + return size; + } + + public static void setListViewEdgeEffectColor(AbsListView listView, int color) { + if (Build.VERSION.SDK_INT >= 21) { + try { + Field field = AbsListView.class.getDeclaredField("mEdgeGlowTop"); + field.setAccessible(true); + EdgeEffect mEdgeGlowTop = (EdgeEffect) field.get(listView); + if (mEdgeGlowTop != null) { + mEdgeGlowTop.setColor(color); + } + + field = AbsListView.class.getDeclaredField("mEdgeGlowBottom"); + field.setAccessible(true); + EdgeEffect mEdgeGlowBottom = (EdgeEffect) field.get(listView); + if (mEdgeGlowBottom != null) { + mEdgeGlowBottom.setColor(color); + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + } + + @SuppressLint("NewApi") + public static void clearDrawableAnimation(View view) { + if (Build.VERSION.SDK_INT < 21 || view == null) { + return; + } + Drawable drawable; + if (view instanceof ListView) { + drawable = ((ListView) view).getSelector(); + if (drawable != null) { + drawable.setState(StateSet.NOTHING); + } + } else { + drawable = view.getBackground(); + if (drawable != null) { + drawable.setState(StateSet.NOTHING); + drawable.jumpToCurrentState(); + } + } + } + + public static final int FLAG_TAG_BR = 1; + public static final int FLAG_TAG_BOLD = 2; + public static final int FLAG_TAG_COLOR = 4; + public static final int FLAG_TAG_ALL = FLAG_TAG_BR | FLAG_TAG_BOLD | FLAG_TAG_COLOR; + + public static Spannable replaceTags(String str) { + return replaceTags(str, FLAG_TAG_ALL); + } + + public static Spannable replaceTags(String str, int flag) { + try { + int start; + int end; + StringBuilder stringBuilder = new StringBuilder(str); + if ((flag & FLAG_TAG_BR) != 0) { + while ((start = stringBuilder.indexOf("
")) != -1) { + stringBuilder.replace(start, start + 4, "\n"); + } + while ((start = stringBuilder.indexOf("
")) != -1) { + stringBuilder.replace(start, start + 5, "\n"); + } + } + ArrayList bolds = new ArrayList<>(); + if ((flag & FLAG_TAG_BOLD) != 0) { + while ((start = stringBuilder.indexOf("")) != -1) { + stringBuilder.replace(start, start + 3, ""); + end = stringBuilder.indexOf(""); + if (end == -1) { + end = stringBuilder.indexOf(""); + } + stringBuilder.replace(end, end + 4, ""); + bolds.add(start); + bolds.add(end); + } + } + ArrayList colors = new ArrayList<>(); + if ((flag & FLAG_TAG_COLOR) != 0) { + while ((start = stringBuilder.indexOf("", start); + int color = Color.parseColor(stringBuilder.substring(start, end)); + stringBuilder.replace(start, end + 1, ""); + end = stringBuilder.indexOf(""); + stringBuilder.replace(end, end + 4, ""); + colors.add(start); + colors.add(end); + colors.add(color); + } + } + SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(stringBuilder); + for (int a = 0; a < bolds.size() / 2; a++) { + spannableStringBuilder.setSpan(new TypefaceSpan(AndroidUtilities.getTypeface("fonts/rmedium.ttf")), bolds.get(a * 2), bolds.get(a * 2 + 1), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + for (int a = 0; a < colors.size() / 3; a++) { + spannableStringBuilder.setSpan(new ForegroundColorSpan(colors.get(a * 3 + 2)), colors.get(a * 3), colors.get(a * 3 + 1), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + return spannableStringBuilder; + } catch (Exception e) { + FileLog.e("tmessages", e); + } + return new SpannableStringBuilder(str); + } + + public static boolean needShowPasscode(boolean reset) { + boolean wasInBackground; + if (Build.VERSION.SDK_INT >= 14) { + wasInBackground = ForegroundDetector.getInstance().isWasInBackground(reset); + if (reset) { + ForegroundDetector.getInstance().resetBackgroundVar(); + } + } else { + wasInBackground = UserConfig.lastPauseTime != 0; + } + return UserConfig.passcodeHash.length() > 0 && wasInBackground && + (UserConfig.appLocked || UserConfig.autoLockIn != 0 && UserConfig.lastPauseTime != 0 && !UserConfig.appLocked && (UserConfig.lastPauseTime + UserConfig.autoLockIn) <= ConnectionsManager.getInstance().getCurrentTime()); + } + + public static void shakeView(final View view, final float x, final int num) { + if (num == 6) { + ViewProxy.setTranslationX(view, 0); + view.clearAnimation(); + return; + } + AnimatorSetProxy animatorSetProxy = new AnimatorSetProxy(); + animatorSetProxy.playTogether(ObjectAnimatorProxy.ofFloat(view, "translationX", AndroidUtilities.dp(x))); + animatorSetProxy.setDuration(50); + animatorSetProxy.addListener(new AnimatorListenerAdapterProxy() { + @Override + public void onAnimationEnd(Object animation) { + shakeView(view, num == 5 ? 0 : -x, num + 1); + } + }); + animatorSetProxy.start(); + } + + + + *//*public static String ellipsize(String text, int maxLines, int maxWidth, TextPaint paint) { + if (text == null || paint == null) { + return null; + } + int count; + int offset = 0; + StringBuilder result = null; + TextView + for (int a = 0; a < maxLines; a++) { + count = paint.breakText(text, true, maxWidth, null); + if (a != maxLines - 1) { + if (result == null) { + result = new StringBuilder(count * maxLines + 1); + } + boolean foundSpace = false; + for (int c = count - 1; c >= offset; c--) { + if (text.charAt(c) == ' ') { + foundSpace = true; + result.append(text.substring(offset, c - 1)); + offset = c - 1; + } + } + if (!foundSpace) { + offset = count; + } + text = text.substring(0, offset); + } else if (maxLines == 1) { + return text.substring(0, count); + } else { + result.append(text.substring(0, count)); + } + } + return result.toString(); + }*//* + + *//*public static void turnOffHardwareAcceleration(Window window) { + if (window == null || Build.MODEL == null || Build.VERSION.SDK_INT < 11) { + return; + } + if (Build.MODEL.contains("GT-S5301") || + Build.MODEL.contains("GT-S5303") || + Build.MODEL.contains("GT-B5330") || + Build.MODEL.contains("GT-S5302") || + Build.MODEL.contains("GT-S6012B") || + Build.MODEL.contains("MegaFon_SP-AI")) { + window.clearFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED); + } + }*//* + + public static void checkForCrashes(Activity context) { + CrashManager.register(context, BuildVars.DEBUG_VERSION ? BuildVars.HOCKEY_APP_HASH_DEBUG : BuildVars.HOCKEY_APP_HASH, new CrashManagerListener() { + @Override + public boolean includeDeviceData() { + return true; + } + }); + } + + public static void checkForUpdates(Activity context) { + if (BuildVars.DEBUG_VERSION) { + UpdateManager.register(context, BuildVars.DEBUG_VERSION ? BuildVars.HOCKEY_APP_HASH_DEBUG : BuildVars.HOCKEY_APP_HASH); + } + } + + public static void unregisterUpdates() { + if (BuildVars.DEBUG_VERSION) { + UpdateManager.unregister(); + } + } + + public static void addMediaToGallery(String fromPath) { + if (fromPath == null) { + return; + } + File f = new File(fromPath); + Uri contentUri = Uri.fromFile(f); + addMediaToGallery(contentUri); + } + + public static void addMediaToGallery(Uri uri) { + if (uri == null) { + return; + } + try { + Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); + mediaScanIntent.setData(uri); + ApplicationLoader.applicationContext.sendBroadcast(mediaScanIntent); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + + private static File getAlbumDir() { + if (Build.VERSION.SDK_INT >= 23 && ApplicationLoader.applicationContext.checkSelfPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + return FileLoader.getInstance().getDirectory(FileLoader.MEDIA_DIR_CACHE); + } + File storageDir = null; + if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { + storageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "Telegram"); + if (!storageDir.mkdirs()) { + if (!storageDir.exists()){ + FileLog.d("tmessages", "failed to create directory"); + return null; + } + } + } else { + FileLog.d("tmessages", "External storage is not mounted READ/WRITE."); + } + + return storageDir; + } + + public static File generatePicturePath() { + try { + File storageDir = getAlbumDir(); + String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date()); + return new File(storageDir, "IMG_" + timeStamp + ".jpg"); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + return null; + } + + public static CharSequence generateSearchName(String name, String name2, String q) { + if (name == null && name2 == null) { + return ""; + } + SpannableStringBuilder builder = new SpannableStringBuilder(); + String wholeString = name; + if (wholeString == null || wholeString.length() == 0) { + wholeString = name2; + } else if (name2 != null && name2.length() != 0) { + wholeString += " " + name2; + } + wholeString = wholeString.trim(); + String lower = " " + wholeString.toLowerCase(); + + int index; + int lastIndex = 0; + while ((index = lower.indexOf(" " + q, lastIndex)) != -1) { + int idx = index - (index == 0 ? 0 : 1); + int end = q.length() + (index == 0 ? 0 : 1) + idx; + + if (lastIndex != 0 && lastIndex != idx + 1) { + builder.append(wholeString.substring(lastIndex, idx)); + } else if (lastIndex == 0 && idx != 0) { + builder.append(wholeString.substring(0, idx)); + } + + String query = wholeString.substring(idx, end); + if (query.startsWith(" ")) { + builder.append(" "); + } + query = query.trim(); + builder.append(AndroidUtilities.replaceTags("" + query + "")); + + lastIndex = end; + } + + if (lastIndex != -1 && lastIndex != wholeString.length()) { + builder.append(wholeString.substring(lastIndex, wholeString.length())); + } + + return builder; + } + + public static File generateVideoPath() { + try { + File storageDir = getAlbumDir(); + String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date()); + return new File(storageDir, "VID_" + timeStamp + ".mp4"); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + return null; + } + + public static String formatFileSize(long size) { + if (size < 1024) { + return String.format("%d B", size); + } else if (size < 1024 * 1024) { + return String.format("%.1f KB", size / 1024.0f); + } else if (size < 1024 * 1024 * 1024) { + return String.format("%.1f MB", size / 1024.0f / 1024.0f); + } else { + return String.format("%.1f GB", size / 1024.0f / 1024.0f / 1024.0f); + } + } + + public static byte[] decodeQuotedPrintable(final byte[] bytes) { + if (bytes == null) { + return null; + } + final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + for (int i = 0; i < bytes.length; i++) { + final int b = bytes[i]; + if (b == '=') { + try { + final int u = Character.digit((char) bytes[++i], 16); + final int l = Character.digit((char) bytes[++i], 16); + buffer.write((char) ((u << 4) + l)); + } catch (Exception e) { + FileLog.e("tmessages", e); + return null; + } + } else { + buffer.write(b); + } + } + byte[] array = buffer.toByteArray(); + try { + buffer.close(); + } catch (Exception e) { + FileLog.e("tmessages", e); + } + return array; + } + + public static boolean copyFile(InputStream sourceFile, File destFile) throws IOException { + OutputStream out = new FileOutputStream(destFile); + byte[] buf = new byte[4096]; + int len; + while ((len = sourceFile.read(buf)) > 0) { + Thread.yield(); + out.write(buf, 0, len); + } + out.close(); + return true; + } + + */ + + public static boolean copyFile(File sourceFile, File destFile) throws IOException { + if (!destFile.exists()) { + destFile.createNewFile(); + } + FileInputStream source = null; + FileOutputStream destination = null; + try { + source = new FileInputStream(sourceFile); + destination = new FileOutputStream(destFile); + destination.getChannel().transferFrom(source.getChannel(), 0, source.getChannel().size()); + } catch (Exception e) { + return false; + } finally { + if (source != null) { + source.close(); + } + if (destination != null) { + destination.close(); + } + } + return true; + } + + public static boolean isExternalStorageDocument(Uri uri) { + return "com.android.externalstorage.documents".equals(uri.getAuthority()); + } + + public static boolean isDownloadsDocument(Uri uri) { + return "com.android.providers.downloads.documents".equals(uri.getAuthority()); + } + + public static boolean isMediaDocument(Uri uri) { + return "com.android.providers.media.documents".equals(uri.getAuthority()); + } + + @SuppressLint("NewApi") + public static String getPath(final Uri uri) { + try { + final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; + if (isKitKat && DocumentsContract.isDocumentUri(ContextHolder.getContext(), uri)) { + /*if (isExternalStorageDocument(uri)) { + final String docId = DocumentsContract.getDocumentId(uri); + final String[] split = docId.split(":"); + final String type = split[0]; + if ("primary".equalsIgnoreCase(type)) { + return Environment.getExternalStorageDirectory() + "/" + split[1]; + } + } else if (isDownloadsDocument(uri)) { + final String id = DocumentsContract.getDocumentId(uri); + final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); + return getDataColumn(ApplicationLoader.applicationContext, contentUri, null, null); + } else*/ + if (isMediaDocument(uri)) { + final String docId = DocumentsContract.getDocumentId(uri); + final String[] split = docId.split(":"); + final String type = split[0]; + + Uri contentUri = null; + switch (type) { + case "image": + contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; + break; + /* case "video": + contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; + break; + case "audio": + contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; + break;*/ + } + + final String selection = "_id=?"; + final String[] selectionArgs = new String[]{ + split[1] + }; + + return getDataColumn(ContextHolder.getContext(), contentUri, selection, selectionArgs); + } + } else if ("content".equalsIgnoreCase(uri.getScheme())) { + return getDataColumn(ContextHolder.getContext(), uri, null, null); + } else if ("file".equalsIgnoreCase(uri.getScheme())) { + return uri.getPath(); + } + } catch (Exception e) { + //FileLog.e("tmessages", e); + } + return null; + } + + public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { + + Cursor cursor = null; + final String column = "_data"; + final String[] projection = { + column + }; + + try { + cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); + if (cursor != null && cursor.moveToFirst()) { + final int column_index = cursor.getColumnIndexOrThrow(column); + return cursor.getString(column_index); + } + } catch (Exception e) { + //FileLog.e("tmessages", e); + } finally { + if (cursor != null) { + cursor.close(); + } + } + return null; + } + + public static boolean isWaitingForSms() { + boolean value; + synchronized (smsLock) { + value = waitingForSms; + } + return value; + } + + public static void setWaitingForSms(boolean value) { + synchronized (smsLock) { + waitingForSms = value; + } + } + + public static void showKeyboard(View view) { + if (view == null) { + return; + } + InputMethodManager inputManager = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + inputManager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT); + } + + public static boolean isKeyboardShowed(View view) { + if (view == null) { + return false; + } + InputMethodManager inputManager = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + return inputManager.isActive(view); + } + + public static void hideKeyboard(View view) { + if (view == null) { + return; + } + InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + if (!imm.isActive()) { + return; + } + imm.hideSoftInputFromWindow(view.getWindowToken(), 0); + } + + public static void rateInCafeBazaar(Activity activity) { + Intent intent = new Intent(Intent.ACTION_EDIT); + intent.setData(Uri.parse(SystemPreferences.MarketPackages.Bazaar.pageAddressPrefix + BuildConfig.APPLICATION_ID)); + intent.setPackage(SystemPreferences.MarketPackages.Bazaar.packageName); + activity.startActivity(intent); + } + + private static void rateInMyket(Activity activity) { + String url= SystemPreferences.MarketPackages.Myket.pageAddressPrefix + BuildConfig.APPLICATION_ID + ";refId=app;end"; + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(url)); + activity.startActivity(intent); + } + + /** + * Not working to debug mode. see code comment + * @param activity + */ + // in debug mode there s postfix for application_id which makes finding app in google play impossible + public static void rateInGooglePlay(Activity activity) { + Intent rateIntent = new Intent(Intent.ACTION_VIEW, + Uri.parse(SystemPreferences.MarketPackages.MARKET_ADDRESS_PREFIX + BuildConfig.APPLICATION_ID)); + boolean marketFound = false; + + // find all applications able to handle our rateIntent + final List otherApps = activity.getPackageManager().queryIntentActivities(rateIntent, 0); + for (ResolveInfo otherApp: otherApps) { + // look for Google Play application + if (otherApp.activityInfo.applicationInfo.packageName.equals(SystemPreferences.MarketPackages.GooglePlay.packageName)) { + + ActivityInfo otherAppActivity = otherApp.activityInfo; + ComponentName componentName = new ComponentName( + otherAppActivity.applicationInfo.packageName, + otherAppActivity.name + ); + rateIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + rateIntent.setComponent(componentName); + activity.startActivity(rateIntent); + marketFound = true; + break; + } + } + + // if GP not present on device, open web browser + if (!marketFound) { + Intent webIntent = new Intent(Intent.ACTION_VIEW, + Uri.parse(SystemPreferences.MarketPackages.GooglePlay.pageAddressPrefix + BuildConfig.APPLICATION_ID)); + activity.startActivity(webIntent); + } + } + + // TODO: horrible code! Refactor it for God's sake. + public static void rateApplication(Activity activity, SystemPreferences.MarketPackages market) { + // if the suggested market is not installed, rate it in order of + // bazaar, myket, googlePlay + if (market != null && isPackageInstalled(market.packageName, activity)) { + switch (market) { + case Bazaar: + rateInCafeBazaar(activity); + break; + case Myket: + rateInMyket(activity); + break; + case GooglePlay: + rateInGooglePlay(activity); + break; + } + }else if (isPackageInstalled(SystemPreferences.MarketPackages.Myket.packageName, activity)){ + rateInMyket(activity); + }else if (isPackageInstalled(SystemPreferences.MarketPackages.GooglePlay.packageName, activity)){ + rateInGooglePlay(activity); + }else if (isPackageInstalled(SystemPreferences.MarketPackages.Bazaar.packageName, activity)){ + rateInCafeBazaar(activity); + }else{ + // must not happen + Logger.toastError(R.string.error_no_market_found); + } + /*for (String marketPackage : new String[]{})*/ + } + + private static boolean isPackageInstalled(String packageName, Context context) { + PackageManager pm = context.getPackageManager(); + try { + pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES); + return true; + } catch (PackageManager.NameNotFoundException e) { + return false; + } + } + + + /** ask user to grant the needed permission + * + * @return true if permission already assigned otherwise false. + */ + /*public static boolean getPermissionFromUserResult(BaseActivity activity, String permission, int requestCode){ + if (!hasPermission(activity, permission)){ + ActivityCompat.requestPermissions(activity, new String[]{permission}, requestCode); + return false; + } + return true; + }*/ + +} \ No newline at end of file diff --git a/src/main/java/com/shaya/poinila/android/presentation/OneSignalReceiver.java b/src/main/java/com/shaya/poinila/android/presentation/OneSignalReceiver.java new file mode 100755 index 0000000..b019202 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/OneSignalReceiver.java @@ -0,0 +1,17 @@ +package com.shaya.poinila.android.presentation; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; +import android.widget.Toast; + +/** + * Created by iran on 6/11/2016. + */ +public class OneSignalReceiver extends BroadcastReceiver{ + @Override + public void onReceive(Context context, Intent intent) { +// Log.i(getClass().getName(), "OneSignalReceiver"); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/PageChanger.java b/src/main/java/com/shaya/poinila/android/presentation/PageChanger.java new file mode 100755 index 0000000..8d66dc1 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/PageChanger.java @@ -0,0 +1,254 @@ +package com.shaya.poinila.android.presentation; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.text.TextUtils; + +import com.raizlabs.android.dbflow.annotation.NotNull; +import com.shaya.poinila.android.presentation.view.activity.ChromeActivity; +import com.shaya.poinila.android.presentation.view.activity.CollectionListActivity; +import com.shaya.poinila.android.presentation.view.activity.CommentsListActivity; +import com.shaya.poinila.android.presentation.view.activity.FullImageActivity; +import com.shaya.poinila.android.presentation.view.activity.HelpActivity; +import com.shaya.poinila.android.presentation.view.activity.MainActivity; +import com.shaya.poinila.android.presentation.view.activity.MemberListActivity; +import com.shaya.poinila.android.presentation.view.activity.NewPostActivity; +import com.shaya.poinila.android.presentation.view.activity.NotificationOpenedActivity; +import com.shaya.poinila.android.presentation.view.activity.OthersProfileActivity; +import com.shaya.poinila.android.presentation.view.activity.PostListActivity; +import com.shaya.poinila.android.presentation.view.activity.SelectInterestActivity; +import com.shaya.poinila.android.presentation.view.activity.SignUpLoginActivity; +import com.shaya.poinila.android.presentation.view.activity.WebviewActivity; +import com.shaya.poinila.android.presentation.view.dialog.NewPostDialog; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.util.NavigationUtils; +import com.shaya.poinila.android.utils.PushNotificationUtils; +import com.shaya.poinila.android.utils.Utils; + +import data.model.Collection; +import data.model.Member; +import data.model.Post; +import data.model.SuggestedWebPagePost; +import manager.DataRepository; + +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_CONTENT_URI; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_ENTITY; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_FIRST_LOGIN; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_ITEM_COUNT; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_MEMBER_ID; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_PAGE_TITLE_PARAMETER; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_POST_ID; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_REQUEST_ID; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_SECOND_ENTITY_ID; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_STARTED_FROM_SETTING; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_WEBSITE_URL; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_COLLECTION_POSTS; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_EXPLORE; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_MEMBER_POSTS; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_POST_LIKERS; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_POST_RELATED_POSTS; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_POST_REPOSTING_COLLECTIONS; + +/** + * Created by iran on 2015-09-27. + */ +public class PageChanger { + public static void goToProfile(Activity activity, Member member) { + DataRepository.getInstance().putTempModel(member); + NavigationUtils.goToActivity(OthersProfileActivity.class, + activity, KEY_MEMBER_ID, member.getId()); + } + + public static void goToProfile(Activity activity, String memberID) { + DataRepository.getInstance().putTempModel(null); + NavigationUtils.goToActivity(OthersProfileActivity.class, + activity, KEY_MEMBER_ID, memberID); + } + + public static void goToCollection(Activity activity, Collection collection) { + DataRepository.getInstance().putTempModel(collection); + goToCollection(activity, collection.getId(), collection.name, null); + } + + public static void goToCollection(Activity activity, @Nullable String collectionId, + @NotNull String collectionName, @Nullable String userName){ + Bundle bundle = new Bundle(); + bundle.putString(KEY_ENTITY, collectionId != null ? collectionId : collectionName); + bundle.putInt(KEY_REQUEST_ID, REQUEST_COLLECTION_POSTS); + bundle.putString(KEY_SECOND_ENTITY_ID, userName != null ? userName : collectionName); + NavigationUtils.goToActivity(PostListActivity.class, activity, bundle); + } + + /*public static void goToCollectionByName(Activity activity, String collectionName, String userName){ + Bundle bundle = new Bundle(); + bundle.putString(KEY_Entity, collectionName); + bundle.putInt(KEY_REQUEST_ID, REQUEST_COLLECTION_POSTS); + bundle.putString(KEY_PAGE_TITLE_PARAMETER, collectionName); + bundle.putString(KEY_SECOND_ENTITY_ID, userName); + NavigationUtils.goToActivity(PostListActivity.class, activity, bundle); + }*/ + + public static void goToPost(Activity activity, Post post) { + DataRepository.getInstance().putTempModel(post); + goToPost(activity, post.getId()); + } + + public static void goToPost(Activity activity, String postID) { + Bundle bundle = new Bundle(); + bundle.putString(KEY_ENTITY, postID); + bundle.putInt(KEY_REQUEST_ID, REQUEST_POST_RELATED_POSTS); + NavigationUtils.goToActivity(PostListActivity.class, activity, bundle); + + /* BaseFragment fragment = PostListFragment.newInstance(postID, REQUEST_POST_RELATED_POSTS); + if (activity instanceof FragmentHostActivity) + ((FragmentHostActivity) activity).addFragment(fragment, true); + else { + NavigationUtils.goToActivity(FragmentHostActivity.class, activity); + DataRepository.getInstance().putTempModel(fragment); + }*/ + } + + public static void goToExplore(Activity activity, String tagText) { + Bundle bundle = new Bundle(); + bundle.putString(KEY_ENTITY, tagText); + bundle.putInt(KEY_REQUEST_ID, REQUEST_EXPLORE); + NavigationUtils.goToActivity(PostListActivity.class, activity, bundle); + } + + public static void goToLikersList(Activity activity, int faveCount, String postID) { + Bundle bundle = new Bundle(); + bundle.putString(KEY_ENTITY, postID); + bundle.putInt(KEY_REQUEST_ID, REQUEST_POST_LIKERS); + bundle.putInt(KEY_ITEM_COUNT, faveCount); + NavigationUtils.goToActivity(MemberListActivity.class, activity, bundle); + } + + public static void goToCommentList(Activity activity, int commentCount, String postID) { + Bundle bundle = new Bundle(); + bundle.putString(KEY_POST_ID, postID); + bundle.putInt(KEY_ITEM_COUNT, commentCount); + NavigationUtils.goToActivity(CommentsListActivity.class, activity, bundle); + } + + public static void goToRepostList(Activity activity, int repostCount, String postID) { + Bundle bundle = new Bundle(); + bundle.putString(KEY_ENTITY, postID); + bundle.putInt(KEY_REQUEST_ID, REQUEST_POST_REPOSTING_COLLECTIONS); + bundle.putInt(KEY_ITEM_COUNT, repostCount); + NavigationUtils.goToActivity(CollectionListActivity.class, activity, bundle); + } + + public static void gotToNotificationActivity(PushNotificationUtils.NOTIFICATION_TYPE type, String data) { + Bundle bundle = new Bundle(); + + bundle.putString("type", type.toString()); + if(!TextUtils.isEmpty(data)) + bundle.putString("data", data); + + Logger.log("push data = " + data, Logger.LEVEL_INFO); + + NavigationUtils.goToActivity(NotificationOpenedActivity.class, PoinilaApplication.getAppContext(), bundle, Intent.FLAG_ACTIVITY_NEW_TASK); + } + + public static void goToMemberPosts(Activity activity, String memberID, String memberName) { + Bundle bundle = new Bundle(); + bundle.putString(KEY_ENTITY, memberID); + bundle.putInt(KEY_REQUEST_ID, REQUEST_MEMBER_POSTS); + bundle.putString(KEY_SECOND_ENTITY_ID, memberName); + NavigationUtils.goToActivity(PostListActivity.class, activity, bundle); + } + + public static void goToHelpActivity(Activity activity, boolean startedFromSetting) { + Intent intent = NavigationUtils.makeNavigationIntent(HelpActivity.class, activity); + intent.putExtra(KEY_STARTED_FROM_SETTING, startedFromSetting); + if (!startedFromSetting) + intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); + activity.startActivity(intent); + } + + public static void goToLoginActivity(Activity activity) { + goToLoginActivity(activity, null); + } + + public static void goToLoginActivity(Activity activity, Uri shareUri) { + Intent intent = NavigationUtils.makeNavigationIntent(SignUpLoginActivity.class, activity); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); + if (shareUri != null) + intent.setData(shareUri); + activity.finish(); + activity.startActivity(intent); + } + + public static void goToInlineBrowser(Activity activity, String url, @NotNull String postId, String pageTitle) { + Bundle bundle = new Bundle(); + if (!TextUtils.isEmpty(pageTitle)) + bundle.putString(KEY_PAGE_TITLE_PARAMETER, pageTitle); + + if(!TextUtils.isEmpty(postId)) + bundle.putString(KEY_ENTITY, postId); + + bundle.putString(KEY_WEBSITE_URL, url); + if(Utils.getBrowserAvailablePackageName() == null){ + NavigationUtils.goToActivity(WebviewActivity.class, activity, bundle); + }else { + if(!TextUtils.isEmpty(postId)) + bundle.putString("referrer", getPostUrl(postId)); + NavigationUtils.goToActivity(ChromeActivity.class, activity, bundle); + } + } + + public static String getPostUrl(String postId){ + return ConstantsUtils.POINILA_SERVER_ADDRESS.concat("post/" + postId + "/"); + } + + //TODO problem in showing full screen dialogs + public static void goToNewPost(android.support.v4.app.FragmentManager fragmentManager, SuggestedWebPagePost webpagePost) { + /*Bundle bundle = new Bundle(); + bundle.putParcelable(ConstantsUtils.KEY_WEBPAGE_POST, webpagePost == null ? null : Parcels.wrap(webpagePost)); + NavigationUtils.goToActivity(NewPostActivity.class, activity, bundle);*/ + NewPostDialog.newInstance(webpagePost).show(fragmentManager, null); + } + + //TODO problem in showing full screen dialogs + public static void goToNewWebSitePost(Context context, SuggestedWebPagePost webpagePost) { + Bundle bundle = new Bundle(); + NavigationUtils.goToActivity(NewPostActivity.class, context, bundle); + } + + public static void goToSelectInterest(Activity activity, boolean firstLogin) { + Bundle bundle = new Bundle(); + bundle.putBoolean(KEY_FIRST_LOGIN, firstLogin); + NavigationUtils.goToActivity(SelectInterestActivity.class, activity, bundle); + } + + public static void goToSelectInterest(Context context, boolean firstLogin) { + Bundle bundle = new Bundle(); + bundle.putBoolean(KEY_FIRST_LOGIN, firstLogin); + NavigationUtils.goToActivity(SelectInterestActivity.class, context, bundle); + } + + public static void goToDashboard(Activity activity) { + Intent intent = NavigationUtils.makeNavigationIntent(MainActivity.class, activity); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); + activity.startActivity(intent); + activity.finish(); + } + + public static void goToDashboard(Context context) { + Intent intent = NavigationUtils.makeNavigationIntent(MainActivity.class, context); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } + + public static void goToFullImage(Activity activity, String imageAddress) { + Intent intent = NavigationUtils.makeNavigationIntent(FullImageActivity.class, activity); + intent.putExtra(KEY_CONTENT_URI, imageAddress); + activity.startActivity(intent); + } + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/PoinilaApplication.java b/src/main/java/com/shaya/poinila/android/presentation/PoinilaApplication.java new file mode 100755 index 0000000..517140e --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/PoinilaApplication.java @@ -0,0 +1,158 @@ +package com.shaya.poinila.android.presentation; + +import android.app.Application; +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.util.DisplayMetrics; +import android.util.Log; + +import com.crashlytics.android.Crashlytics; +import com.google.android.gms.analytics.GoogleAnalytics; +import com.google.android.gms.analytics.Tracker; +import com.onesignal.OneSignal; +import com.raizlabs.android.dbflow.config.FlowConfig; +import com.raizlabs.android.dbflow.config.FlowManager; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.ContextHolder; +import com.shaya.poinila.android.util.DeviceInfoUtils; +import com.shaya.poinila.android.presentation.notification.OneSignalNotificationOpenedHelper; + +import io.fabric.sdk.android.Fabric; +import org.json.JSONObject; + +import java.util.Locale; + +import data.PoinilaNetService; +import data.model.Member; +import manager.DBFacade; +import manager.DataRepository; +import uk.co.chrisjenx.calligraphy.CalligraphyConfig; + +/** + * Created by iran on 2015-06-03. + * + * @author Alireza Farahani + */ +public class PoinilaApplication extends Application { + + //private RefWatcher refWatcher; + + private static Context mContext; + + private static String authToken; + + private Tracker mTracker; + + @Override + public void onCreate() { + super.onCreate(); + final Fabric fabric = new Fabric.Builder(this) + .kits(new Crashlytics()) + .debuggable(true) + .build(); + Fabric.with(fabric); + ContextHolder.setContext(getApplicationContext()); + FlowManager.init(new FlowConfig.Builder(this).build()); //refWatcher = LeakCanary.install(this); + setLocale(); + //TypefaceUtil.overrideFont(getApplicationContext(), "SERIF", "fonts/BYekan.ttf"); + //TypefaceUtil.overrideFont(getApplicationContext(), "SERIF", "fonts/iransans.ttf"); + //TypefaceUtil.overrideFont(getApplicationContext(), "SERIF-LIGHT", "fonts/IRANSansLight.ttf"); + //TypefaceUtil.overrideFont(getApplicationContext(), "SERIF-BOLD", "fonts/IRANSansBold.ttf"); + CalligraphyConfig.initDefault(new CalligraphyConfig.Builder() + .setDefaultFontPath(getString(R.string.default_font_path)) + .setFontAttrId(R.attr.fontPath) + .build()); + //OneSignal.startInit(this).init(); + //setupCrashReport(); + + mContext = this; + + OneSignal + .startInit(this) + .setNotificationOpenedHandler(new OneSignalNotificationOpenedHelper(this)) +// .setAutoPromptLocation(true) + + .init(); + + OneSignal.enableInAppAlertNotification(false); + + + OneSignal.getTags(new OneSignal.GetTagsHandler() { + @Override + public void tagsAvailable(JSONObject tags) { + + } + }); + + + } + + public void logUser() { + // You can call any combination of these three methods + Member user = DBFacade.getCachedMyInfo(); + if(user != null){ + Crashlytics.setUserIdentifier(user.getId()); +// if(!TextUtils.isEmpty(user.email)) Crashlytics.setUserEmail(user.email); + Crashlytics.setUserName(user.uniqueName); + } + } + + /** + * Gets the default {@link Tracker} for this {@link Application}. + * @return tracker + */ + synchronized public Tracker getDefaultTracker() { + if (mTracker == null) { + GoogleAnalytics analytics = GoogleAnalytics.getInstance(this); + mTracker = analytics.newTracker(ConstantsUtils.GOOGLE_ANALYTICS_TRACKING_ID); + mTracker.enableAutoActivityTracking(true); + mTracker.setAppVersion(DeviceInfoUtils.CLIENT_VERSION_NAME); + } + return mTracker; + } + + + public static Context getAppContext(){ + return mContext; + } + + public static String getAuthToken(){ + return authToken; + } + + private void setupCrashReport() { + // Setup handler for uncaught exceptions. + Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { + @Override + public void uncaughtException(Thread thread, Throwable e) { + handleUncaughtException(thread, e); + } + }); + } + + private void handleUncaughtException(Thread thread, Throwable e) { + PoinilaNetService.sendReport(ConstantsUtils.REPORT_TYPE_BUG, e.getClass().getSimpleName(), Log.getStackTraceString(e)); + throw new RuntimeException(""); + /*android.os.Process.killProcess(android.os.Process.myPid()); + System.exit(1); // crash with "unfortunately stopped blah blah*/ + + } + + /* public static RefWatcher getRefWatcher(Context context) { + PoinilaApplication application = (PoinilaApplication) context.getApplicationContext(); + return application.refWatcher; + }*/ + public void setLocale() { + Locale myLocale = new Locale("fa"); + Resources res = getResources(); + DisplayMetrics dm = res.getDisplayMetrics(); + Configuration conf = res.getConfiguration(); + conf.locale = myLocale; + res.updateConfiguration(conf, dm); + } + + +} + + diff --git a/src/main/java/com/shaya/poinila/android/presentation/SmsReceiver.java b/src/main/java/com/shaya/poinila/android/presentation/SmsReceiver.java new file mode 100755 index 0000000..fa6b023 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/SmsReceiver.java @@ -0,0 +1,77 @@ +package com.shaya.poinila.android.presentation; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.telephony.SmsManager; +import android.telephony.SmsMessage; +import android.text.TextUtils; +import android.util.Log; + +import com.shaya.poinila.android.presentation.uievent.SmsReceivedEvent; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.Logger; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import manager.DataRepository; + +/** + * Created by iran on 12/8/2015. + */ +public class SmsReceiver extends BroadcastReceiver { + final SmsManager sms = SmsManager.getDefault(); + + @Override + public void onReceive(Context context, Intent intent) { + if (BuildConfig.DEBUG) { + Logger.toast("Sms Received"); + } + if (intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")) { + + Bundle bundle = intent.getExtras(); + SmsMessage[] msgs; + if (bundle != null) { + try { + Object[] pdus = (Object[]) bundle.get("pdus"); + msgs = new SmsMessage[pdus.length]; + String wholeString = ""; + for (int i = 0; i < msgs.length; i++) { + msgs[i] = SmsMessage.createFromPdu((byte[]) pdus[i]); + if(isPonilaNumber(msgs[i].getOriginatingAddress())){ + wholeString += msgs[i].getMessageBody(); + } + } + + if(!TextUtils.isEmpty(wholeString)){ +// Log.i(getClass().getName(), "wholeString = " + wholeString); + Pattern pattern = Pattern.compile("[0-9]+"); + final Matcher matcher = pattern.matcher(wholeString); + if (matcher.find()) { +// Log.i(getClass().getName(), "matcher.group(0) = " + matcher.group(0)); + String str = matcher.group(0); + if (str.length() >= 3) { + BusProvider.getBus().post(new SmsReceivedEvent(str)); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + + private boolean isPonilaNumber(String address){ + List numbers = DataRepository.getSMSProviderNumbers(); + for(String number : numbers){ + if(number.contains(address)) + return true; + } + return false; + + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/SortUtils.java b/src/main/java/com/shaya/poinila/android/presentation/SortUtils.java new file mode 100755 index 0000000..df318dd --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/SortUtils.java @@ -0,0 +1,36 @@ +package com.shaya.poinila.android.presentation; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import data.model.Timed; + +/** + * Created by iran on 2015-07-07. + */ +public class SortUtils { + private static Comparator timeComparator = new Comparator() { + @Override + public int compare(Object left, Object right) { + if (!(left instanceof Timed) || !(right instanceof Timed)) + throw new RuntimeException("objects must implement Timed interface"); + Timed lhs = (Timed) left; + Timed rhs = (Timed) right; + if (lhs == rhs) + return 0; + // newer item in less index + if (lhs.getCreationTime() > rhs.getCreationTime()) + return -1; + // older item in greate index + if (lhs.getCreationTime() < rhs.getCreationTime()) + return 1; + // same time + return 0; + } + }; + + public static void sortByTime(List list){ + Collections.sort(list, timeComparator); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/notification/OneSignalNotificationOpenedHelper.java b/src/main/java/com/shaya/poinila/android/presentation/notification/OneSignalNotificationOpenedHelper.java new file mode 100755 index 0000000..ec29cd0 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/notification/OneSignalNotificationOpenedHelper.java @@ -0,0 +1,51 @@ +package com.shaya.poinila.android.presentation.notification; + +import android.content.Context; +import android.util.Log; + +import com.onesignal.OneSignal; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import data.PoinilaNetService; +import data.model.FriendRequestAnswer; + +import static com.shaya.poinila.android.utils.PushNotificationUtils.*; + +/** + * Created by iran on 6/8/2016. + */ +public class OneSignalNotificationOpenedHelper implements OneSignal.NotificationOpenedHandler { + + + Context context; + + public OneSignalNotificationOpenedHelper(Context context){ + this.context = context; + } + + @Override + public void notificationOpened(String message, JSONObject additionalData, boolean isActive) { + + Log.i(getClass().getName(), "additionalData = " + additionalData); + Log.i(getClass().getName(), "message = " + message); + Log.i(getClass().getName(), "isActive = " + isActive); + + JSONArray notifArray = additionalData.optJSONArray("stacked_notifications"); + JSONObject objJS = null; + if(notifArray != null){ + try { + objJS = notifArray.length()> 0 ? notifArray.getJSONObject(0) : null; + } catch (JSONException e) { + e.printStackTrace(); + } + } + + fireNotificationAction(objJS == null ? additionalData: objJS); + + } + + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/presenter/NotificationAdapter.java b/src/main/java/com/shaya/poinila/android/presentation/presenter/NotificationAdapter.java new file mode 100755 index 0000000..0de2dbd --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/presenter/NotificationAdapter.java @@ -0,0 +1,102 @@ +package com.shaya.poinila.android.presentation.presenter; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.viewholder.BaseViewHolder; +import com.shaya.poinila.android.presentation.viewholder.CollectionNotifViewHolder; +import com.shaya.poinila.android.presentation.viewholder.MemberNotifViewHolder; +import com.shaya.poinila.android.presentation.viewholder.PostNotifViewHolder; + +import data.model.ImageUrls; +import data.model.Notification; + +/** + * Created by iran on 2015-08-15. + */ +public class NotificationAdapter extends RecyclerViewAdapter>{ + + //public static final int DATE_HEADER = 0; + public static final int INVITE_NOTIF = 1; + public static final int FRIENDSHIP_ACCEPTED_NOTIF = 2; + public static final int COMMENT_AFTER_YOUR_COMMENT = 3; + public static final int COMMENT_MY_POST = 4; + public static final int FRIENDS_CREATED_COLLECTIONS = 5; + public static final int FRIENDS_FOLLOWED_COLLECTIONS = 6; + public static final int FRIENDS_LIKED_POSTS = 7; + public static final int MY_COLLECTION_FOLLOWED = 8; + public static final int MY_POST_REPOSTED = 9; + public static final int MY_POST_LIKED = 10; + + public NotificationAdapter(Context context){ + super(context, -1); + } + + @Override + public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + switch (viewType){ + /*case DATE_HEADER: + return new DateHeaderViewHolder(mLayoutInflater.inflate(R.layout.title_date_separator, parent, false));*/ + case MY_POST_LIKED: + case COMMENT_AFTER_YOUR_COMMENT: + case COMMENT_MY_POST: + case MY_POST_REPOSTED: + return new PostNotifViewHolder(mLayoutInflater.inflate(R.layout.notif_post, parent, false)); + case MY_COLLECTION_FOLLOWED: + return new CollectionNotifViewHolder(mLayoutInflater.inflate(R.layout.notif_collection, parent, false)); + case FRIENDS_LIKED_POSTS: + return new MemberNotifViewHolder(mLayoutInflater.inflate(R.layout.notif_member, parent, false), ImageUrls.ImageType.POST); + case FRIENDS_CREATED_COLLECTIONS: + case FRIENDS_FOLLOWED_COLLECTIONS: + return new MemberNotifViewHolder(mLayoutInflater.inflate(R.layout.notif_member, parent, false), ImageUrls.ImageType.COLLECTION); + case FRIENDSHIP_ACCEPTED_NOTIF: + return new MemberNotifViewHolder(mLayoutInflater.inflate(R.layout.notif_member, parent, false), ImageUrls.ImageType.MEMBER); + case VIEW_TYPE_LOAD_PROGRESS: + return new BaseViewHolder.EmptyViewHolder(mLayoutInflater.inflate(R.layout.progress, parent, false)); + } + return null; + } + + @Override + protected BaseViewHolder getProperViewHolder(View v, int viewType) { + return null; + } + + @Override + public int getItemViewType(int position) { + /* else if (items.get(position) instanceof AcceptNotif){ + return FRIENDSHIP_ACCEPTED_NOTIF; + }*/ + /* else if (items.get(position) instanceof DateHeader) { + return DATE_HEADER; + }*/ + + int type = super.getItemViewType(position); + if(type == VIEW_TYPE_LOAD_PROGRESS){ + return super.getItemViewType(position); + } + + switch (getItem(position).type){ + case MY_POST_LIKED: + return MY_POST_LIKED; + case COMMENT_AFTER_YOUR_COMMENT: + return COMMENT_AFTER_YOUR_COMMENT; + case COMMENT_MY_POST: + return COMMENT_MY_POST; + case FRIENDS_CREATED_COLLECTIONS: + return FRIENDS_CREATED_COLLECTIONS; + case FRIENDS_FOLLOWED_COLLECTIONS: + return FRIENDS_FOLLOWED_COLLECTIONS; + case FRIENDS_LIKED_POSTS: + return FRIENDS_LIKED_POSTS; + case MY_COLLECTION_FOLLOWED: + return MY_COLLECTION_FOLLOWED; + case MY_POST_REPOSTED: + return MY_POST_REPOSTED; + case FRIENDSHIP_ACCEPTED: + return FRIENDSHIP_ACCEPTED_NOTIF; + } + return -1; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/presenter/RecyclerViewAdapter.java b/src/main/java/com/shaya/poinila/android/presentation/presenter/RecyclerViewAdapter.java new file mode 100755 index 0000000..8edf13d --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/presenter/RecyclerViewAdapter.java @@ -0,0 +1,194 @@ +package com.shaya.poinila.android.presentation.presenter; + +import android.content.Context; +import android.support.annotation.LayoutRes; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.StaggeredGridLayoutManager; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.viewholder.BaseViewHolder; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import data.database.PoinilaDataBase; +import data.model.Identifiable; +import data.model.Loading; +import data.model.Post; + +public abstract class RecyclerViewAdapter> extends RecyclerView.Adapter { + + public static final int VIEW_TYPE_LOAD_PROGRESS = -1; + protected static final int VIEW_TYPE_DATA_ROW = 100; + //private final Class mViewHolderClazz;loading + protected LayoutInflater mLayoutInflater; + private List items; + private int mItemLayoutID = -1; + + // Provide a reference to the views for each items item + // Complex items items may need more than one view per item, and + // you provide access to all the views for a items item in a view holder + //public static class ViewHolder extends RecyclerView.ViewHolder { + // each items item is just a string in this case + + // Provide a suitable constructor (depends on the kind of dataset) + + public RecyclerViewAdapter(Context context, @LayoutRes int itemLayoutID) { + super(); + Log.i(getClass().getName(), "context = " + context); + mLayoutInflater = LayoutInflater.from(context); + items = new ArrayList<>(); + mItemLayoutID = itemLayoutID; + } + + + // Create new views (invoked by the layout manager) + @Override + public VH onCreateViewHolder(ViewGroup parent, int viewType) { + + + if(viewType == VIEW_TYPE_LOAD_PROGRESS){ + View v = mLayoutInflater.inflate(R.layout.progress, parent, false); + return getProperViewHolder(v, viewType); + } + + View v = mLayoutInflater.inflate(mItemLayoutID, parent, false); + return getProperViewHolder(v, viewType); + } + + protected abstract VH getProperViewHolder(View v, int viewType); + + protected boolean isStaggeredGridLayoutManager(){ + return false; + } + + // Replace the contents of a view (invoked by the layout manager) + @Override + public void onBindViewHolder(VH holder, int position) { + + if (!(getItemViewType(position) == VIEW_TYPE_LOAD_PROGRESS)) { + holder.fill(getItem(position)); + } + + if(isStaggeredGridLayoutManager() && getItemViewType(position) == VIEW_TYPE_LOAD_PROGRESS){ + StaggeredGridLayoutManager.LayoutParams layoutParams = (StaggeredGridLayoutManager.LayoutParams) holder.itemView.getLayoutParams(); + layoutParams.setFullSpan(true); + } + + } + + // Return the size of your dataset (invoked by the layout manager) + @Override + public int getItemCount() { + return items.size(); + } + + @Override + public long getItemId(int position) { + //if (getItem(position) instanceof Identifiable) + if (hasStableIds()) + return Integer.parseInt(((Identifiable) getItem(position)).getId()); + return position; + } + + + public T getItem(int position){ + return items.get(position); + } + + public void addItem(T item, int position){ + if (item == null) + return; + items.add(position, item); + notifyItemInserted(position); + } + + public void addItem(T item){ + if (item == null) + return; + items.add(item); + + notifyItemInserted(items.size() - 1); + } + + public void setLoading(T item){ + if (item == null) + return; + items.add(item); + notifyItemInserted(items.size() - 1); + } + + + public void removeLoading(){ + int position = items.size() - 1; + removeItem(position); + notifyItemRemoved(position); + } + + public void addItems(List data){ + int oldSize = items.size(); + items.addAll(oldSize > 0 ? oldSize - 1 : oldSize, data); +// items.addAll(data); + notifyItemRangeInserted(oldSize, data.size()); + // TODO: moshkele inconsistency az ine guya + //notifyDataSetChanged(); + } + + public void addItemsToListHead(List data){ + items.addAll(0, data); + notifyItemRangeInserted(0, data.size()); + } + + public void setItem(T item, int position){ + items.set(position, item); + notifyItemChanged(position); + } + + + + public void resetData(List data) { + // don't use this.items = data snippet. it produces bug in saving suggestions came from server + this.items.clear(); + this.items.addAll(data); + notifyDataSetChanged(); + } + + public void removeItem(int position){ + items.remove(position); + notifyItemRemoved(position); + } + + public void clear(){ + this.items.clear(); + notifyDataSetChanged(); + } + + @Override + public int getItemViewType(int position) { + if (items.get(position) instanceof Loading) + return VIEW_TYPE_LOAD_PROGRESS; + return VIEW_TYPE_DATA_ROW; + } + + public List getItems(){ + return items; + } + + // Don't use it except you have no other choice! I assumed that lists have a single type entity in my design + // But in Dashboard page there's times we want to show a "please rate us" item which is not a post. + // 1- I could just add a fake post but it was not clean and triggered unwanted click events and blah blah + // 2- Replacing current mechanism with new one. It messed the code and produced lots of cast to Post + // 3- Ignoring generics and use the old api but in adding items to adapter I face the generic problem so I decided to just write a function without generic so be able to add anything other than post. + // sorry for long comment, here's your potato :)) + public List getUngenericedItems(){ + return items; + } + + public boolean isEmpty() { + return items.isEmpty() || (items.size() > 0 && items.get(0) instanceof Loading); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/presenter/RecyclerViewProvider.java b/src/main/java/com/shaya/poinila/android/presentation/presenter/RecyclerViewProvider.java new file mode 100755 index 0000000..5a8592e --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/presenter/RecyclerViewProvider.java @@ -0,0 +1,130 @@ +package com.shaya.poinila.android.presentation.presenter; + +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.StaggeredGridLayoutManager; + +import com.shaya.poinila.android.presentation.view.LoaderList; + +/** + * Created by iran on 2015-06-23. + * @author Alireza Farahani + */ +public class RecyclerViewProvider { + private RecyclerView.LayoutManager mLayoutManager; + private RecyclerView mRecyclerView; + private RecyclerViewAdapter mAdapter; + + public RecyclerViewProvider(RecyclerView recyclerView){ + mRecyclerView = recyclerView; + } + + /*public RecyclerViewProvider setRecyclerView(RecyclerView recyclerView){ + mRecyclerView = recyclerView; + return this; + }*/ + + /** + * + * @param direction use constans from {@link StaggeredGridLayoutManager} + * @param columnCount get value from xml resources + */ + public RecyclerViewProvider setStaggeredLayoutManager(int direction, int columnCount){ + mLayoutManager = new StaggeredGridLayoutManager(columnCount, direction); + //((StaggeredGridLayoutManager) mLayoutManager).setGapStrategy(GAP_HANDLING_NONE); + return this; + } + + /** + * + * @param direction use constans from {@link GridLayoutManager} + * @param columnCount get value from xml resources + * @return + */ + public RecyclerViewProvider setGridLayoutManager(int direction, int columnCount) { + mLayoutManager = new GridLayoutManager(null, columnCount, direction, false); +// mRecyclerView.setHasFixedSize(true); + return this; + } + + public RecyclerViewProvider setGridLayoutManager( + int direction, int columnCount, GridLayoutManager.SpanSizeLookup spanSizeLookup) { + ((GridLayoutManager)setGridLayoutManager(direction, columnCount).mLayoutManager) + .setSpanSizeLookup(spanSizeLookup); + return this; + } + + /** + * + * @param direction use constants from (@link LinearLayoutManager} + * @return + */ + public RecyclerViewProvider setLinearLayoutManager(int direction){ + return setLinearLayoutManager(direction, false); + } + + public RecyclerViewProvider setLinearLayoutManager(int direction, boolean reverse) { + mLayoutManager = new LinearLayoutManager(null, direction, reverse); + return this; + } + + public RecyclerViewProvider setAdapter(RecyclerViewAdapter adapter){ + mAdapter = adapter; + return this; + } + + /** + * Sets layoutmanager and adapter for already given recyclerview and then returns it + * @return + */ + public RecyclerView bindViewToAdapter() { + if (mRecyclerView != null) { + mRecyclerView.setLayoutManager(mLayoutManager); + mRecyclerView.setAdapter(mAdapter); + return mRecyclerView; + } + else return null; + } + + public static RecyclerView.OnScrollListener linearListEndDetectorListener(final RecyclerView.Adapter adapter, final LoaderList list) { + return new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + boolean endOfList = ((LinearLayoutManager)recyclerView.getLayoutManager()).findLastVisibleItemPosition() == + adapter.getItemCount() - 1; + if (endOfList && dy != 0){ + list.onLoadMore(); + } + } + }; + } + + public static RecyclerView.OnScrollListener staggeredListEndDetectorListener(final RecyclerView.Adapter adapter, final LoaderList list){ + return new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + int[] lastVisibleItems = ((StaggeredGridLayoutManager)recyclerView.getLayoutManager()). + findLastVisibleItemPositions(null); + int itemCount = adapter.getItemCount() - 1; + if ((lastVisibleItems[0] == itemCount || + lastVisibleItems[1] == itemCount) && dy != 0){ + list.onLoadMore(); + } + } + }; + } + + public static RecyclerView.OnScrollListener gridListEndDetectionListener(final RecyclerView.Adapter adapter, final LoaderList list){ + return new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + boolean endOfList = ((GridLayoutManager) recyclerView.getLayoutManager()).findLastVisibleItemPosition() == + adapter.getItemCount() - 1; + if (endOfList && dy != 0) { + list.onLoadMore(); + } + } + }; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/svgandroid/ParserHelper.java b/src/main/java/com/shaya/poinila/android/presentation/svgandroid/ParserHelper.java new file mode 100755 index 0000000..73e8cd6 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/svgandroid/ParserHelper.java @@ -0,0 +1,306 @@ +package com.shaya.poinila.android.presentation.svgandroid; + +/* + + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ +/** + * Parses numbers from SVG text. Based on the Batik Number Parser (Apache 2 License). + * + * @author Apache Software Foundation, Larva Labs LLC + */ +public class ParserHelper { + + private char current; + private CharSequence s; + public int pos; + private int n; + + public ParserHelper(CharSequence s, int pos) { + this.s = s; + this.pos = pos; + n = s.length(); + current = s.charAt(pos); + } + + private char read() { + if (pos < n) { + pos++; + } + if (pos == n) { + return '\0'; + } else { + return s.charAt(pos); + } + } + + public void skipWhitespace() { + while (pos < n) { + if (Character.isWhitespace(s.charAt(pos))) { + advance(); + } else { + break; + } + } + } + + public void skipNumberSeparator() { + while (pos < n) { + char c = s.charAt(pos); + switch (c) { + case ' ': + case ',': + case '\n': + case '\t': + advance(); + break; + default: + return; + } + } + } + + public void advance() { + current = read(); + } + + /** + * Parses the content of the buffer and converts it to a float. + */ + public float parseFloat() { + int mant = 0; + int mantDig = 0; + boolean mantPos = true; + boolean mantRead = false; + + int exp = 0; + int expDig = 0; + int expAdj = 0; + boolean expPos = true; + + switch (current) { + case '-': + mantPos = false; + // fallthrough + case '+': + current = read(); + } + + m1: switch (current) { + default: + return Float.NaN; + + case '.': + break; + + case '0': + mantRead = true; + l: for (;;) { + current = read(); + switch (current) { + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + break l; + case '.': case 'e': case 'E': + break m1; + default: + return 0.0f; + case '0': + } + } + + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + mantRead = true; + l: for (;;) { + if (mantDig < 9) { + mantDig++; + mant = mant * 10 + (current - '0'); + } else { + expAdj++; + } + current = read(); + switch (current) { + default: + break l; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + } + } + } + + if (current == '.') { + current = read(); + m2: switch (current) { + default: + case 'e': case 'E': + if (!mantRead) { + reportUnexpectedCharacterError( current ); + return 0.0f; + } + break; + + case '0': + if (mantDig == 0) { + l: for (;;) { + current = read(); + expAdj--; + switch (current) { + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + break l; + default: + if (!mantRead) { + return 0.0f; + } + break m2; + case '0': + } + } + } + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + l: for (;;) { + if (mantDig < 9) { + mantDig++; + mant = mant * 10 + (current - '0'); + expAdj--; + } + current = read(); + switch (current) { + default: + break l; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + } + } + } + } + + switch (current) { + case 'e': case 'E': + current = read(); + switch (current) { + default: + reportUnexpectedCharacterError( current ); + return 0f; + case '-': + expPos = false; + case '+': + current = read(); + switch (current) { + default: + reportUnexpectedCharacterError( current ); + return 0f; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + } + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + } + + en: switch (current) { + case '0': + l: for (;;) { + current = read(); + switch (current) { + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + break l; + default: + break en; + case '0': + } + } + + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + l: for (;;) { + if (expDig < 3) { + expDig++; + exp = exp * 10 + (current - '0'); + } + current = read(); + switch (current) { + default: + break l; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + } + } + } + default: + } + + if (!expPos) { + exp = -exp; + } + exp += expAdj; + if (!mantPos) { + mant = -mant; + } + + return buildFloat(mant, exp); + } + + private void reportUnexpectedCharacterError(char c) { + throw new RuntimeException("Unexpected char '" + c + "'."); + } + + /** + * Computes a float from mantissa and exponent. + */ + public static float buildFloat(int mant, int exp) { + if (exp < -125 || mant == 0) { + return 0.0f; + } + + if (exp >= 128) { + return (mant > 0) + ? Float.POSITIVE_INFINITY + : Float.NEGATIVE_INFINITY; + } + + if (exp == 0) { + return mant; + } + + if (mant >= (1 << 26)) { + mant++; // round up trailing bits if they will be dropped. + } + + return (float) ((exp > 0) ? mant * pow10[exp] : mant / pow10[-exp]); + } + + /** + * Array of powers of ten. Using double instead of float gives a tiny bit more precision. + */ + private static final double[] pow10 = new double[128]; + + static { + for (int i = 0; i < pow10.length; i++) { + pow10[i] = Math.pow(10, i); + } + } + + public float nextFloat() { + skipWhitespace(); + float f = parseFloat(); + skipNumberSeparator(); + return f; + } +} \ No newline at end of file diff --git a/src/main/java/com/shaya/poinila/android/presentation/svgandroid/SVG.java b/src/main/java/com/shaya/poinila/android/presentation/svgandroid/SVG.java new file mode 100755 index 0000000..29ce4cc --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/svgandroid/SVG.java @@ -0,0 +1,121 @@ +package com.shaya.poinila.android.presentation.svgandroid; + +import android.graphics.Picture; +import android.graphics.RectF; +import android.graphics.drawable.PictureDrawable; + +/* + + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ + +/** + * Describes a vector Picture object, and optionally its bounds. + * + * @author Larva Labs, LLC + */ +public class SVG { + + /** + * The parsed Picture object. + */ + private Picture picture; + + /** + * These are the bounds for the SVG specified as a hidden "bounds" layer in the SVG. + */ + private RectF bounds; + + /** + * These are the estimated bounds of the SVG computed from the SVG elements while parsing. + * Note that this could be null if there was a failure to compute limits (ie. an empty SVG). + */ + private RectF limits = null; + + /** + * Construct a new SVG. + * @param picture the parsed picture object. + * @param bounds the bounds computed from the "bounds" layer in the SVG. + */ + SVG(Picture picture, RectF bounds) { + this.picture = picture; + this.bounds = bounds; + } + + /** + * Set the limits of the SVG, which are the estimated bounds computed by the parser. + * @param limits the bounds computed while parsing the SVG, may not be entirely accurate. + */ + void setLimits(RectF limits) { + this.limits = limits; + } + + /** + * Create a picture drawable from the SVG. + * @return the PictureDrawable. + */ + public PictureDrawable createPictureDrawable() { + return new PictureDrawable(picture); +// return new PictureDrawable(picture) { +// @Override +// public int getIntrinsicWidth() { +// if (bounds != null) { +// return (int) bounds.width(); +// } else if (limits != null) { +// return (int) limits.width(); +// } else { +// return -1; +// } +// } +// +// @Override +// public int getIntrinsicHeight() { +// if (bounds != null) { +// return (int) bounds.height(); +// } else if (limits != null) { +// return (int) limits.height(); +// } else { +// return -1; +// } +// } +// }; + } + + /** + * Get the parsed SVG picture data. + * @return the picture. + */ + public Picture getPicture() { + return picture; + } + + /** + * Gets the bounding rectangle for the SVG, if one was specified. + * @return rectangle representing the bounds. + */ + public RectF getBounds() { + return bounds; + } + + /** + * Gets the bounding rectangle for the SVG that was computed upon parsing. It may not be entirely accurate for certain curves or transformations, but is often better than nothing. + * @return rectangle representing the computed bounds. + */ + public RectF getLimits() { + return limits; + } +} \ No newline at end of file diff --git a/src/main/java/com/shaya/poinila/android/presentation/svgandroid/SVGParseException.java b/src/main/java/com/shaya/poinila/android/presentation/svgandroid/SVGParseException.java new file mode 100755 index 0000000..6318a30 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/svgandroid/SVGParseException.java @@ -0,0 +1,21 @@ +package com.shaya.poinila.android.presentation.svgandroid; + +/** + * Runtime exception thrown when there is a problem parsing an SVG. + * + * @author Larva Labs, LLC + */ +public class SVGParseException extends RuntimeException { + + public SVGParseException(String s) { + super(s); + } + + public SVGParseException(String s, Throwable throwable) { + super(s, throwable); + } + + public SVGParseException(Throwable throwable) { + super(throwable); + } +} \ No newline at end of file diff --git a/src/main/java/com/shaya/poinila/android/presentation/svgandroid/SVGParser.java b/src/main/java/com/shaya/poinila/android/presentation/svgandroid/SVGParser.java new file mode 100755 index 0000000..db534dc --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/svgandroid/SVGParser.java @@ -0,0 +1,1316 @@ +package com.shaya.poinila.android.presentation.svgandroid; + +import android.content.res.AssetManager; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.LinearGradient; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Picture; +import android.graphics.RadialGradient; +import android.graphics.RectF; +import android.graphics.Shader; +import android.util.Log; + +import org.xml.sax.Attributes; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.DefaultHandler; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; + +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +/* + + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ + +/** + * Entry point for parsing SVG files for Android. + * Use one of the various static methods for parsing SVGs by resource, asset or input stream. + * Optionally, a single color can be searched and replaced in the SVG while parsing. + * You can also parse an svg path directly. + * + * @author Larva Labs, LLC + * @see #getSVGFromResource(Resources, int) + * @see #getSVGFromAsset(AssetManager, String) + * @see #getSVGFromString(String) + * @see #getSVGFromInputStream(InputStream) + * @see #parsePath(String) + */ +public class SVGParser { + + static final String TAG = "SVGAndroid"; + + /** + * Parse SVG data from an input stream. + * + * @param svgData the input stream, with SVG XML data in UTF-8 character encoding. + * @return the parsed SVG. + * @throws SVGParseException if there is an error while parsing. + */ + public static SVG getSVGFromInputStream(InputStream svgData) throws SVGParseException { + return SVGParser.parse(svgData, 0, 0, false); + } + + /** + * Parse SVG data from an input stream and scale to the specific size. + * @param svgData + * @param targetWidth + * @param targetHeight + * @return + * @throws SVGParseException + */ + public static SVG getSVGFromInputStream(InputStream svgData, int targetWidth, int targetHeight) + throws SVGParseException { + return SVGParser.parse(svgData, 0, 0, false, targetWidth, targetHeight); + } + + /** + * Parse SVG data from an Android application resource. + * + * @param resources the Android context resources. + * @param resId the ID of the raw resource SVG. + * @return the parsed SVG. + * @throws SVGParseException if there is an error while parsing. + */ + public static SVG getSVGFromResource(Resources resources, int resId) throws SVGParseException { + return SVGParser.parse(resources.openRawResource(resId), 0, 0, false); + } + + /** + * Parse SVG data from an Android application asset. + * + * @param assetMngr the Android asset manager. + * @param svgPath the path to the SVG file in the application's assets. + * @return the parsed SVG. + * @throws SVGParseException if there is an error while parsing. + * @throws IOException if there was a problem reading the file. + */ + public static SVG getSVGFromAsset(AssetManager assetMngr, String svgPath) throws SVGParseException, IOException { + InputStream inputStream = assetMngr.open(svgPath); + SVG svg = getSVGFromInputStream(inputStream); + inputStream.close(); + return svg; + } + + /** + * Parse SVG data from an input stream, replacing a single color with another color. + * + * @param svgData the input stream, with SVG XML data in UTF-8 character encoding. + * @param searchColor the color in the SVG to replace. + * @param replaceColor the color with which to replace the search color. + * @return the parsed SVG. + * @throws SVGParseException if there is an error while parsing. + */ + public static SVG getSVGFromInputStream(InputStream svgData, int searchColor, int replaceColor, + int targetWidth, int targetHeight) throws SVGParseException { + return SVGParser.parse(svgData, searchColor, replaceColor, false, targetWidth, targetHeight); + } + + /** + * Parse SVG data from a string. + * + * @param svgData the string containing SVG XML data. + * @param searchColor the color in the SVG to replace. + * @param replaceColor the color with which to replace the search color. + * @return the parsed SVG. + * @throws SVGParseException if there is an error while parsing. + */ + public static SVG getSVGFromString(String svgData, int searchColor, int replaceColor) throws SVGParseException { + return SVGParser.parse(new ByteArrayInputStream(svgData.getBytes()), searchColor, replaceColor, false); + } + + /** + * Parse SVG data from an Android application resource. + * + * @param resources the Android context + * @param resId the ID of the raw resource SVG. + * @param searchColor the color in the SVG to replace. + * @param replaceColor the color with which to replace the search color. + * @return the parsed SVG. + * @throws SVGParseException if there is an error while parsing. + */ + public static SVG getSVGFromResource(Resources resources, int resId, int searchColor, int replaceColor) throws SVGParseException { + return SVGParser.parse(resources.openRawResource(resId), searchColor, replaceColor, false); + } + + /** + * Parse SVG data from an Android application asset. + * + * @param assetMngr the Android asset manager. + * @param svgPath the path to the SVG file in the application's assets. + * @param searchColor the color in the SVG to replace. + * @param replaceColor the color with which to replace the search color. + * @return the parsed SVG. + * @throws SVGParseException if there is an error while parsing. + * @throws IOException if there was a problem reading the file. + */ + public static SVG getSVGFromAsset(AssetManager assetMngr, String svgPath, int searchColor, int replaceColor) throws SVGParseException, IOException { + InputStream inputStream = assetMngr.open(svgPath); + SVG svg = getSVGFromInputStream(inputStream, searchColor, replaceColor); + inputStream.close(); + return svg; + } + + /** + * Parses a single SVG path and returns it as a android.graphics.Path object. + * An example path is M250,150L150,350L350,350Z, which draws a triangle. + * + * @param pathString the SVG path, see the specification here. + */ + public static Path parsePath(String pathString) { + return doPath(pathString); + } + + private static SVG parse(InputStream in, Integer searchColor, Integer replaceColor, boolean whiteMode, + int targetWidth, int targetHeight) throws SVGParseException { +// Util.debug("Parsing SVG..."); + try { + long start = System.currentTimeMillis(); + SAXParserFactory spf = SAXParserFactory.newInstance(); + SAXParser sp = spf.newSAXParser(); + XMLReader xr = sp.getXMLReader(); + final Picture picture = new Picture(); + SVGHandler handler = new SVGHandler(picture, targetWidth, targetHeight); + handler.setColorSwap(searchColor, replaceColor); + handler.setWhiteMode(whiteMode); + xr.setContentHandler(handler); + xr.parse(new InputSource(in)); +// Util.debug("Parsing complete in " + (System.currentTimeMillis() - start) + " millis."); + SVG result = new SVG(picture, handler.bounds); + // Skip bounds if it was an empty pic + if (!Float.isInfinite(handler.limits.top)) { + result.setLimits(handler.limits); + } + return result; + } catch (Exception e) { + throw new SVGParseException(e); + } + } + + private static SVG parse(InputStream in, Integer searchColor, Integer replaceColor, boolean whiteMode) throws SVGParseException { + return parse(in, searchColor, replaceColor, whiteMode, 0, 0); + } + + private static NumberParse parseNumbers(String s) { + //Util.debug("Parsing numbers from: '" + s + "'"); + int n = s.length(); + int p = 0; + ArrayList numbers = new ArrayList(); + boolean skipChar = false; + for (int i = 1; i < n; i++) { + if (skipChar) { + skipChar = false; + continue; + } + char c = s.charAt(i); + switch (c) { + // This ends the parsing, as we are on the next element + case 'M': + case 'm': + case 'Z': + case 'z': + case 'L': + case 'l': + case 'H': + case 'h': + case 'V': + case 'v': + case 'C': + case 'c': + case 'S': + case 's': + case 'Q': + case 'q': + case 'T': + case 't': + case 'a': + case 'A': + case ')': { + String str = s.substring(p, i); + if (str.trim().length() > 0) { + //Util.debug(" Last: " + str); + Float f = Float.parseFloat(str); + numbers.add(f); + } + p = i; + return new NumberParse(numbers, p); + } + case '\n': + case '\t': + case ' ': + case ',': + case '-': { + String str = s.substring(p, i); + // Just keep moving if multiple whitespace + if (str.trim().length() > 0) { + //Util.debug(" Next: " + str); + Float f = Float.parseFloat(str); + numbers.add(f); + if (c == '-') { + p = i; + } else { + p = i + 1; + skipChar = true; + } + } else { + p++; + } + break; + } + } + } + String last = s.substring(p); + if (last.length() > 0) { + //Util.debug(" Last: " + last); + try { + numbers.add(Float.parseFloat(last)); + } catch (NumberFormatException nfe) { + // Just white-space, forget it + } + p = s.length(); + } + return new NumberParse(numbers, p); + } + + private static Matrix parseTransform(String s) { + if (s.startsWith("matrix(")) { + NumberParse np = parseNumbers(s.substring("matrix(".length())); + if (np.numbers.size() == 6) { + Matrix matrix = new Matrix(); + matrix.setValues(new float[]{ + // Row 1 + np.numbers.get(0), + np.numbers.get(2), + np.numbers.get(4), + // Row 2 + np.numbers.get(1), + np.numbers.get(3), + np.numbers.get(5), + // Row 3 + 0, + 0, + 1, + }); + return matrix; + } + } else if (s.startsWith("translate(")) { + NumberParse np = parseNumbers(s.substring("translate(".length())); + if (np.numbers.size() > 0) { + float tx = np.numbers.get(0); + float ty = 0; + if (np.numbers.size() > 1) { + ty = np.numbers.get(1); + } + Matrix matrix = new Matrix(); + matrix.postTranslate(tx, ty); + return matrix; + } + } else if (s.startsWith("scale(")) { + NumberParse np = parseNumbers(s.substring("scale(".length())); + if (np.numbers.size() > 0) { + float sx = np.numbers.get(0); + float sy = 0; + if (np.numbers.size() > 1) { + sy = np.numbers.get(1); + } + Matrix matrix = new Matrix(); + matrix.postScale(sx, sy); + return matrix; + } + } else if (s.startsWith("skewX(")) { + NumberParse np = parseNumbers(s.substring("skewX(".length())); + if (np.numbers.size() > 0) { + float angle = np.numbers.get(0); + Matrix matrix = new Matrix(); + matrix.postSkew((float) Math.tan(angle), 0); + return matrix; + } + } else if (s.startsWith("skewY(")) { + NumberParse np = parseNumbers(s.substring("skewY(".length())); + if (np.numbers.size() > 0) { + float angle = np.numbers.get(0); + Matrix matrix = new Matrix(); + matrix.postSkew(0, (float) Math.tan(angle)); + return matrix; + } + } else if (s.startsWith("rotate(")) { + NumberParse np = parseNumbers(s.substring("rotate(".length())); + if (np.numbers.size() > 0) { + float angle = np.numbers.get(0); + float cx = 0; + float cy = 0; + if (np.numbers.size() > 2) { + cx = np.numbers.get(1); + cy = np.numbers.get(2); + } + Matrix matrix = new Matrix(); + matrix.postTranslate(cx, cy); + matrix.postRotate(angle); + matrix.postTranslate(-cx, -cy); + return matrix; + } + } + return null; + } + + /** + * This is where the hard-to-parse paths are handled. + * Uppercase rules are absolute positions, lowercase are relative. + * Types of path rules: + *

+ *

    + *
  1. M/m - (x y)+ - Move to (without drawing) + *
  2. Z/z - (no params) - Close path (back to starting point) + *
  3. L/l - (x y)+ - Line to + *
  4. H/h - x+ - Horizontal ine to + *
  5. V/v - y+ - Vertical line to + *
  6. C/c - (x1 y1 x2 y2 x y)+ - Cubic bezier to + *
  7. S/s - (x2 y2 x y)+ - Smooth cubic bezier to (shorthand that assumes the x2, y2 from previous C/S is the x1, y1 of this bezier) + *
  8. Q/q - (x1 y1 x y)+ - Quadratic bezier to + *
  9. T/t - (x y)+ - Smooth quadratic bezier to (assumes previous control point is "reflection" of last one w.r.t. to current point) + *
+ *

+ * Numbers are separate by whitespace, comma or nothing at all (!) if they are self-delimiting, (ie. begin with a - sign) + * + * @param s the path string from the XML + */ + private static Path doPath(String s) { + int n = s.length(); + ParserHelper ph = new ParserHelper(s, 0); + ph.skipWhitespace(); + Path p = new Path(); + float lastX = 0; + float lastY = 0; + float lastX1 = 0; + float lastY1 = 0; + float subPathStartX = 0; + float subPathStartY = 0; + char prevCmd = 0; + while (ph.pos < n) { + char cmd = s.charAt(ph.pos); + switch (cmd) { + case '-': + case '+': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (prevCmd == 'm' || prevCmd == 'M') { + cmd = (char) (((int) prevCmd) - 1); + break; + } else if (prevCmd == 'c' || prevCmd == 'C') { + cmd = prevCmd; + break; + } else if (prevCmd == 'l' || prevCmd == 'L') { + cmd = prevCmd; + break; + } + default: { + ph.advance(); + prevCmd = cmd; + } + } + + boolean wasCurve = false; + switch (cmd) { + case 'M': + case 'm': { + float x = ph.nextFloat(); + float y = ph.nextFloat(); + if (cmd == 'm') { + subPathStartX += x; + subPathStartY += y; + p.rMoveTo(x, y); + lastX += x; + lastY += y; + } else { + subPathStartX = x; + subPathStartY = y; + p.moveTo(x, y); + lastX = x; + lastY = y; + } + break; + } + case 'Z': + case 'z': { + p.close(); + p.moveTo(subPathStartX, subPathStartY); + lastX = subPathStartX; + lastY = subPathStartY; + lastX1 = subPathStartX; + lastY1 = subPathStartY; + wasCurve = true; + break; + } + case 'L': + case 'l': { + float x = ph.nextFloat(); + float y = ph.nextFloat(); + if (cmd == 'l') { + p.rLineTo(x, y); + lastX += x; + lastY += y; + } else { + p.lineTo(x, y); + lastX = x; + lastY = y; + } + break; + } + case 'H': + case 'h': { + float x = ph.nextFloat(); + if (cmd == 'h') { + p.rLineTo(x, 0); + lastX += x; + } else { + p.lineTo(x, lastY); + lastX = x; + } + break; + } + case 'V': + case 'v': { + float y = ph.nextFloat(); + if (cmd == 'v') { + p.rLineTo(0, y); + lastY += y; + } else { + p.lineTo(lastX, y); + lastY = y; + } + break; + } + case 'C': + case 'c': { + wasCurve = true; + float x1 = ph.nextFloat(); + float y1 = ph.nextFloat(); + float x2 = ph.nextFloat(); + float y2 = ph.nextFloat(); + float x = ph.nextFloat(); + float y = ph.nextFloat(); + if (cmd == 'c') { + x1 += lastX; + x2 += lastX; + x += lastX; + y1 += lastY; + y2 += lastY; + y += lastY; + } + p.cubicTo(x1, y1, x2, y2, x, y); + lastX1 = x2; + lastY1 = y2; + lastX = x; + lastY = y; + break; + } + case 'S': + case 's': { + wasCurve = true; + float x2 = ph.nextFloat(); + float y2 = ph.nextFloat(); + float x = ph.nextFloat(); + float y = ph.nextFloat(); + if (cmd == 's') { + x2 += lastX; + x += lastX; + y2 += lastY; + y += lastY; + } + float x1 = 2 * lastX - lastX1; + float y1 = 2 * lastY - lastY1; + p.cubicTo(x1, y1, x2, y2, x, y); + lastX1 = x2; + lastY1 = y2; + lastX = x; + lastY = y; + break; + } + case 'A': + case 'a': { + float rx = ph.nextFloat(); + float ry = ph.nextFloat(); + float theta = ph.nextFloat(); + int largeArc = (int) ph.nextFloat(); + int sweepArc = (int) ph.nextFloat(); + float x = ph.nextFloat(); + float y = ph.nextFloat(); + drawArc(p, lastX, lastY, x, y, rx, ry, theta, largeArc, sweepArc); + lastX = x; + lastY = y; + break; + } + } + if (!wasCurve) { + lastX1 = lastX; + lastY1 = lastY; + } + ph.skipWhitespace(); + } + return p; + } + + private static void drawArc(Path p, float lastX, float lastY, float x, float y, float rx, float ry, float theta, int largeArc, int sweepArc) { + // todo - not implemented yet, may be very hard to do using Android drawing facilities. + } + + private static NumberParse getNumberParseAttr(String name, Attributes attributes) { + int n = attributes.getLength(); + for (int i = 0; i < n; i++) { + if (attributes.getLocalName(i).equals(name)) { + return parseNumbers(attributes.getValue(i)); + } + } + return null; + } + + private static String getStringAttr(String name, Attributes attributes) { + int n = attributes.getLength(); + for (int i = 0; i < n; i++) { + if (attributes.getLocalName(i).equals(name)) { + return attributes.getValue(i); + } + } + return null; + } + + private static Float getFloatAttr(String name, Attributes attributes) { + return getFloatAttr(name, attributes, null); + } + + private static Float getFloatAttr(String name, Attributes attributes, Float defaultValue) { + String v = getStringAttr(name, attributes); + if (v == null) { + return defaultValue; + } else { + if (v.endsWith("px")) { + v = v.substring(0, v.length() - 2); + } +// Log.d(TAG, "Float parsing '" + name + "=" + v + "'"); + return Float.parseFloat(v); + } + } + + private static Integer getHexAttr(String name, Attributes attributes) { + String v = getStringAttr(name, attributes); + //Util.debug("Hex parsing '" + name + "=" + v + "'"); + if (v == null) { + return null; + } else { + try { + return Integer.parseInt(v.substring(1), 16); + } catch (NumberFormatException nfe) { + // todo - parse word-based color here + return null; + } + } + } + + private static class NumberParse { + private ArrayList numbers; + private int nextCmd; + + public NumberParse(ArrayList numbers, int nextCmd) { + this.numbers = numbers; + this.nextCmd = nextCmd; + } + + public int getNextCmd() { + return nextCmd; + } + + public float getNumber(int index) { + return numbers.get(index); + } + + } + + private static class Gradient { + String id; + String xlink; + boolean isLinear; + float x1, y1, x2, y2; + float x, y, radius; + ArrayList positions = new ArrayList(); + ArrayList colors = new ArrayList(); + Matrix matrix = null; + + public Gradient createChild(Gradient g) { + Gradient child = new Gradient(); + child.id = g.id; + child.xlink = id; + child.isLinear = g.isLinear; + child.x1 = g.x1; + child.x2 = g.x2; + child.y1 = g.y1; + child.y2 = g.y2; + child.x = g.x; + child.y = g.y; + child.radius = g.radius; + child.positions = positions; + child.colors = colors; + child.matrix = matrix; + if (g.matrix != null) { + if (matrix == null) { + child.matrix = g.matrix; + } else { + Matrix m = new Matrix(matrix); + m.preConcat(g.matrix); + child.matrix = m; + } + } + return child; + } + } + + private static class StyleSet { + HashMap styleMap = new HashMap(); + + private StyleSet(String string) { + String[] styles = string.split(";"); + for (String s : styles) { + String[] style = s.split(":"); + if (style.length == 2) { + styleMap.put(style[0], style[1]); + } + } + } + + public String getStyle(String name) { + return styleMap.get(name); + } + } + + private static class Properties { + StyleSet styles = null; + Attributes atts; + + private Properties(Attributes atts) { + this.atts = atts; + String styleAttr = getStringAttr("style", atts); + if (styleAttr != null) { + styles = new StyleSet(styleAttr); + } + } + + public String getAttr(String name) { + String v = null; + if (styles != null) { + v = styles.getStyle(name); + } + if (v == null) { + v = getStringAttr(name, atts); + } + return v; + } + + public String getString(String name) { + return getAttr(name); + } + + public Integer getHex(String name) { + String v = getAttr(name); + if (v == null || !v.startsWith("#")) { + return null; + } else { + try { + return Integer.parseInt(v.substring(1), 16); + } catch (NumberFormatException nfe) { + // todo - parse word-based color here + return null; + } + } + } + + public Float getFloat(String name, float defaultValue) { + Float v = getFloat(name); + if (v == null) { + return defaultValue; + } else { + return v; + } + } + + public Float getFloat(String name) { + String v = getAttr(name); + if (v == null) { + return null; + } else { + try { + return Float.parseFloat(v); + } catch (NumberFormatException nfe) { + return null; + } + } + } + } + + private static class SVGHandler extends DefaultHandler { + + Picture picture; + Canvas canvas; + Paint paint; + // Scratch rect (so we aren't constantly making new ones) + RectF rect = new RectF(); + RectF bounds = null; + RectF limits = new RectF(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY); + + Integer searchColor = null; + Integer replaceColor = null; + int targetWidth; + int targetHeight; + + boolean whiteMode = false; + + boolean pushed = false; + + HashMap gradientMap = new HashMap(); + HashMap gradientRefMap = new HashMap(); + Gradient gradient = null; + + private SVGHandler(Picture picture) { + this.picture = picture; + paint = new Paint(); + paint.setAntiAlias(true); + } + + private SVGHandler(Picture picture, int targetWidth, int targetHeight) { + this(picture); + this.targetWidth = targetWidth; + this.targetHeight = targetHeight; + } + + public void setColorSwap(Integer searchColor, Integer replaceColor) { + this.searchColor = searchColor; + this.replaceColor = replaceColor; + } + + public void setWhiteMode(boolean whiteMode) { + this.whiteMode = whiteMode; + } + + @Override + public void startDocument() throws SAXException { + // Set up prior to parsing a doc + } + + @Override + public void endDocument() throws SAXException { + // Clean up after parsing a doc + } + + private boolean doFill(Properties atts, HashMap gradients) { + if ("none".equals(atts.getString("display"))) { + return false; + } + if (whiteMode) { + paint.setStyle(Paint.Style.FILL); + paint.setColor(0xFFFFFFFF); + return true; + } + String fillString = atts.getString("fill"); + if (fillString != null && fillString.startsWith("url(#")) { + // It's a gradient fill, look it up in our map + String id = fillString.substring("url(#".length(), fillString.length() - 1); + Shader shader = gradients.get(id); + if (shader != null) { + //Util.debug("Found shader!"); + paint.setShader(shader); + paint.setStyle(Paint.Style.FILL); + return true; + } else { + //Util.debug("Didn't find shader!"); + return false; + } + } else { + paint.setShader(null); + Integer color = atts.getHex("fill"); + if (color != null) { + doColor(atts, color, true); + paint.setStyle(Paint.Style.FILL); + return true; + } else if (atts.getString("fill") == null && atts.getString("stroke") == null) { + // Default is black fill + paint.setStyle(Paint.Style.FILL); + paint.setColor(0xFF000000); + return true; + } + } + return false; + } + + private boolean doStroke(Properties atts) { + if (whiteMode) { + // Never stroke in white mode + return false; + } + if ("none".equals(atts.getString("display"))) { + return false; + } + Integer color = atts.getHex("stroke"); + if (color != null) { + doColor(atts, color, false); + // Check for other stroke attributes + Float width = atts.getFloat("stroke-width"); + // Set defaults + + if (width != null) { + paint.setStrokeWidth(width); + } + String linecap = atts.getString("stroke-linecap"); + if ("round".equals(linecap)) { + paint.setStrokeCap(Paint.Cap.ROUND); + } else if ("square".equals(linecap)) { + paint.setStrokeCap(Paint.Cap.SQUARE); + } else if ("butt".equals(linecap)) { + paint.setStrokeCap(Paint.Cap.BUTT); + } + String linejoin = atts.getString("stroke-linejoin"); + if ("miter".equals(linejoin)) { + paint.setStrokeJoin(Paint.Join.MITER); + } else if ("round".equals(linejoin)) { + paint.setStrokeJoin(Paint.Join.ROUND); + } else if ("bevel".equals(linejoin)) { + paint.setStrokeJoin(Paint.Join.BEVEL); + } + paint.setStyle(Paint.Style.STROKE); + return true; + } + return false; + } + + private Gradient doGradient(boolean isLinear, Attributes atts) { + Gradient gradient = new Gradient(); + gradient.id = getStringAttr("id", atts); + gradient.isLinear = isLinear; + if (isLinear) { + gradient.x1 = getFloatAttr("x1", atts, 0f); + gradient.x2 = getFloatAttr("x2", atts, 0f); + gradient.y1 = getFloatAttr("y1", atts, 0f); + gradient.y2 = getFloatAttr("y2", atts, 0f); + } else { + gradient.x = getFloatAttr("cx", atts, 0f); + gradient.y = getFloatAttr("cy", atts, 0f); + gradient.radius = getFloatAttr("r", atts, 0f); + } + String transform = getStringAttr("gradientTransform", atts); + if (transform != null) { + gradient.matrix = parseTransform(transform); + } + String xlink = getStringAttr("href", atts); + if (xlink != null) { + if (xlink.startsWith("#")) { + xlink = xlink.substring(1); + } + gradient.xlink = xlink; + } + return gradient; + } + + private void doColor(Properties atts, Integer color, boolean fillMode) { + int c = (0xFFFFFF & color) | 0xFF000000; + if (searchColor != null && searchColor == c) { + c = replaceColor; + } + paint.setColor(c); + Float opacity = atts.getFloat("opacity"); + if (opacity == null) { + opacity = atts.getFloat(fillMode ? "fill-opacity" : "stroke-opacity"); + } + if (opacity == null) { + paint.setAlpha(255); + } else { + paint.setAlpha((int) (255 * opacity)); + } + } + + private boolean hidden = false; + private int hiddenLevel = 0; + private boolean boundsMode = false; + + private void doLimits(float x, float y) { + if (x < limits.left) { + limits.left = x; + } + if (x > limits.right) { + limits.right = x; + } + if (y < limits.top) { + limits.top = y; + } + if (y > limits.bottom) { + limits.bottom = y; + } + } + + private void doLimits(float x, float y, float width, float height) { + doLimits(x, y); + doLimits(x + width, y + height); + } + + private void doLimits(Path path) { + path.computeBounds(rect, false); + doLimits(rect.left, rect.top); + doLimits(rect.right, rect.bottom); + } + + private void pushTransform(Attributes atts) { + final String transform = getStringAttr("transform", atts); + pushed = transform != null; + if (pushed) { + final Matrix matrix = parseTransform(transform); + canvas.save(); + canvas.concat(matrix); + } + } + + private void popTransform() { + if (pushed) { + canvas.restore(); + } + } + + /** + * Start recording picture on the canvas. + * If target width and height are set for the canvas + * scale output picture uniformally using by the smallest + * dimention. + * @param imageWidth Width of the SVG image. + * @param imageHeight Height of the SVG image. + * @return + */ + private Canvas beginRecordingPicture(int imageWidth, int imageHeight) { + if(targetWidth == 0 || targetHeight == 0) { + return picture.beginRecording(imageWidth, imageHeight); + } else { + Canvas canvas = picture.beginRecording(targetWidth, targetHeight); + prepareScaledCanvas(canvas, imageWidth, imageHeight); + return canvas; + } + } + + private static final void prepareScaledCanvas(Canvas canvas, float imageWidth, float imageHeight) { + final float scaleX = canvas.getWidth() / imageWidth; + final float scaleY = canvas.getHeight() / imageHeight; + + if(scaleX > scaleY) { + final float dx = ((scaleX - scaleY) * imageWidth) / 2; + canvas.translate(dx, 0); + canvas.scale(scaleY, scaleY); + } else { + final float dy = ((scaleY - scaleX) * imageHeight) / 2; + canvas.translate(0, dy); + canvas.scale(scaleX, scaleX); + } + } + + + @Override + public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { + // Reset paint opacity + paint.setAlpha(255); + // Ignore everything but rectangles in bounds mode + if (boundsMode) { + if (localName.equals("rect")) { + Float x = getFloatAttr("x", atts); + if (x == null) { + x = 0f; + } + Float y = getFloatAttr("y", atts); + if (y == null) { + y = 0f; + } + Float width = getFloatAttr("width", atts); + Float height = getFloatAttr("height", atts); + bounds = new RectF(x, y, x + width, y + width); + } + return; + } + if (localName.equals("svg")) { + int imageWidth = (int) Math.ceil(getFloatAttr("width", atts)); + int imageHeight = (int) Math.ceil(getFloatAttr("height", atts)); + canvas = beginRecordingPicture(imageWidth, imageHeight); + } else if (localName.equals("defs")) { + // Ignore + } else if (localName.equals("linearGradient")) { + gradient = doGradient(true, atts); + } else if (localName.equals("radialGradient")) { + gradient = doGradient(false, atts); + } else if (localName.equals("stop")) { + if (gradient != null) { + float offset = getFloatAttr("offset", atts); + String styles = getStringAttr("style", atts); + StyleSet styleSet = new StyleSet(styles); + String colorStyle = styleSet.getStyle("stop-color"); + int color = Color.BLACK; + if (colorStyle != null) { + if (colorStyle.startsWith("#")) { + color = Integer.parseInt(colorStyle.substring(1), 16); + } else { + color = Integer.parseInt(colorStyle, 16); + } + } + String opacityStyle = styleSet.getStyle("stop-opacity"); + if (opacityStyle != null) { + float alpha = Float.parseFloat(opacityStyle); + int alphaInt = Math.round(255 * alpha); + color |= (alphaInt << 24); + } else { + color |= 0xFF000000; + } + gradient.positions.add(offset); + gradient.colors.add(color); + } + } else if (localName.equals("g")) { + // Check to see if this is the "bounds" layer + if ("bounds".equalsIgnoreCase(getStringAttr("id", atts))) { + boundsMode = true; + } + if (hidden) { + hiddenLevel++; + //Util.debug("Hidden up: " + hiddenLevel); + } + // Go in to hidden mode if display is "none" + if ("none".equals(getStringAttr("display", atts))) { + if (!hidden) { + hidden = true; + hiddenLevel = 1; + //Util.debug("Hidden up: " + hiddenLevel); + } + } + } else if (!hidden && localName.equals("rect")) { + Float x = getFloatAttr("x", atts); + if (x == null) { + x = 0f; + } + Float y = getFloatAttr("y", atts); + if (y == null) { + y = 0f; + } + Float width = getFloatAttr("width", atts); + Float height = getFloatAttr("height", atts); + pushTransform(atts); + Properties props = new Properties(atts); + if (doFill(props, gradientMap)) { + doLimits(x, y, width, height); + canvas.drawRect(x, y, x + width, y + height, paint); + } + if (doStroke(props)) { + canvas.drawRect(x, y, x + width, y + height, paint); + } + popTransform(); + } else if (!hidden && localName.equals("line")) { + Float x1 = getFloatAttr("x1", atts); + Float x2 = getFloatAttr("x2", atts); + Float y1 = getFloatAttr("y1", atts); + Float y2 = getFloatAttr("y2", atts); + Properties props = new Properties(atts); + if (doStroke(props)) { + pushTransform(atts); + doLimits(x1, y1); + doLimits(x2, y2); + canvas.drawLine(x1, y1, x2, y2, paint); + popTransform(); + } + } else if (!hidden && localName.equals("circle")) { + Float centerX = getFloatAttr("cx", atts); + Float centerY = getFloatAttr("cy", atts); + Float radius = getFloatAttr("r", atts); + if (centerX != null && centerY != null && radius != null) { + pushTransform(atts); + Properties props = new Properties(atts); + if (doFill(props, gradientMap)) { + doLimits(centerX - radius, centerY - radius); + doLimits(centerX + radius, centerY + radius); + canvas.drawCircle(centerX, centerY, radius, paint); + } + if (doStroke(props)) { + canvas.drawCircle(centerX, centerY, radius, paint); + } + popTransform(); + } + } else if (!hidden && localName.equals("ellipse")) { + Float centerX = getFloatAttr("cx", atts); + Float centerY = getFloatAttr("cy", atts); + Float radiusX = getFloatAttr("rx", atts); + Float radiusY = getFloatAttr("ry", atts); + if (centerX != null && centerY != null && radiusX != null && radiusY != null) { + pushTransform(atts); + Properties props = new Properties(atts); + rect.set(centerX - radiusX, centerY - radiusY, centerX + radiusX, centerY + radiusY); + if (doFill(props, gradientMap)) { + doLimits(centerX - radiusX, centerY - radiusY); + doLimits(centerX + radiusX, centerY + radiusY); + canvas.drawOval(rect, paint); + } + if (doStroke(props)) { + canvas.drawOval(rect, paint); + } + popTransform(); + } + } else if (!hidden && (localName.equals("polygon") || localName.equals("polyline"))) { + NumberParse numbers = getNumberParseAttr("points", atts); + if (numbers != null) { + Path p = new Path(); + ArrayList points = numbers.numbers; + if (points.size() > 1) { + pushTransform(atts); + Properties props = new Properties(atts); + p.moveTo(points.get(0), points.get(1)); + for (int i = 2; i < points.size(); i += 2) { + float x = points.get(i); + float y = points.get(i + 1); + p.lineTo(x, y); + } + // Don't close a polyline + if (localName.equals("polygon")) { + p.close(); + } + if (doFill(props, gradientMap)) { + doLimits(p); + canvas.drawPath(p, paint); + } + if (doStroke(props)) { + canvas.drawPath(p, paint); + } + popTransform(); + } + } + } else if (!hidden && localName.equals("path")) { + Path p = doPath(getStringAttr("d", atts)); + pushTransform(atts); + Properties props = new Properties(atts); + if (doFill(props, gradientMap)) { + doLimits(p); + canvas.drawPath(p, paint); + } + if (doStroke(props)) { + canvas.drawPath(p, paint); + } + popTransform(); + } else if (!hidden) { + Log.d(TAG, "UNRECOGNIZED SVG COMMAND: " + localName); + } + } + + @Override + public void characters(char ch[], int start, int length) { + // no-op + } + + @Override + public void endElement(String namespaceURI, String localName, String qName) + throws SAXException { + switch (localName) { + case "svg": + picture.endRecording(); + break; + case "linearGradient": + if (gradient.id != null) { + if (gradient.xlink != null) { + Gradient parent = gradientRefMap.get(gradient.xlink); + if (parent != null) { + gradient = parent.createChild(gradient); + } + } + int[] colors = new int[gradient.colors.size()]; + for (int i = 0; i < colors.length; i++) { + colors[i] = gradient.colors.get(i); + } + float[] positions = new float[gradient.positions.size()]; + for (int i = 0; i < positions.length; i++) { + positions[i] = gradient.positions.get(i); + } + if (colors.length == 0) { + Log.d("BAD", "BAD"); + } + LinearGradient g = new LinearGradient(gradient.x1, gradient.y1, gradient.x2, gradient.y2, colors, positions, Shader.TileMode.CLAMP); + if (gradient.matrix != null) { + g.setLocalMatrix(gradient.matrix); + } + gradientMap.put(gradient.id, g); + gradientRefMap.put(gradient.id, gradient); + } + break; + case "radialGradient": + if (gradient.id != null) { + if (gradient.xlink != null) { + Gradient parent = gradientRefMap.get(gradient.xlink); + if (parent != null) { + gradient = parent.createChild(gradient); + } + } + int[] colors = new int[gradient.colors.size()]; + for (int i = 0; i < colors.length; i++) { + colors[i] = gradient.colors.get(i); + } + float[] positions = new float[gradient.positions.size()]; + for (int i = 0; i < positions.length; i++) { + positions[i] = gradient.positions.get(i); + } + if (gradient.xlink != null) { + Gradient parent = gradientRefMap.get(gradient.xlink); + if (parent != null) { + gradient = parent.createChild(gradient); + } + } + RadialGradient g = new RadialGradient(gradient.x, gradient.y, gradient.radius, colors, positions, Shader.TileMode.CLAMP); + if (gradient.matrix != null) { + g.setLocalMatrix(gradient.matrix); + } + gradientMap.put(gradient.id, g); + gradientRefMap.put(gradient.id, gradient); + } + break; + case "g": + if (boundsMode) { + boundsMode = false; + } + // Break out of hidden mode + if (hidden) { + hiddenLevel--; + //Util.debug("Hidden down: " + hiddenLevel); + if (hiddenLevel == 0) { + hidden = false; + } + } + // Clear gradient map + gradientMap.clear(); + break; + } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/AdapterPositionedEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/AdapterPositionedEvent.java new file mode 100755 index 0000000..9e7b437 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/AdapterPositionedEvent.java @@ -0,0 +1,12 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-11-08. + */ +public abstract class AdapterPositionedEvent { + public AdapterPositionedEvent(int adapterPosition) { + this.adapterPosition = adapterPosition; + } + + public int adapterPosition; +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/AddItemUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/AddItemUIEvent.java new file mode 100755 index 0000000..f9643dd --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/AddItemUIEvent.java @@ -0,0 +1,12 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-07-26. + */ +public class AddItemUIEvent { + public AddItemUIEvent(int adapterPosition) { + this.adapterPosition = adapterPosition; + } + + public int adapterPosition; +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/AfterVerifyResponse.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/AfterVerifyResponse.java new file mode 100755 index 0000000..9133891 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/AfterVerifyResponse.java @@ -0,0 +1,9 @@ +package com.shaya.poinila.android.presentation.uievent; + +import data.event.BaseEvent; + +/** + * Created by hossein on 9/7/16. + */ +public class AfterVerifyResponse extends BaseEvent { +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/AnswerFriendshipUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/AnswerFriendshipUIEvent.java new file mode 100755 index 0000000..d17150f --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/AnswerFriendshipUIEvent.java @@ -0,0 +1,16 @@ +package com.shaya.poinila.android.presentation.uievent; + +import data.model.FriendRequestAnswer; + +/** + * Created by iran on 2015-08-15. + */ +public class AnswerFriendshipUIEvent { + public int adapterPosition; + public FriendRequestAnswer accept; + + public AnswerFriendshipUIEvent(int adapterPosition, FriendRequestAnswer answer) { + this.adapterPosition = adapterPosition; + this.accept = answer; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/CapturePhotoEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/CapturePhotoEvent.java new file mode 100755 index 0000000..72c6fa5 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/CapturePhotoEvent.java @@ -0,0 +1,16 @@ +package com.shaya.poinila.android.presentation.uievent; + +import android.content.Intent; + +/** + * Created by AlirezaF on 7/17/2015. + */ +public class CapturePhotoEvent { + public final int requestCode; + public final Intent intent; + + public CapturePhotoEvent(Intent takePictureIntent, int requestTakePhoto) { + intent = takePictureIntent; + requestCode = requestTakePhoto; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/ChangePasswordUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/ChangePasswordUIEvent.java new file mode 100755 index 0000000..7629ef7 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/ChangePasswordUIEvent.java @@ -0,0 +1,14 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-08-01. + */ +public class ChangePasswordUIEvent { + public final String oldPass; + public final String newPass; + + public ChangePasswordUIEvent(String oldPass, String newPass) { + this.oldPass = oldPass; + this.newPass = newPass; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/CheckBoxClickUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/CheckBoxClickUIEvent.java new file mode 100755 index 0000000..70335ae --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/CheckBoxClickUIEvent.java @@ -0,0 +1,14 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-08-19. + */ +public class CheckBoxClickUIEvent { + public boolean checked; + public int adapterPosition; + + public CheckBoxClickUIEvent(boolean checked, int adapterPosition) { + this.checked = checked; + this.adapterPosition = adapterPosition; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/CirclesSelectedUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/CirclesSelectedUIEvent.java new file mode 100755 index 0000000..2778962 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/CirclesSelectedUIEvent.java @@ -0,0 +1,15 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-08-29. + */ +public class CirclesSelectedUIEvent { + public final boolean[] selectedCircles; + //public final int actorID; + + public CirclesSelectedUIEvent(boolean[] selectedCircles, int actorID) { + + this.selectedCircles = selectedCircles; + //this.actorID = actorID; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/CollectionClickedUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/CollectionClickedUIEvent.java new file mode 100755 index 0000000..b1053b7 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/CollectionClickedUIEvent.java @@ -0,0 +1,16 @@ +package com.shaya.poinila.android.presentation.uievent; + +import data.event.BaseEvent; +import data.event.IdentifiableEvent; + +/** + * Created by iran on 2015-08-06. + */ +public class CollectionClickedUIEvent extends IdentifiableEvent { + public int adapterPosition; + + public CollectionClickedUIEvent(int adapterPosition, BaseEvent.ReceiverName receiverTag) { + super(receiverTag); + this.adapterPosition = adapterPosition; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/CollectionFrameToggledEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/CollectionFrameToggledEvent.java new file mode 100755 index 0000000..9dde5f4 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/CollectionFrameToggledEvent.java @@ -0,0 +1,10 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-11-08. + */ +public class CollectionFrameToggledEvent extends AdapterPositionedEvent{ + public CollectionFrameToggledEvent(int adapterPosition) { + super(adapterPosition); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/CollectionListActivityImageSelectedUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/CollectionListActivityImageSelectedUIEvent.java new file mode 100755 index 0000000..7f75a59 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/CollectionListActivityImageSelectedUIEvent.java @@ -0,0 +1,10 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-09-22. + */ +public class CollectionListActivityImageSelectedUIEvent extends ImageSelectedUIEvent { + public CollectionListActivityImageSelectedUIEvent(ImageSelectedUIEvent event) { + super(event); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/CommentLongClickUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/CommentLongClickUIEvent.java new file mode 100755 index 0000000..5a1e72a --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/CommentLongClickUIEvent.java @@ -0,0 +1,12 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-09-20. + */ +public class CommentLongClickUIEvent { + public int adapterPosition; + + public CommentLongClickUIEvent(int adapterPosition) { + this.adapterPosition = adapterPosition; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/DeleteCircleUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/DeleteCircleUIEvent.java new file mode 100755 index 0000000..77b7bd6 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/DeleteCircleUIEvent.java @@ -0,0 +1,13 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-07-28. + */ +public class DeleteCircleUIEvent { + public int adapterPosition; + + public DeleteCircleUIEvent(int adapterPosition) { + + this.adapterPosition = adapterPosition; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/DeleteFrameUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/DeleteFrameUIEvent.java new file mode 100755 index 0000000..5eb1b0c --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/DeleteFrameUIEvent.java @@ -0,0 +1,14 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-07-28. + */ +public class DeleteFrameUIEvent { + + + public int adapterPosition; + + public DeleteFrameUIEvent(int adapterPosition) { + this.adapterPosition = adapterPosition; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/EditCircleNameUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/EditCircleNameUIEvent.java new file mode 100755 index 0000000..38ff222 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/EditCircleNameUIEvent.java @@ -0,0 +1,12 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-07-28. + */ +public class EditCircleNameUIEvent { + public int adapterPosition; + + public EditCircleNameUIEvent(int adapterPosition) { + this.adapterPosition = adapterPosition; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/EditFrameNameUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/EditFrameNameUIEvent.java new file mode 100755 index 0000000..da67102 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/EditFrameNameUIEvent.java @@ -0,0 +1,12 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-07-28. + */ +public class EditFrameNameUIEvent { + public int adapterPosition; + + public EditFrameNameUIEvent(int adapterPosition) { + this.adapterPosition = adapterPosition; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/EditItemUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/EditItemUIEvent.java new file mode 100755 index 0000000..267d7b1 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/EditItemUIEvent.java @@ -0,0 +1,13 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-08-17. + */ +public class EditItemUIEvent { + public int adapterPosition; + + public EditItemUIEvent(int adapterPosition) { + + this.adapterPosition = adapterPosition; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/ExploreTagEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/ExploreTagEvent.java new file mode 100755 index 0000000..b9bdacd --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/ExploreTagEvent.java @@ -0,0 +1,12 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 12/26/2015. + */ +public class ExploreTagEvent { + public String text; + + public ExploreTagEvent(String text) { + this.text = text; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/FABMenuCollapseUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/FABMenuCollapseUIEvent.java new file mode 100755 index 0000000..9c76ceb --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/FABMenuCollapseUIEvent.java @@ -0,0 +1,13 @@ +package com.shaya.poinila.android.presentation.uievent; + + +import data.event.IdentifiableEvent; + +/** + * Created by iran on 11/22/2015. + */ +public class FABMenuCollapseUIEvent extends IdentifiableEvent { + public FABMenuCollapseUIEvent(ReceiverName receiverName) { + super(receiverName); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/FABMenuExpandUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/FABMenuExpandUIEvent.java new file mode 100755 index 0000000..fcda96f --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/FABMenuExpandUIEvent.java @@ -0,0 +1,14 @@ +package com.shaya.poinila.android.presentation.uievent; + + +import data.event.BaseEvent; +import data.event.IdentifiableEvent; + +/** + * Created by iran on 11/22/2015. + */ +public class FABMenuExpandUIEvent extends IdentifiableEvent { + public FABMenuExpandUIEvent(BaseEvent.ReceiverName receiverName) { + super(receiverName); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/FramesUpdatedUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/FramesUpdatedUIEvent.java new file mode 100755 index 0000000..30f0dbf --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/FramesUpdatedUIEvent.java @@ -0,0 +1,17 @@ +package com.shaya.poinila.android.presentation.uievent; + + +import java.util.List; + +import data.model.Frame; + +/** + * Created by iran on 2015-08-01. + */ +public class FramesUpdatedUIEvent { + public List frames; + + public FramesUpdatedUIEvent(List frames) { + this.frames = frames; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/FriendCirclesUpdated.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/FriendCirclesUpdated.java new file mode 100755 index 0000000..653bd67 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/FriendCirclesUpdated.java @@ -0,0 +1,19 @@ +package com.shaya.poinila.android.presentation.uievent; + + +import java.util.List; + +import data.model.Member; + +/** + * Created by iran on 2015-10-11. + */ +public class FriendCirclesUpdated { + public final List selectedCirclesIDs; + public final Member member; + + public FriendCirclesUpdated(List selectedCirclesIDs, Member member) { + this.selectedCirclesIDs = selectedCirclesIDs; + this.member = member; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/FriendshipClickEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/FriendshipClickEvent.java new file mode 100755 index 0000000..daa7fd4 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/FriendshipClickEvent.java @@ -0,0 +1,12 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by AlirezaF on 7/16/2015. + */ +public class FriendshipClickEvent { + public final int adapterPosition; + + public FriendshipClickEvent(int adapterPosition) { + this.adapterPosition = adapterPosition; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/GoNextPhaseUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/GoNextPhaseUIEvent.java new file mode 100755 index 0000000..0ac3e95 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/GoNextPhaseUIEvent.java @@ -0,0 +1,12 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 12/6/2015. + */ +public class GoNextPhaseUIEvent { + public GoNextPhaseUIEvent(String phase) { + this.phase = phase; + } + + public String phase; +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/GoPreviousPhaseUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/GoPreviousPhaseUIEvent.java new file mode 100755 index 0000000..c3709ab --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/GoPreviousPhaseUIEvent.java @@ -0,0 +1,7 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 12/6/2015. + */ +public class GoPreviousPhaseUIEvent { +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/GoogleLoginSucceedEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/GoogleLoginSucceedEvent.java new file mode 100755 index 0000000..259c85c --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/GoogleLoginSucceedEvent.java @@ -0,0 +1,10 @@ +package com.shaya.poinila.android.presentation.uievent; + +import data.event.LoginSucceedEvent; + +/** + * Created by iran on 6/26/2016. + */ +public class GoogleLoginSucceedEvent extends LoginSucceedEvent { + public boolean firstLoginDoneByGoogle = false; +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/HelpDashboardFragment.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/HelpDashboardFragment.java new file mode 100755 index 0000000..e6e8e66 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/HelpDashboardFragment.java @@ -0,0 +1,9 @@ +package com.shaya.poinila.android.presentation.uievent; + +import data.event.BaseEvent; + +/** + * Created by iran on 5/28/2016. + */ +public class HelpDashboardFragment extends BaseEvent { +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/HelpMyFollowedCollectionListFragment.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/HelpMyFollowedCollectionListFragment.java new file mode 100755 index 0000000..c5b364c --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/HelpMyFollowedCollectionListFragment.java @@ -0,0 +1,10 @@ +package com.shaya.poinila.android.presentation.uievent; + +import data.event.BaseEvent; + +/** + * Created by iran on 5/28/2016. + */ +public class HelpMyFollowedCollectionListFragment extends BaseEvent { + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/HelpMyProfileFragment.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/HelpMyProfileFragment.java new file mode 100755 index 0000000..320412c --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/HelpMyProfileFragment.java @@ -0,0 +1,10 @@ +package com.shaya.poinila.android.presentation.uievent; + + +import data.event.BaseEvent; + +/** + * Created by iran on 5/28/2016. + */ +public class HelpMyProfileFragment extends BaseEvent { +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/ImageClickedUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/ImageClickedUIEvent.java new file mode 100755 index 0000000..b4a3da6 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/ImageClickedUIEvent.java @@ -0,0 +1,12 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by AlirezaF on 7/16/2015. + */ +public class ImageClickedUIEvent { + public final int adapterPosition; + + public ImageClickedUIEvent(int adapterPosition) { + this.adapterPosition = adapterPosition; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/ImageSelectedUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/ImageSelectedUIEvent.java new file mode 100755 index 0000000..6817e19 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/ImageSelectedUIEvent.java @@ -0,0 +1,20 @@ +package com.shaya.poinila.android.presentation.uievent; + +import android.net.Uri; + +/** + * Created by AlirezaF on 7/17/2015. + */ +public class ImageSelectedUIEvent { + public String absolutePath; + public Uri mediaPath; + + public ImageSelectedUIEvent(String absolutePath, Uri mediaPath) { + this.absolutePath = absolutePath; + this.mediaPath = mediaPath; + } + + public ImageSelectedUIEvent(ImageSelectedUIEvent event){ + this(event.absolutePath, event.mediaPath); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/MainActivityImageSelectedUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/MainActivityImageSelectedUIEvent.java new file mode 100755 index 0000000..6618dd3 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/MainActivityImageSelectedUIEvent.java @@ -0,0 +1,10 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-09-22. + */ +public class MainActivityImageSelectedUIEvent extends ImageSelectedUIEvent { + public MainActivityImageSelectedUIEvent(ImageSelectedUIEvent event) { + super(event); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/MemberCircleToggledEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/MemberCircleToggledEvent.java new file mode 100755 index 0000000..092e9e1 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/MemberCircleToggledEvent.java @@ -0,0 +1,10 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-11-08. + */ +public class MemberCircleToggledEvent extends AdapterPositionedEvent{ + public MemberCircleToggledEvent(int adapterPosition) { + super(adapterPosition); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/MemberClickedUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/MemberClickedUIEvent.java new file mode 100755 index 0000000..7afcf8d --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/MemberClickedUIEvent.java @@ -0,0 +1,15 @@ +package com.shaya.poinila.android.presentation.uievent; + +import data.event.BaseEvent; +import data.event.IdentifiableEvent; + +/** + * Created by AlirezaF on 7/16/2015. + */ +public class MemberClickedUIEvent extends IdentifiableEvent { + public int adapterPosition; + public MemberClickedUIEvent(int adapterPosition, BaseEvent.ReceiverName receiverTag) { + super(receiverTag); + this.adapterPosition = adapterPosition; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/NeutralDialogButtonClickedUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/NeutralDialogButtonClickedUIEvent.java new file mode 100755 index 0000000..11d31c1 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/NeutralDialogButtonClickedUIEvent.java @@ -0,0 +1,7 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-08-19. + */ +public class NeutralDialogButtonClickedUIEvent { +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/NewWebsitePostEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/NewWebsitePostEvent.java new file mode 100755 index 0000000..e2e9409 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/NewWebsitePostEvent.java @@ -0,0 +1,12 @@ +package com.shaya.poinila.android.presentation.uievent; + + +import data.model.SuggestedWebPagePost; + +public class NewWebsitePostEvent { + public SuggestedWebPagePost suggestedPost; + + public NewWebsitePostEvent(SuggestedWebPagePost suggestedPost) { + this.suggestedPost = suggestedPost; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/NotifActorClickedUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/NotifActorClickedUIEvent.java new file mode 100755 index 0000000..154c9cc --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/NotifActorClickedUIEvent.java @@ -0,0 +1,12 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-10-25. + */ +public class NotifActorClickedUIEvent { + public int adapterPosition; + + public NotifActorClickedUIEvent(int adapterPosition) { + this.adapterPosition = adapterPosition; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/NotifParticipantClickedUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/NotifParticipantClickedUIEvent.java new file mode 100755 index 0000000..e4413ae --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/NotifParticipantClickedUIEvent.java @@ -0,0 +1,17 @@ +package com.shaya.poinila.android.presentation.uievent; + +import data.model.ImageUrls; +import data.model.Participant; + +/** + * Created by iran on 2015-11-02. + */ +public class NotifParticipantClickedUIEvent { + public Participant participant; + public ImageUrls.ImageType participantsType; + + public NotifParticipantClickedUIEvent(Participant participant, ImageUrls.ImageType participantsType) { + this.participant = participant; + this.participantsType = participantsType; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/OnAnswerFriendshipUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/OnAnswerFriendshipUIEvent.java new file mode 100755 index 0000000..0f5b935 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/OnAnswerFriendshipUIEvent.java @@ -0,0 +1,16 @@ +package com.shaya.poinila.android.presentation.uievent; + +import data.model.FriendRequestAnswer; + +/** + * Created by iran on 2015-08-15. + */ +public class OnAnswerFriendshipUIEvent { + private int adapterPosition; + public FriendRequestAnswer accept; + + public OnAnswerFriendshipUIEvent(int adapterPosition, FriendRequestAnswer answer) { + this.adapterPosition = adapterPosition; + this.accept = answer; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/OnFollowUnfollowCollectionUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/OnFollowUnfollowCollectionUIEvent.java new file mode 100755 index 0000000..b471ac0 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/OnFollowUnfollowCollectionUIEvent.java @@ -0,0 +1,20 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-08-06. + */ +public class OnFollowUnfollowCollectionUIEvent { + public int adapterPosition; + public boolean follow; + + public OnFollowUnfollowCollectionUIEvent(int adapterPosition) { + + this.adapterPosition = adapterPosition; + } + + public OnFollowUnfollowCollectionUIEvent(int adapterPosition, boolean follow) { + + this.adapterPosition = adapterPosition; + this.follow = follow; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/OnFrameClickedUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/OnFrameClickedUIEvent.java new file mode 100755 index 0000000..6886347 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/OnFrameClickedUIEvent.java @@ -0,0 +1,16 @@ +package com.shaya.poinila.android.presentation.uievent; + + +import data.model.Frame; + +/** + * Created by iran on 2015-08-08. + */ +public class OnFrameClickedUIEvent { + public Frame frame; + + public OnFrameClickedUIEvent(Frame frame) { + + this.frame = frame; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/OnOffSettingToggledUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/OnOffSettingToggledUIEvent.java new file mode 100755 index 0000000..f9be19f --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/OnOffSettingToggledUIEvent.java @@ -0,0 +1,14 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-09-07. + */ +public class OnOffSettingToggledUIEvent { + public int adapterPosition; + public boolean settingOn; + + public OnOffSettingToggledUIEvent(int adapterPosition, boolean settingOn) { + this.adapterPosition = adapterPosition; + this.settingOn = settingOn; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/PermissionEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/PermissionEvent.java new file mode 100755 index 0000000..b877010 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/PermissionEvent.java @@ -0,0 +1,23 @@ +package com.shaya.poinila.android.presentation.uievent; + + +import data.event.BaseEvent; + +/** + * Created by iran on 1/18/2016. + */ +public class PermissionEvent extends BaseEvent { + public String permissionString; + + public PermissionEvent(String permissionString) { + + this.permissionString = permissionString; + } + /*public final boolean granted; + public final int requestCode; + + public PermissionEvent(boolean granted, int requestCode) { + this.granted = granted; + this.requestCode = requestCode; + }*/ +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/PhotoCapturedEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/PhotoCapturedEvent.java new file mode 100755 index 0000000..374d064 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/PhotoCapturedEvent.java @@ -0,0 +1,7 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by AlirezaF on 7/17/2015. + */ +public class PhotoCapturedEvent { +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/PositiveButtonClickedUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/PositiveButtonClickedUIEvent.java new file mode 100755 index 0000000..b748773 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/PositiveButtonClickedUIEvent.java @@ -0,0 +1,17 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-07-28. + */ +public class PositiveButtonClickedUIEvent { + private Object data; + + public Object getData() { + return data; + } + + public PositiveButtonClickedUIEvent setData(Object data) { + this.data = data; + return this; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/PostClickedUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/PostClickedUIEvent.java new file mode 100755 index 0000000..3c9b594 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/PostClickedUIEvent.java @@ -0,0 +1,17 @@ +package com.shaya.poinila.android.presentation.uievent; + + +import data.event.BaseEvent; +import data.event.IdentifiableEvent; + +/** + * Created by iran on 2015-07-23. + */ +public class PostClickedUIEvent extends IdentifiableEvent { + public int adapterPosition; + + public PostClickedUIEvent(int adapterPosition, BaseEvent.ReceiverName receiverTag) { + super(receiverTag); + this.adapterPosition = adapterPosition; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/PostComponentClickedUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/PostComponentClickedUIEvent.java new file mode 100755 index 0000000..ec3b1a1 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/PostComponentClickedUIEvent.java @@ -0,0 +1,33 @@ +package com.shaya.poinila.android.presentation.uievent; + + +import java.util.Arrays; +import java.util.List; + +import data.event.BaseEvent; + +/** + * Created by iran on 2015-11-18. + */ +public class PostComponentClickedUIEvent extends BaseEvent { + public Type type; + + public PostComponentClickedUIEvent(Type type){ + this.type = type; + } + + public enum Type{ + Fave, + FaversList, + Comments, + RepostersList, + Repost, + Reference, + Collection, + Poster, + OriginalCollection, + FullImage; + + public static List guestCantPerformActions = Arrays.asList(Fave, Repost); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/PostHeightUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/PostHeightUIEvent.java new file mode 100755 index 0000000..975a614 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/PostHeightUIEvent.java @@ -0,0 +1,14 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-10-14. + */ +public class PostHeightUIEvent { + public final int position; + public final int height; + + public PostHeightUIEvent(int position, int height) { + this.position = position; + this.height = height; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/PostListActivityImageSelectedUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/PostListActivityImageSelectedUIEvent.java new file mode 100755 index 0000000..8bbd274 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/PostListActivityImageSelectedUIEvent.java @@ -0,0 +1,10 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-09-22. + */ +public class PostListActivityImageSelectedUIEvent extends ImageSelectedUIEvent { + public PostListActivityImageSelectedUIEvent(ImageSelectedUIEvent event) { + super(event); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/ProfilePicSelectedEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/ProfilePicSelectedEvent.java new file mode 100755 index 0000000..38eecb8 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/ProfilePicSelectedEvent.java @@ -0,0 +1,16 @@ +package com.shaya.poinila.android.presentation.uievent; + +import android.net.Uri; + +/** + * Created by iran on 2015-09-13. + */ +public class ProfilePicSelectedEvent { + public String absolutePath; + public Uri mediaPath; + + public ProfilePicSelectedEvent(String absolutePath, Uri mediaPath) { + this.absolutePath = absolutePath; + this.mediaPath = mediaPath; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/RatePonilaEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/RatePonilaEvent.java new file mode 100755 index 0000000..0b21e5e --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/RatePonilaEvent.java @@ -0,0 +1,10 @@ +package com.shaya.poinila.android.presentation.uievent; + + +import data.event.BaseEvent; + +/** + * Created by iran on 1/20/2016. + */ +public class RatePonilaEvent extends BaseEvent { +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/RemoveItemUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/RemoveItemUIEvent.java new file mode 100755 index 0000000..3998f43 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/RemoveItemUIEvent.java @@ -0,0 +1,12 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-07-26. + */ +public class RemoveItemUIEvent { + public RemoveItemUIEvent(int adapterPosition) { + this.adapterPosition = adapterPosition; + } + + public int adapterPosition; +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/RemovePostUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/RemovePostUIEvent.java new file mode 100755 index 0000000..a321d9b --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/RemovePostUIEvent.java @@ -0,0 +1,13 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-08-16. + */ +public class RemovePostUIEvent { + public int adapterPosition; + + public RemovePostUIEvent(int adapterPosition) { + + this.adapterPosition = adapterPosition; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/RemoveTagEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/RemoveTagEvent.java new file mode 100755 index 0000000..84abb6a --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/RemoveTagEvent.java @@ -0,0 +1,20 @@ +package com.shaya.poinila.android.presentation.uievent; + +import android.view.View; + +/** + * Created by AlirezaF on 7/22/2015. + */ +public class RemoveTagEvent { + public View tagView; + public int adapterPosition; + + public RemoveTagEvent(View tagView) { + this.tagView = tagView; + } + + public RemoveTagEvent(int adapterPosition) { + + this.adapterPosition = adapterPosition; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/SelectImageEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/SelectImageEvent.java new file mode 100755 index 0000000..ab4d6f6 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/SelectImageEvent.java @@ -0,0 +1,16 @@ +package com.shaya.poinila.android.presentation.uievent; + +import android.content.Intent; + +/** + * Created by AlirezaF on 7/17/2015. + */ +public class SelectImageEvent { + public final Intent intent; + public int requestCode; + + public SelectImageEvent(Intent selectImageIntent, int requestSelectImage) { + intent = selectImageIntent; + requestCode = requestSelectImage; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/ShowSelectInterestUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/ShowSelectInterestUIEvent.java new file mode 100755 index 0000000..b9751a1 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/ShowSelectInterestUIEvent.java @@ -0,0 +1,7 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-07-12. + */ +public class ShowSelectInterestUIEvent { +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/ShowVerifySnackbarEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/ShowVerifySnackbarEvent.java new file mode 100755 index 0000000..6b51409 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/ShowVerifySnackbarEvent.java @@ -0,0 +1,7 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 7/20/2016. + */ +public class ShowVerifySnackbarEvent { +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/SimpleSettingTextSetEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/SimpleSettingTextSetEvent.java new file mode 100755 index 0000000..0874ab7 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/SimpleSettingTextSetEvent.java @@ -0,0 +1,18 @@ +package com.shaya.poinila.android.presentation.uievent; + +import com.shaya.poinila.android.presentation.view.activity.SettingActivity.SettingType; + +/** + * Created by iran on 2015-07-21. + */ +public class SimpleSettingTextSetEvent { + public SettingType settingType; + public String value; + public int itemPosition; + + public SimpleSettingTextSetEvent(SettingType settingType, String value, int itemPosition) { + this.settingType = settingType; + this.value = value; + this.itemPosition = itemPosition; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/SmsReceivedEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/SmsReceivedEvent.java new file mode 100755 index 0000000..3002f43 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/SmsReceivedEvent.java @@ -0,0 +1,12 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 12/8/2015. + */ +public class SmsReceivedEvent { + public String str; + + public SmsReceivedEvent(String str) { + this.str = str; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/SuggestionPosts.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/SuggestionPosts.java new file mode 100755 index 0000000..d9c2cb2 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/SuggestionPosts.java @@ -0,0 +1,18 @@ +package com.shaya.poinila.android.presentation.uievent; + +import java.util.List; + +import data.event.BaseEvent; +import data.model.Post; + +/** + * Created by iran on 7/24/2016. + */ +public class SuggestionPosts extends BaseEvent { + public List posts; + + public SuggestionPosts(List posts){ + this.posts = posts; + } + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/UpdateNewPostDialogEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/UpdateNewPostDialogEvent.java new file mode 100755 index 0000000..5b4335e --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/UpdateNewPostDialogEvent.java @@ -0,0 +1,12 @@ +package com.shaya.poinila.android.presentation.uievent; + +import com.raizlabs.android.dbflow.structure.BaseModel; + +/** + * Created by iran on 8/1/2016. + */ +public class UpdateNewPostDialogEvent extends data.event.ModelEvent { + public UpdateNewPostDialogEvent(BaseModel model) { + super(model); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/UpdateUICommentEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/UpdateUICommentEvent.java new file mode 100755 index 0000000..d72a72c --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/UpdateUICommentEvent.java @@ -0,0 +1,17 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-09-30. + */ +public class UpdateUICommentEvent { + public static final int INCREMENT_COMMENTS = 1; + public static final int DECREMENT_COMMENTS = 2; + public String postId; + + public int action; + + public UpdateUICommentEvent(int action, String postId) { + this.action = action; + this.postId = postId; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/UpdateUiRepostEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/UpdateUiRepostEvent.java new file mode 100755 index 0000000..f5545b9 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/UpdateUiRepostEvent.java @@ -0,0 +1,18 @@ +package com.shaya.poinila.android.presentation.uievent; + +import data.event.BaseEvent; +import data.model.Post; + +/** + * Created by iran on 7/11/2016. + */ +public class UpdateUiRepostEvent extends BaseEvent { + + public boolean isSuccess = true; + public int postId; + + public UpdateUiRepostEvent(int postId, boolean isSuccess){ + this.isSuccess = isSuccess; + this.postId = postId; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/UrlClickedUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/UrlClickedUIEvent.java new file mode 100755 index 0000000..647c7cb --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/UrlClickedUIEvent.java @@ -0,0 +1,12 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-11-18. + */ +public class UrlClickedUIEvent { + public String url; + + public UrlClickedUIEvent(String Url) { + url = Url; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/ViewCircleMembersUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/ViewCircleMembersUIEvent.java new file mode 100755 index 0000000..ee60915 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/ViewCircleMembersUIEvent.java @@ -0,0 +1,13 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-07-28. + */ +public class ViewCircleMembersUIEvent { + public int adapterPosition; + + public ViewCircleMembersUIEvent(int adapterPosition) { + + this.adapterPosition = adapterPosition; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/ViewFrameMembersUIEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/ViewFrameMembersUIEvent.java new file mode 100755 index 0000000..895f55b --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/ViewFrameMembersUIEvent.java @@ -0,0 +1,12 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by iran on 2015-07-28. + */ +public class ViewFrameMembersUIEvent { + public int adapterPosition; + + public ViewFrameMembersUIEvent(int adapterPosition) { + this.adapterPosition = adapterPosition; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/WebpageImagesClickEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/WebpageImagesClickEvent.java new file mode 100755 index 0000000..a92f2fd --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/WebpageImagesClickEvent.java @@ -0,0 +1,12 @@ +package com.shaya.poinila.android.presentation.uievent; + +/** + * Created by AlirezaF on 7/16/2015. + */ +public class WebpageImagesClickEvent { + public final int adapterPosition; + + public WebpageImagesClickEvent(int adapterPosition) { + this.adapterPosition = adapterPosition; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/sync/BaseSyncEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/sync/BaseSyncEvent.java new file mode 100755 index 0000000..f94cc26 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/sync/BaseSyncEvent.java @@ -0,0 +1,7 @@ +package com.shaya.poinila.android.presentation.uievent.sync; + +/** + * Created by iran on 7/11/2016. + */ +public class BaseSyncEvent { +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/uievent/sync/PostActionSyncEvent.java b/src/main/java/com/shaya/poinila/android/presentation/uievent/sync/PostActionSyncEvent.java new file mode 100755 index 0000000..9c994bc --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/uievent/sync/PostActionSyncEvent.java @@ -0,0 +1,14 @@ +package com.shaya.poinila.android.presentation.uievent.sync; + +import data.model.Post; + +/** + * Created by iran on 7/11/2016. + */ +public class PostActionSyncEvent extends BaseSyncEvent { + public Post post; + + public PostActionSyncEvent(Post post){ + this.post = post; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/ImagePickerInterface.java b/src/main/java/com/shaya/poinila/android/presentation/view/ImagePickerInterface.java new file mode 100755 index 0000000..c7e6fb5 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/ImagePickerInterface.java @@ -0,0 +1,18 @@ +package com.shaya.poinila.android.presentation.view; + +import android.net.Uri; + +/** + * Created by iran on 2015-07-21. + */ +public interface ImagePickerInterface { + void onRemoveSelectedImage(); + + void onImageReceived(String absolutePath, Uri mediaPath); + + void onCapturePhoto(); + + void onSelectFromGallery(); + +} + diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/LoaderList.java b/src/main/java/com/shaya/poinila/android/presentation/view/LoaderList.java new file mode 100755 index 0000000..46aa596 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/LoaderList.java @@ -0,0 +1,8 @@ +package com.shaya.poinila.android.presentation.view; + +/** + * Created by iran on 2015-09-15. + */ +public interface LoaderList { + void onLoadMore(); +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/NotificationNumberListener.java b/src/main/java/com/shaya/poinila/android/presentation/view/NotificationNumberListener.java new file mode 100755 index 0000000..b8fd3bf --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/NotificationNumberListener.java @@ -0,0 +1,10 @@ +package com.shaya.poinila.android.presentation.view; + +import java.io.Serializable; + +/** + * Created by iran on 5/15/2016. + */ +public interface NotificationNumberListener extends Serializable { + public void onNotificationNumber(int num); +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/OnHelpShowListener.java b/src/main/java/com/shaya/poinila/android/presentation/view/OnHelpShowListener.java new file mode 100755 index 0000000..9dd3fb7 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/OnHelpShowListener.java @@ -0,0 +1,25 @@ +package com.shaya.poinila.android.presentation.view; + +import android.view.View; + +/** + * Created by iran on 5/28/2016. + */ +public interface OnHelpShowListener { + + public void onHelpShow(); + + public void setSelected(boolean status); + + public boolean isSelected(); + + public void setReady(boolean status); + + public boolean isReady(); + + public void setViewHelpStatus(boolean status); + + public boolean isViewedHelp(); + + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/PageSelectedListener.java b/src/main/java/com/shaya/poinila/android/presentation/view/PageSelectedListener.java new file mode 100755 index 0000000..ccb3e79 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/PageSelectedListener.java @@ -0,0 +1,10 @@ +package com.shaya.poinila.android.presentation.view; + +import com.shaya.poinila.android.presentation.view.fragments.BusFragment; + +/** + * Created by iran on 7/4/2016. + */ +public interface PageSelectedListener { + public void onReadyPage(BusFragment fragment, int position); +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/ViewInflater.java b/src/main/java/com/shaya/poinila/android/presentation/view/ViewInflater.java new file mode 100755 index 0000000..2bc576a --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/ViewInflater.java @@ -0,0 +1,139 @@ +package com.shaya.poinila.android.presentation.view; + +import android.content.Context; +import android.support.v7.widget.CardView; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import com.makeramen.roundedimageview.Corner; +import com.makeramen.roundedimageview.RoundedTransformationBuilder; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.ExploreTagEvent; +import com.shaya.poinila.android.presentation.uievent.RemoveTagEvent; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.ResourceUtils; +import com.shaya.poinila.android.util.TimeUtils; +import com.squareup.picasso.Transformation; + +import org.apmem.tools.layouts.FlowLayout; + +import butterknife.ButterKnife; +import data.model.Comment; +import data.model.ImageUrls; +import data.model.Tag; +import manager.DataRepository; + +import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; + +/** + * Created by AlirezaF on 7/22/2015. + */ +public class ViewInflater { + public static View inflateRemovableTag(Tag tag, Context context) { + final View tagView = LayoutInflater.from(context).inflate(R.layout.tag_removable, null); + ButterKnife.findById(tagView, R.id.removeButton).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new RemoveTagEvent(tagView)); + } + }); + ((TextView)ButterKnife.findById(tagView, R.id.tag)).setText(tag.name); + return tagView; + } + + public static void addTagToSearchBar(Tag tag, Context context, RelativeLayout rlContainer) { + final View tagView = LayoutInflater.from(context).inflate(R.layout.tag_removable, rlContainer, false); + tagView.setId(rlContainer.getChildCount()); + ButterKnife.findById(tagView, R.id.removeButton).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new RemoveTagEvent(tagView)); + } + }); + ((TextView) ButterKnife.findById(tagView, R.id.tag)).setText(tag.name); + View lastChild = rlContainer.getChildAt(rlContainer.getChildCount() - 1); + + RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT); + lp.addRule(RelativeLayout.LEFT_OF, lastChild.getId()); + lp.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE); + rlContainer.addView(tagView, rlContainer.getChildCount(), lp); + } + + /* public static View inflateNormalTag(Tag tag, Context context) { + TextView tagView = (TextView) LayoutInflater.from(context). + inflate(R.layout.tag_text_view, null); + tagView.setText(tag.name); + return tagView; + }*/ + + public static View inflateNormalTag(String tag, Context context) { + TextView tagView = (TextView) LayoutInflater.from(context). + inflate(R.layout.tag_text_view, null); + tagView.setText(tag); + return tagView; + } + + public static View inflateComment(Comment comment, Context context){ + View commentView = LayoutInflater.from(context).inflate(R.layout.comment, null); + ViewUtils.setImage((ImageView) commentView.findViewById(R.id.image), + comment.commenter.imageUrls, ImageUrls.ImageType.MEMBER, ImageUrls.ImageSize.AVATAR); + ((TextView)commentView.findViewById(R.id.name)).setText(comment.commenter.fullName); + ((TextView)commentView.findViewById(R.id.date_created)). + setText(TimeUtils.getTimeString(comment.creationDate, DataRepository.getInstance().getServerTimeDifference())); + ((TextView)commentView.findViewById(R.id.comment_content)). + setText(comment.content); + + return commentView; + } + + public static void addTagToContainer(FlowLayout tagsContainer, Tag tag){//Tag tag) { + final TextView tagView = (TextView) LayoutInflater.from(tagsContainer.getContext()). + inflate(R.layout.tag_text_view, tagsContainer, false); + tagView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new ExploreTagEvent(tagView.getText().toString())); + } + }); + tagView.setText(tag.name);//.name) + ViewUtils.setFont(tagView, tagView.getContext().getString(R.string.default_bold_font_path)); +// ; + tagsContainer.addView(tagView); + } + + public static void addRemovableTagToContainer(final FlowLayout tagsContainer, String tagText) { + final LinearLayout tagView = (LinearLayout) LayoutInflater.from(tagsContainer.getContext()). + inflate(R.layout.tag_removable, tagsContainer, false); + + TextView tag = ButterKnife.findById(tagView, R.id.tag); + tag.setText(tagText);//.name); + tagView.findViewById(R.id.removeButton).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new RemoveTagEvent(tagsContainer.indexOfChild(tagView))); + } + }); + + tagsContainer.addView(tagView); + } + + public static View inflateImageCaption(LinearLayout ll, String name, ImageUrls coverImageUrl) { + Transformation transformation = new RoundedTransformationBuilder() + .cornerRadiusDp(Corner.TOP_LEFT, ResourceUtils.getDimen(R.dimen.corner_lvlhalf)) + .cornerRadiusDp(Corner.TOP_RIGHT, ResourceUtils.getDimen(R.dimen.corner_lvlhalf)) + .oval(false) + .build(); + CardView card = (CardView) LayoutInflater.from(ll.getContext()).inflate(R.layout.image_caption, ll, false); + ViewUtils.setText(((TextView) card.findViewById(R.id.caption)), name); + ViewUtils.setFont(((TextView) card.findViewById(R.id.caption)), ll.getContext().getString(R.string.default_font_path)); + ViewUtils.setImage((ImageView) card.findViewById(R.id.image), coverImageUrl, ImageUrls.ImageType.COLLECTION, ImageUrls.ImageSize.BIG, transformation); + return card; + } + + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/ViewUtils.java b/src/main/java/com/shaya/poinila/android/presentation/view/ViewUtils.java new file mode 100755 index 0000000..7468bba --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/ViewUtils.java @@ -0,0 +1,460 @@ +package com.shaya.poinila.android.presentation.view; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.Typeface; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Looper; +import android.support.annotation.NonNull; +import android.support.annotation.StringRes; +import android.support.design.widget.TextInputLayout; +import android.support.v4.content.ContextCompat; +import android.support.v4.text.BidiFormatter; +import android.text.Editable; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.util.Log; +import android.util.Patterns; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.makeramen.roundedimageview.RoundedImageView; +import com.mobsandgeeks.saripaar.ValidationError; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.NotifParticipantClickedUIEvent; +import com.shaya.poinila.android.presentation.view.costom_view.AspectRatioImageView; +import com.shaya.poinila.android.presentation.view.costom_view.SvgMaskedImageView; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.ContextHolder; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.util.ResourceUtils; +import com.squareup.picasso.Picasso; +import com.squareup.picasso.RequestCreator; +import com.squareup.picasso.Transformation; + +import java.util.Arrays; +import java.util.List; +import java.util.regex.Pattern; + +import butterknife.ButterKnife; +import data.model.Image; +import data.model.ImageUrls; +import data.model.Notification; +import data.model.Participant; +import de.hdodenhof.circleimageview.CircleImageView; +import static com.shaya.poinila.android.util.ResourceUtils.getDimen; +import static com.shaya.poinila.android.util.ResourceUtils.getString; +import static com.shaya.poinila.android.util.ResourceUtils.getStringFormatted; +import static com.shaya.poinila.android.util.StringUtils.persianNumber; + +/** + * checks field for being null avoiding NullPointerException. In case of imagesUrls set placeholder + * also. + */ +public class ViewUtils { + + public static void setText(TextView textView, CharSequence text) { + if (textView != null && text != null) { + /*if (textView.getVisibility() == View.GONE) + textView.setVisibility(View.VISIBLE);*/ + textView.setText(text); + } + /*else if (textView != null) + textView.setVisibility(View.GONE);*/ + } + + public static void setFont(TextView textView, String path){ + Typeface typeface = Typeface.createFromAsset(textView.getContext().getAssets(), path); + textView.setTypeface(typeface); + } + + public static void setText(TextView textView, int number) { + setText(textView, persianNumber(number)); + } + + public static void setImage(ImageView imageView, ImageUrls urls, + ImageUrls.ImageType imageType, ImageUrls.ImageSize imageSize) { + setImage(imageView, urls, imageType, imageSize, null); + } + + public static void setImage(ImageView imageView, ImageUrls urls, + ImageUrls.ImageType imageType, ImageUrls.ImageSize imageSize, @NonNull Transformation transformation) { + if (imageView == null) + return; + Drawable placeHolderDrawable = properPlaceHolder(urls, imageType, imageSize); + Image properImage = null; + if (urls != null && urls.isNotEmpty()) { + switch (imageType) { + case POST: + properImage = urls.properPostImage(imageSize); + if (imageSize != ImageUrls.ImageSize.AVATAR) { // maintain ratio + //imageView.getLayoutParams().height = imageView.getLayoutParams().width * properImage.height / properImage.width; + ((AspectRatioImageView) imageView).setAspectRatio(properImage.height * 1f / properImage.width); + imageView.requestLayout(); + } + break; + case COLLECTION: + properImage = urls.properCollectionImage(imageSize); + break; + case MEMBER: + properImage = urls.properMemberImage(imageSize); + break; + case INTEREST: + properImage = urls.interest; + break; + } + } + handleImage(imageView, properImage, placeHolderDrawable, transformation); + } + + private static Drawable properPlaceHolder(ImageUrls urls, ImageUrls.ImageType imageType, ImageUrls.ImageSize imageSize) { + if (urls != null && !TextUtils.isEmpty(urls.dominantColor)) { + if (!urls.dominantColor.contains("#")) + urls.dominantColor = "#" + urls.dominantColor; + return new ColorDrawable(Color.parseColor(urls.dominantColor)); + } + + int defaultResId = 0; + switch (imageType) { + case POST: + if (imageSize == ImageUrls.ImageSize.AVATAR)// maintain ratio + defaultResId = R.drawable.post_no_image; + break; + case COLLECTION: + defaultResId = R.drawable.collection_no_image; + break; + case MEMBER: + if (imageSize == ImageUrls.ImageSize.BIG) + defaultResId = R.drawable.user_no_image_big; + else if (imageSize == ImageUrls.ImageSize.AVATAR) + defaultResId = R.drawable.user_no_image; + break; + } + return defaultResId == 0 ? null : ContextCompat.getDrawable(ContextHolder.getContext(), defaultResId); + } + + private static void handleImage(ImageView imageView, Image properImage, Drawable placeHolderDrawable, + Transformation transformation) { + if (properImage != null) { + RequestCreator creator = Picasso.with(imageView.getContext()).load(properImage.url); + if (placeHolderDrawable != null) + creator.placeholder(placeHolderDrawable).into(imageView); + if (transformation != null) + creator.transform(transformation); + creator.into(imageView); + } else { + imageView.setImageDrawable(placeHolderDrawable); + } + } + + public static void setNotificationTitle(TextView titleView, Notification notification) { + List participants = notification.participants; + switch (notification.type) { + case FRIENDS_FOLLOWED_COLLECTIONS: + case FRIENDS_LIKED_POSTS: + case FRIENDS_CREATED_COLLECTIONS: + setText(titleView, notification.mainActor.userName); + return; + } + + String Space = " "; + String AND = ResourceUtils.getString(R.string.and); + String OTHER_PEOPLE = ResourceUtils.getString(R.string.other_people); + + StringBuilder title = new StringBuilder(); + title.append("%s"); + if (participants.size() > 1) { + String participantsCount = persianNumber(participants.size() - 1); + title.append(Space).append(AND).append(Space).append(participantsCount).append(Space).append(OTHER_PEOPLE); + } + /*int participantsLimit = ResourceUtils.getInteger(R.integer.participants_show_limit); + if (participants.size() > participantsLimit){ + for (int i = 0; i < participantsLimit; i++) { + title.append(participants.get(i).uniqueName); + if (i != participantsLimit) + } + title.append(AND).append(participants.size() - participantsLimit).append(" ").append(OTHER_PEOPLE); + } + else if (participants.size() == participantsLimit){ + title.append(participants.get(0).uniqueName).append(COMMA). + append(participants.get(1).uniqueName).append(AND).append(participants.get(2).uniqueName); + } + else if (participants.size() == 2){ + title.append(participants.get(0).uniqueName).append(AND).append(participants.get(1).uniqueName); + } + else{ // == 1 + title.append(participants.get(0).uniqueName); + }*/ + /*BidiFormatter.getInstance(true).unicodeWrap(title.toString())*/ + String wrappedUniqueName = BidiFormatter.getInstance(true /* rtlContext */).unicodeWrap(participants.get(0).userName); + String formattedText = String.format(title.toString(), wrappedUniqueName); + setText(titleView, formattedText); + } + + public static void setNotificationImages(ViewGroup imageContainer, + List participants, + ImageUrls.ImageType participantImageType) { + int participantsLimit = ResourceUtils.getInteger(R.integer.participants_show_limit); + Context context = imageContainer.getContext(); + imageContainer.removeAllViews(); + int participantsSize = (int) getDimen(R.dimen.participant_size); + + for (int i = 0; i < participantsLimit && i < participants.size(); i++) { + ImageView imageView = null; + ImageUrls urls = participants.get(i).imageUrls; + switch (participantImageType) { + case MEMBER: + imageView = new CircleImageView(context); + break; + case POST: + imageView = new SvgMaskedImageView(context); + break; + case COLLECTION: + imageView = new RoundedImageView(context); + int corner = ((int) getDimen(R.dimen.corner_lvlhalf)); + ((RoundedImageView) imageView).setCornerRadius(corner); + break; + } + + imageContainer.addView(imageView, 0); + + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( + participantsSize, participantsSize); + params.setMargins(0, 0, ((int) getDimen(R.dimen.margin_lvlhalf)), 0); + imageView.setLayoutParams(params); + imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); + setParticipantClickListener(imageView, participants.get(i), participantImageType); + if (urls == null) { + Picasso.with(imageView.getContext()).load(getDefaultNotifImage(participantImageType)).into(imageView); + } else { + setImage(imageView, urls, participantImageType, ImageUrls.ImageSize.AVATAR); + } + } + if (participants.size() > participantsLimit) { + TextView moreParticipant = new TextView(context); + moreParticipant.setLayoutParams(new ViewGroup.LayoutParams(participantsSize, participantsSize)); + moreParticipant.setGravity(Gravity.CENTER); + + imageContainer.addView(moreParticipant); + setText(moreParticipant, "+" + persianNumber(participants.size() - participantsLimit)); + } + } + + private static void setParticipantClickListener( + ImageView imageView, final Participant participant, final ImageUrls.ImageType participantImageType) { + imageView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post( + new NotifParticipantClickedUIEvent(participant, participantImageType)); + } + }); + } + + public static void enableLayoutChildes(ViewGroup viewGroup, boolean enable) { + viewGroup.setEnabled(enable); + for (int i = 0; i < viewGroup.getChildCount(); i++) { + View child = viewGroup.getChildAt(i); + child.setEnabled(enable); + if (child instanceof ViewGroup) + enableLayoutChildes(((ViewGroup) child), enable); + } + } + + public static int getDefaultNotifImage(ImageUrls.ImageType type) { + switch (type) { + case COLLECTION: + return R.drawable.collection_no_image; + case POST: + return R.drawable.post_no_image; + case MEMBER: + return R.drawable.user_no_image; + } + return -1; + } + + public static void removeView(final ViewGroup parentView, final int position) { + new Handler().postAtFrontOfQueue(new Runnable() { + @Override + public void run() { + parentView.removeViewAt(position); //ButterKnife.findById(container, R.actorID.dialog_content) + } + }); + } + + public static void removeView(final ViewGroup parentView, final View removeView) { + new Handler().postAtFrontOfQueue(new Runnable() { + @Override + public void run() { + parentView.removeView(removeView); //ButterKnife.findById(container, R.actorID.dialog_content) + } + }); + } + + + public static boolean validateInputs(EditText[] requiredItems, int[] minLengths, EditText[] limitedItems, int[] maxLengths) { + for (int i = 0; i < requiredItems.length; i++) { + validateInputMinLength(requiredItems[i], minLengths[i]); + } + for (int i = 0; i < limitedItems.length; i++) { + validateInputMaxLength(limitedItems[i], maxLengths[i]); + } + return true; + } + + public static boolean validatePasswordInput(EditText passwordInput){ + return validateInputMinLength(passwordInput, ConstantsUtils.min_length_password); + } + + public static boolean validateInputMinLength(EditText requiredInput, int minLength){ + String text = requiredInput.getText().toString().trim(); + if (TextUtils.isEmpty(text)) { + setInputError(requiredInput, getString(R.string.error_required_field)); + return false; + } else if (text.length() < minLength) { + setInputError(requiredInput, getStringFormatted(R.string.error_min_formatted, minLength)); + return false; + } + return true; + } + + public static boolean validateInputMaxLength(EditText editText, int maxLength) { + String text = editText.getText().toString().trim(); + if (text.length() > maxLength) { + setInputError(editText, getStringFormatted(R.string.error_max_formatted, maxLength)); + return false; + } + return true; + } + + public static boolean validateEdittextsByRegex(EditText[] regexItems, Pattern[] regexPatterns, @StringRes int[] regexErrors) { + for (int i = 0; i < regexItems.length; i++) { + String text = regexItems[i].getText().toString().trim(); + if (!regexPatterns[i].matcher(text).matches()) { + setInputError(regexItems[i], getString(regexErrors[i])); + return false; + } + } + return true; + } + + public static boolean validateEntityName(EditText editText) { + return validateEdittextsByRegex(new EditText[]{editText}, new Pattern[]{ConstantsUtils.entitiesNamePattern}, new int[]{R.string.error_entity_name}); + } + + public static void temporaryError(final EditText editText, String error) { + editText.setError(error); + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { + @Override + public void run() { + editText.setError(null); + } + }, 2000); + } + + public static void setInputError(final EditText editText, @StringRes int errorResId){ + setInputError(editText, getString(errorResId)); + } + + public static void setInputError(final EditText editText, String error) { + if (editText.getParent() instanceof TextInputLayout) { + final TextInputLayout inputLayout = (TextInputLayout) editText.getParent(); + inputLayout.setErrorEnabled(true); + inputLayout.setError(error); + TextWatcher inputLayoutTextWatcher = null; + final TextWatcher finalInputLayoutTextWatcher = inputLayoutTextWatcher; + inputLayoutTextWatcher = new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + inputLayout.setErrorEnabled(false); + inputLayout.setError(null); + editText.removeTextChangedListener(finalInputLayoutTextWatcher); + } + + @Override + public void afterTextChanged(Editable s) { + } + }; + editText.addTextChangedListener(inputLayoutTextWatcher); + } else { + temporaryError(editText, error); + } + } + + public static boolean validateImage(Bitmap cropAvatar, int minDimensionSize) { + if(cropAvatar == null ){ + Logger.toastError(R.string.error_no_image_found); + return false; + } + if (cropAvatar.getWidth() < minDimensionSize || cropAvatar.getHeight() < minDimensionSize) { + Logger.toastError(R.string.error_small_image); + return false; + } + return true; + } + + public static void setViewsVisibilityToVisible(View... views) { + setViewsVisibilityToVisible(Arrays.asList(views)); + } + + public static void setViewsVisibilityToGone(View... views) { + setViewsVisibilityToGone(Arrays.asList(views)); + } + + public static void setViewsVisibilityToVisible(List views) { + ButterKnife.apply(views, VISIBILITY, View.VISIBLE); + } + + public static void setViewsVisibilityToGone(List views) { + ButterKnife.apply(views, VISIBILITY, View.GONE); + } + + static final ButterKnife.Setter VISIBILITY = new ButterKnife.Setter() { + @Override + public void set(View view, Integer value, int index) { + view.setVisibility(value); + } + }; + + public static void setValueAndDisableTextView(TextView textView, String value, boolean disableField) { + textView.setVisibility(View.VISIBLE); + textView.setEnabled(!disableField); + textView.setText(value); + } + + public static void setValueAndDisableInputLayout(TextInputLayout inputLayout, String value, boolean disableField) { + if (disableField) { + inputLayout.setCounterEnabled(false); + inputLayout.setErrorEnabled(false); + inputLayout.setError(null); + } + setValueAndDisableTextView(inputLayout.getEditText(), value, disableField); + } + + public static void handleSaripaarErrors(List errors, Context context) { + for (ValidationError error : errors) { + if (error.getView() instanceof TextView) { + setInputError(((EditText) error.getView()), + error.getCollatedErrorMessage(context)); + } + } + } + + public static boolean validateUrl(EditText urlInput) { + return validateEdittextsByRegex(new EditText[]{urlInput}, + new Pattern[]{Patterns.WEB_URL}, new int[]{R.string.error_invalid_url}); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/AppSettingActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/AppSettingActivity.java new file mode 100755 index 0000000..ea09f9c --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/AppSettingActivity.java @@ -0,0 +1,51 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.annotation.TargetApi; +import android.os.Build; +import android.os.Bundle; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.view.View; +import android.view.ViewGroup; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.fragments.AppSettingFragment; + +/** + * Created by iran on 2015-11-07. + */ +public class AppSettingActivity extends AppCompatActivity{ + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + ViewGroup vp = (ViewGroup) getLayoutInflater().inflate(R.layout.activity_fragment_host, null); + Toolbar toolbar = (Toolbar) getLayoutInflater().inflate(R.layout.toolbar, vp, false); + //toolbar = ButterKnife.findById(vp, R.actorID.toolbar); + vp.addView(toolbar, 0); + setContentView(vp); + + setSupportActionBar(toolbar); + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setHomeButtonEnabled(true); + } + + //forceRTLIfSupported(); + + // Display the fragment as the main content. + getFragmentManager().beginTransaction() + .replace(R.id.content, new AppSettingFragment()) + .commit(); + + } + + /*@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + private void forceRTLIfSupported() + { + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){ + getWindow().getDecorView().setLayoutDirection(View.LAYOUT_DIRECTION_RTL); + } + }*/ +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/BaseActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/BaseActivity.java new file mode 100755 index 0000000..77f0903 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/BaseActivity.java @@ -0,0 +1,258 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.app.Activity; +import android.app.ProgressDialog; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ActivityInfo; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.support.v4.app.NavUtils; +import android.support.v4.app.TaskStackBuilder; +import android.support.v4.content.LocalBroadcastManager; +import android.support.v7.app.AppCompatActivity; +import android.text.TextUtils; +import android.util.Log; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; + +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.NavigationUtils; +import com.shaya.poinila.android.util.PoinilaPreferences; + +import butterknife.ButterKnife; +import manager.DataRepository; +import uk.co.chrisjenx.calligraphy.CalligraphyContextWrapper; + +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static com.shaya.poinila.android.util.ConstantsUtils.INTENT_FILTER_JWT; +import static com.shaya.poinila.android.util.ConstantsUtils.INTENT_FILTER_SERVER_TIME; + +/** + * Created by iran on 2015-06-21. + * + * @author Alireza Farahani + */ +public abstract class BaseActivity extends AppCompatActivity { + + public static final int MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 100; + public static final int MY_PERMISSIONS_REQUEST_RECEIVE_SMS = 200; + protected boolean requestOnFirstTime = true; + + protected String titleParameter; + protected ProgressDialog progressDialog; + private View progressView; + private ViewGroup rootView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + handleIntentExtras(); + if(isForcePortrait()) + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + + // TODO: crash reporting here. + /* Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { + @Override + public void uncaughtException(Thread paramThread, Throwable paramThrowable) { + if (paramThrowable instanceof AuthorizationException) { + + } else { + paramThrowable.printStackTrace(); + } + } + });*/ + setContentView(getActivityView()); + + ButterKnife.bind(this); + + initLoadingDialog(); + + initUI(); + + } + + public boolean isForcePortrait(){ + return true; + } + + private void initLoadingDialog() { + progressDialog = new ProgressDialog(this); + //progressDialog.setTitle(R.string.loading); + progressDialog.setMessage(getString(R.string.please_wait)); + progressDialog.setCanceledOnTouchOutside(false); + progressDialog.setCancelable(false); + } + + protected void showProgressDialog(){ + progressDialog.show(); + } + + protected void dismissProgressDialog(){ + progressDialog.dismiss(); + } + + // Our handler for received Intents. This will be called whenever an Intent + // with an action named "custom-event-name" is broadcasted. + private BroadcastReceiver mJWTReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // Get extra data included in the Intent + String activityName = BaseActivity.this.getClass().getSimpleName(); + if (activityName.equals(SignUpLoginActivity.class.getSimpleName()) || + activityName.equals(SplashActivity.class.getSimpleName()) || + activityName.equals(HelpActivity.class.getSimpleName())) + return; + + /*PoinilaPreferences.putAuthToken(null); + DataRepository.setUserAsAnonymous(true);*/ + DataRepository.logout(); + PageChanger.goToLoginActivity(BaseActivity.this); + } + }; + + private BroadcastReceiver mServerTimeReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + long timeDifference = intent.getLongExtra(ConstantsUtils.KEY_TIME_DIFFERENCE, (long) (3.5 * 60 * 60 * 1000)); // ba utc 3.5 hour tafavot darim + DataRepository.getInstance().putServerTimeDifference(timeDifference); + } + }; + +/* private boolean requestingIsLocked; + private BroadcastReceiver mRequestFailedReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + requestingIsLocked = false; + } + };*/ + + protected View getActivityView() { + return getLayoutInflater().inflate(getLayoutResourceId(), null); + } + + @Override + protected void onStart() { + // Register to receive messages. + // We are registering an observer (mMessageReceiver) to receive Intents + // with actions named "custom-event-name". + super.onStart(); + BusProvider.getBus().register(this); + LocalBroadcastManager.getInstance(this).registerReceiver(mJWTReceiver, + new IntentFilter(INTENT_FILTER_JWT)); + LocalBroadcastManager.getInstance(this).registerReceiver(mServerTimeReceiver, + new IntentFilter(INTENT_FILTER_SERVER_TIME)); + /*LocalBroadcastManager.getInstance(this).registerReceiver(mRequestFailedReceiver, + new IntentFilter(ConstantsUtils.INTENT_FILTER_REQUEST_FAILED));*/ + } + + @Override + protected void onStop() { + BusProvider.getBus().unregister(this); + LocalBroadcastManager.getInstance(this).unregisterReceiver(mJWTReceiver); + LocalBroadcastManager.getInstance(this).unregisterReceiver(mServerTimeReceiver); + super.onStop(); + } + + protected abstract void initUI(); + + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + switch (id) { + case R.id.action_settings: + return true; + case android.R.id.home: + handleUpNavigation(); + return true; + } + return super.onOptionsItemSelected(item); + } + + protected void handleUpNavigation() { + // I don't know if this trick is flawless or not! + // code from http://stackoverflow.com/a/20631508/1660013 + /*if (!TextUtils.isEmpty(NavUtils.getParentActivityName(this))) { +// NavUtils.navigateUpFromSameTask(this); + Intent upIntent = NavUtils.getParentActivityIntent(this); + if (NavUtils.shouldUpRecreateTask(this, upIntent) + || getIntent().getAction() != null) { // deep linked: force new stack + // create new task + TaskStackBuilder.create(this).addNextIntentWithParentStack(upIntent) + .startActivities(); + } else { + // Stay in same task + NavUtils.navigateUpTo(this, upIntent); + } + } else*/ finish(); + } + + protected abstract void handleToolbar(); + + + protected abstract int getLayoutResourceId(); + + protected Activity getActivity() { + return this; + } + + protected void handleIntentExtras() { + Bundle bundle = getIntent().getExtras(); + if (bundle != null) { + titleParameter = bundle.getString(ConstantsUtils.KEY_PAGE_TITLE_PARAMETER, ""); + } + } + +// protected void showProgressDialog() { +// //ViewUtils.enableLayoutChildes(((ViewGroup) findViewById(R.id.content_container)), false); +// //progressDialog.show(); +// +// //findViewById(android.R.id.content).setVisibility(View.INVISIBLE); +// rootView = (ViewGroup) findViewById(android.R.id.content); +// progressView = getLayoutInflater().inflate(R.layout.progress, rootView, false); +// ViewUtils.enableLayoutChildes(((ViewGroup) findViewById(android.R.id.content)), false); +// rootView.addView(progressView, MATCH_PARENT, MATCH_PARENT); +// +// new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { +// @Override +// public void run() { +// if (rootView.findViewById(R.id.progress_view) != null) +// dismissProgress(false); +// } +// }, ConstantsUtils.CONNECT_TIME_OUT_MILLISECONDS); +// } + + + protected void dismissProgress(boolean successful) { + rootView.removeView(progressView); + if (successful) { + ViewUtils.enableLayoutChildes(((ViewGroup) findViewById(android.R.id.content)), true); + } else { + ViewUtils.enableLayoutChildes(((ViewGroup) findViewById(R.id.toolbar)), true); + } + } + + public void onSuccessfulResponse() { + dismissProgress(true); + } + + + @Override + protected void attachBaseContext(Context newBase) { + super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase)); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/ChangePasswordActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/ChangePasswordActivity.java new file mode 100755 index 0000000..5ed9896 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/ChangePasswordActivity.java @@ -0,0 +1,105 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.os.Bundle; +import android.support.design.widget.TextInputEditText; +import android.text.InputType; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.EditText; +import android.widget.ImageView; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.squareup.otto.Subscribe; + +import butterknife.Bind; +import butterknife.OnClick; +import data.PoinilaNetService; +import data.event.ServerResponseEvent; +import uk.co.chrisjenx.calligraphy.CalligraphyConfig; +import uk.co.chrisjenx.calligraphy.CalligraphyUtils; + +public class ChangePasswordActivity extends ToolbarActivity{ + + + @Bind(R.id.first_text_field) + TextInputEditText oldPasswordField; + + @Bind(R.id.second_text_field) + TextInputEditText newPasswordField; + private boolean passwordVisible; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + protected void initUI() { + + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_change_pass, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + if (id == R.id.action_submit_changes){ + if (validate()) { + PoinilaNetService.changePassword(newPasswordField.getText().toString(), oldPasswordField.getText().toString()); + } + }else if (id == R.id.action_toggle_visibility){ + toggleVisibility(item); + } + + //noinspection SimplifiableIfStatement + + return super.onOptionsItemSelected(item); + } + + private void toggleVisibility(MenuItem item) { + passwordVisible ^= true; + for (EditText passwordInput : new EditText[]{oldPasswordField, newPasswordField}) { + int start = passwordInput.getSelectionStart(); + int end = passwordInput.getSelectionEnd(); + passwordInput.setInputType(passwordVisible ? + InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD : + InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); + CalligraphyUtils.applyFontToTextView(getActivity(), passwordInput, CalligraphyConfig.get().getFontPath()); + passwordInput.setSelection(start, end); + + item.setIcon(passwordVisible ? + R.drawable.invisible_black_24dp : + R.drawable.visible_black_24dp); + } + } + + private boolean validate() { + return ViewUtils.validatePasswordInput(newPasswordField); + } + + @Override + protected int getLayoutResourceId() { + return R.layout.activity_change_password; + } + + + @Subscribe public void onServerResponse(ServerResponseEvent event){ + if (!event.succeed){ + switch (event.errorCode){ + case 447: + ViewUtils.setInputError(oldPasswordField, R.string.error_old_password_wrong); + break; + } + } + } + + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/ChromeActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/ChromeActivity.java new file mode 100755 index 0000000..c85e5a6 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/ChromeActivity.java @@ -0,0 +1,161 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.provider.Browser; +import android.support.annotation.Nullable; +import android.support.customtabs.CustomTabsCallback; +import android.support.customtabs.CustomTabsClient; +import android.support.customtabs.CustomTabsIntent; +import android.support.customtabs.CustomTabsServiceConnection; +import android.support.customtabs.CustomTabsSession; +import android.text.TextUtils; +import android.util.Log; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.utils.Utils; + +import java.lang.ref.WeakReference; + +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_WEBSITE_URL; + +/** + * Created by iran on 6/19/2016. + */ +public class ChromeActivity extends Activity { + + private CustomTabsSession mCustomTabsSession; + private CustomTabsClient mClient; + private CustomTabsServiceConnection mConnection; + private String mPackageNameToBind; + private static WeakReference sCurrentSession; + + String url; + String referrer; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.chrome_activity); + + url = getIntent().getStringExtra(KEY_WEBSITE_URL); + referrer = getIntent().getStringExtra("referrer"); + + bindCustomTabsService(); + + } + + private void bindCustomTabsService() { + if (mClient != null) return; + mPackageNameToBind = Utils.getBrowserAvailablePackageName(); + if (TextUtils.isEmpty(mPackageNameToBind)) { + finish(); + } + + mConnection = new CustomTabsServiceConnection() { + @Override + public void onCustomTabsServiceConnected(ComponentName componentName, CustomTabsClient customTabsClient) { + mClient = customTabsClient; + + if(mClient != null){ + mClient.warmup(0); + CustomTabsSession session = getSession(); + if (mClient != null && session != null) session.mayLaunchUrl(Uri.parse(url), null, null); + launchUrl(); + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + finish(); + } + }; + + CustomTabsClient.bindCustomTabsService(ChromeActivity.this, mPackageNameToBind, mConnection); + + + } + + @Override + protected void onDestroy() { + unbindCustomTabsService(); + super.onDestroy(); + } + + private void unbindCustomTabsService() { + if (mConnection == null) return; + unbindService(mConnection); + mClient = null; + mCustomTabsSession = null; + } + + private CustomTabsSession getSession() { + if (mClient == null) { + mCustomTabsSession = null; + } else if (mCustomTabsSession == null) { + mCustomTabsSession = mClient.newSession(new CustomTabsCallback(){ + @Override + public void onNavigationEvent(int navigationEvent, Bundle extras) { + super.onNavigationEvent(navigationEvent, extras); + + switch (navigationEvent){ + case 6: // Go to Parent Activity + ChromeActivity.this.finish(); + break; + } + } + }); + setCurrentSession(mCustomTabsSession); + } + return mCustomTabsSession; + } + + /** + * @return The current {@link CustomTabsSession} object. + */ + public static @Nullable + CustomTabsSession getCurrentSession() { + return sCurrentSession == null ? null : sCurrentSession.get(); + } + + /** + * Sets the current session to the given one. + * @param session The current session. + */ + public static void setCurrentSession(CustomTabsSession session) { + sCurrentSession = new WeakReference(session); + } + + private void launchUrl(){ + CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(getSession()); + builder + .setToolbarColor(getResources().getColor(R.color.poinila_gray)) + .setShowTitle(true); +// prepareMenuItems(builder); +// prepareActionButton(builder); +// prepareBottombar(builder); +// builder.setStartAnimations(this, R.anim.slide_in_right, R.anim.slide_out_left); +// builder.setExitAnimations(this, R.anim.slide_in_left, R.anim.slide_out_right); +// builder.setCloseButtonIcon( +// BitmapFactory.decodeResource(getResources(), R.drawable.ic_arrow_back)); + CustomTabsIntent customTabsIntent = builder.build(); + if(!TextUtils.isEmpty(referrer)) + customTabsIntent.intent.putExtra(Intent.EXTRA_REFERRER,Uri.parse(Intent.URI_ANDROID_APP_SCHEME + "//" + getPackageName())); + customTabsIntent.launchUrl(this, Uri.parse(url)); + } + +// private void prepareMenuItems(CustomTabsIntent.Builder builder) { +// Intent menuIntent = new Intent(); +// menuIntent.setClass(getApplicationContext(), this.getClass()); +// // Optional animation configuration when the user clicks menu items. +// Bundle menuBundle = ActivityOptions.makeCustomAnimation(this, android.R.anim.slide_in_left, +// android.R.anim.slide_out_right).toBundle(); +// PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0, menuIntent, 0, +// menuBundle); +// builder.addMenuItem("Menu entry 1", pi); +// } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/CirclesManagementActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/CirclesManagementActivity.java new file mode 100755 index 0000000..39f2fcf --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/CirclesManagementActivity.java @@ -0,0 +1,138 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.os.Bundle; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewProvider; +import com.shaya.poinila.android.presentation.uievent.DeleteCircleUIEvent; +import com.shaya.poinila.android.presentation.uievent.EditCircleNameUIEvent; +import com.shaya.poinila.android.presentation.uievent.PositiveButtonClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.SimpleSettingTextSetEvent; +import com.shaya.poinila.android.presentation.uievent.ViewCircleMembersUIEvent; +import com.shaya.poinila.android.presentation.view.dialog.ChangeCircleNameDialog; +import com.shaya.poinila.android.presentation.view.dialog.CircleMembersManagementDialog; +import com.shaya.poinila.android.presentation.view.dialog.NewCircleDialog; +import com.shaya.poinila.android.presentation.view.dialog.PoinilaAlertDialog; +import com.shaya.poinila.android.presentation.viewholder.CircleEditViewHolder; +import com.squareup.otto.Subscribe; + +import butterknife.Bind; +import data.PoinilaNetService; +import data.event.CircleReceivedEvent; +import data.model.Circle; +import manager.DBFacade; + +public class CirclesManagementActivity extends ToolbarActivity { + + @Bind(R.id.recycler_view) + RecyclerView mRecyclerView; + private RecyclerViewAdapter mAdapter; + private int clickedCirclePosition; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + + + @Override + protected void initUI() { + mRecyclerView = new RecyclerViewProvider(mRecyclerView). + setLinearLayoutManager(LinearLayoutManager.VERTICAL). + setAdapter(new RecyclerViewAdapter(getActivity(), R.layout.circle_edit_item) { + @Override + protected CircleEditViewHolder getProperViewHolder(View v, int viewType) { + return new CircleEditViewHolder(v); + } + }).bindViewToAdapter(); + mAdapter = (RecyclerViewAdapter) mRecyclerView.getAdapter(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_circles_management, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + if (id == R.id.action_add_circle){ + new NewCircleDialog().show(getSupportFragmentManager(), null); + } + //noinspection SimplifiableIfStatement + + return super.onOptionsItemSelected(item); + } + + @Override + protected void onStart() { + super.onStart(); + + mAdapter.resetData(DBFacade.getMyCircles()); + + } + + @Subscribe public void onViewMembersEvent(ViewCircleMembersUIEvent event){ + String circleID = mAdapter.getItem(event.adapterPosition).getId(); + CircleMembersManagementDialog.newInstance(circleID).show(getSupportFragmentManager(), null); + } + + @Subscribe public void onEditCircleNameClick(final EditCircleNameUIEvent event){ + clickedCirclePosition = event.adapterPosition; + ChangeCircleNameDialog.newInstance(mAdapter.getItem(clickedCirclePosition).name).show(getSupportFragmentManager(), null); + } + + @Subscribe public void onCircleChange(SimpleSettingTextSetEvent event){ + switch (event.settingType) { + case CIRCLE_NAME: + Circle editedCircle = mAdapter.getItem(clickedCirclePosition); + editedCircle.name = event.value; + mAdapter.notifyItemChanged(clickedCirclePosition); + PoinilaNetService.updateCircle(editedCircle); + break; + case NEW_CIRCLE: + PoinilaNetService.createCircle(event.value); + break; + } + } + + @Subscribe public void onDeleteCircle(DeleteCircleUIEvent event){ + clickedCirclePosition = event.adapterPosition; + new PoinilaAlertDialog.Builder().setMessage(R.string.confirm_delete_circle). + setPositiveBtnText(R.string.yes).setNegativeBtnText(R.string.no). + build().show(getSupportFragmentManager(), null); + } + + @Subscribe + public void onPositiveDialogButton(PositiveButtonClickedUIEvent event) { + PoinilaNetService.deleteCircle(mAdapter.getItem(clickedCirclePosition)); + mAdapter.removeItem(clickedCirclePosition); + } + + + @Subscribe public void onCircleReceived(CircleReceivedEvent event){ + mAdapter.addItem(event.circle, 0); + } + + @Override + protected void handleToolbar() { + super.handleToolbar(); + setTitle(R.string.title_activity_circles_management); + } + + @Override + protected int getLayoutResourceId() { + return R.layout.activity_circle_management; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/CollectionListActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/CollectionListActivity.java new file mode 100755 index 0000000..eec12dc --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/CollectionListActivity.java @@ -0,0 +1,29 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.app.Fragment; +import android.os.Bundle; + +import com.shaya.poinila.android.presentation.view.fragments.CollectionListFragment; + +public class CollectionListActivity extends FragmentHostActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + handleIntentExtras(); + super.onCreate(savedInstanceState); + } + + @Override + protected void initUI() {} + + @Override + protected android.support.v4.app.Fragment getHostedFragment() { + return CollectionListFragment.newInstance(mainEntityID, requestID); + } + + @Override + protected boolean withToolbar() { + return true; + } + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/CommentsListActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/CommentsListActivity.java new file mode 100755 index 0000000..bfb2551 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/CommentsListActivity.java @@ -0,0 +1,246 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v4.app.DialogFragment; +import android.support.v7.app.AlertDialog; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.View; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; +import android.widget.ImageButton; +import android.widget.TextView; +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewProvider; +import com.shaya.poinila.android.presentation.uievent.CommentLongClickUIEvent; +import com.shaya.poinila.android.presentation.uievent.MemberClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.RemoveItemUIEvent; +import com.shaya.poinila.android.presentation.uievent.UpdateUICommentEvent; +import com.shaya.poinila.android.presentation.viewholder.BaseViewHolder; +import com.shaya.poinila.android.presentation.viewholder.CommentViewHolder; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.Logger; +import com.squareup.otto.Subscribe; +import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration; + +import butterknife.Bind; +import butterknife.OnClick; +import data.PoinilaNetService; +import data.event.CommentReceivedEvent; +import data.event.CommentsReceivedEvent; +import data.model.Comment; +import data.model.Loading; +import manager.DBFacade; +import manager.DataRepository; + +import static com.shaya.poinila.android.presentation.uievent.UpdateUICommentEvent.DECREMENT_COMMENTS; +import static com.shaya.poinila.android.presentation.uievent.UpdateUICommentEvent.INCREMENT_COMMENTS; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; +import static com.shaya.poinila.android.presentation.view.ViewUtils.validateInputs; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_ITEM_COUNT; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_POST_ID; +import static com.shaya.poinila.android.util.ConstantsUtils.max_length_comment; +import static com.shaya.poinila.android.util.ConstantsUtils.min_length_comment; + +// TODO: change to fragment host activity +public class CommentsListActivity extends ToolbarActivity { + + @Bind(R.id.recycler_view) + RecyclerView mRecyclerListView; + + @Bind(R.id.item_count) + TextView mItemCountView; + + @Bind(R.id.comment_field) + EditText commentInputView; + + @Bind(R.id.send) ImageButton sendCommentBtn; + + private String postID; + private String bookmark; + private int itemCount; + + protected boolean hasLoading = false; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + //if (savedInstanceState == null) { + postID = getIntent().getStringExtra(KEY_POST_ID); + /*}else{ + postID = savedInstanceState.getString(KEY_POST_ID); + }*/ + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + outState.putString(KEY_POST_ID, postID); + super.onSaveInstanceState(outState); + } + + + @Subscribe public void onCommentsReceived(CommentsReceivedEvent event){ + getRecyclerViewAdapter().addItems(event.data); + + bookmark = event.bookmark; + requestOnFirstTime = false; + } + + public void setLoading(Loading loading){ + hasLoading = true; + getRecyclerViewAdapter().setLoading(loading); + } + + public void removeLoading(){ + if(hasLoading) { + hasLoading = false; + getRecyclerViewAdapter().removeLoading(); + } + } + + @Override + protected void onStart() { + super.onStart(); + + if(requestOnFirstTime){ + DataRepository.getInstance().getPostComments(postID, bookmark); + } + } + + @Override + protected void initUI() { + updateCountView(itemCount); + mRecyclerListView = new RecyclerViewProvider(mRecyclerListView). + setLinearLayoutManager(LinearLayoutManager.VERTICAL). + setAdapter(new RecyclerViewAdapter(getActivity(), R.layout.comment_large) { + @Override + protected BaseViewHolder getProperViewHolder(View v, int viewType) { + + if(viewType == VIEW_TYPE_LOAD_PROGRESS){ + return new BaseViewHolder.EmptyViewHolder(v); + } + + return new CommentViewHolder(v); + } + }).bindViewToAdapter(); + mRecyclerListView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()) + .marginResId(R.dimen.margin_lvl1) + .build()); + commentInputView.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + @Override + public void afterTextChanged(Editable s) { + sendCommentBtn.setEnabled(!s.toString().trim().isEmpty()); + } + }); + } + + @OnClick(R.id.send) public void sendComment(){ + if (DataRepository.isUserAnonymous()){ + Logger.toastError(R.string.error_guest_action); + return; + } + + EditText[] commentEditTextArr = new EditText[]{commentInputView}; + if (validateInputs(commentEditTextArr, new int[min_length_comment], commentEditTextArr, new int[]{max_length_comment})) { + PoinilaNetService.commentOnPost(postID, commentInputView.getText().toString()); + commentInputView.setText(""); + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(commentInputView.getWindowToken(), 0); + } + } + + @Subscribe public void onCommentSucceeded(CommentReceivedEvent event){ +// Log.i(getClass().getName(), "commenter = " + DBFacade.getCachedMyInfo()); + event.comment.commenter = DBFacade.getCachedMyInfo(); + getRecyclerViewAdapter().addItem(event.comment); + updateCountView(++itemCount); + BusProvider.getBus().post(new UpdateUICommentEvent(INCREMENT_COMMENTS, postID)); + BusProvider.getSyncUIBus().post(new UpdateUICommentEvent(INCREMENT_COMMENTS, postID)); + } + + @Subscribe public void onCommentLongClick(final CommentLongClickUIEvent event){ + CommentAction.newInstance(event).show(getSupportFragmentManager(), null); + } + + @Subscribe public void onDeleteComment(RemoveItemUIEvent event){ + PoinilaNetService.deleteComment(((Comment)getRecyclerViewAdapter().getItem(event.adapterPosition)).getId()); + getRecyclerViewAdapter().removeItem(event.adapterPosition); + updateCountView(--itemCount); + BusProvider.getBus().post(new UpdateUICommentEvent(DECREMENT_COMMENTS, postID)); + BusProvider.getSyncUIBus().post(new UpdateUICommentEvent(DECREMENT_COMMENTS, postID)); + + } + + @Subscribe public void onGoingToProfile(MemberClickedUIEvent event){ + PageChanger.goToProfile(getActivity(), + ((Comment)getRecyclerViewAdapter().getItem(event.adapterPosition)).commenter); + } + + + @Override + protected void handleToolbar() { + super.handleToolbar(); + setTitle(R.string.title_activity_comments_list); + } + + @Override + protected void handleIntentExtras() { + itemCount = getIntent().getIntExtra(KEY_ITEM_COUNT, 0); + } + + @Override + protected int getLayoutResourceId() { + return R.layout.activity_comments; + } + + public RecyclerViewAdapter getRecyclerViewAdapter() { + return ((RecyclerViewAdapter) mRecyclerListView.getAdapter()); + } + + private void updateCountView(int itemCount) { + setText(mItemCountView, getString(R.string.comments_formatted, itemCount)); + } + + + + public static class CommentAction extends DialogFragment{ + + CommentLongClickUIEvent event; + + public static CommentAction newInstance(CommentLongClickUIEvent event){ + CommentAction fragment = new CommentAction(); + fragment.event = event; + + return fragment; + } + + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setItems(getActivity().getResources().getStringArray(R.array.comment_options), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + // The 'which' argument contains the index position + // of the selected item + BusProvider.getBus().post(new RemoveItemUIEvent(event.adapterPosition)); + } + }); + return builder.create(); + } + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/CropImageActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/CropImageActivity.java new file mode 100755 index 0000000..8ab80be --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/CropImageActivity.java @@ -0,0 +1,103 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.graphics.Bitmap; +import android.net.Uri; +import android.support.v7.widget.Toolbar; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; + +import com.isseiaoki.simplecropview.CropImageView; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.presentation.view.costom_view.GalleryCameraImagePickerView; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.ImageUtils; +import com.shaya.poinila.android.util.Logger; + +import butterknife.Bind; +import butterknife.OnClick; +import data.PoinilaNetService; + +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_IMAGE_ADDRESS; +import static com.shaya.poinila.android.util.ConstantsUtils.PROFILE_PIC_MIN_DIMENSION; + +public class CropImageActivity extends ToolbarActivity { + + @Bind(R.id.crop_image_view) + GalleryCameraImagePickerView pickerView; + + @Bind(R.id.submit) Button submitBtn; + @Bind(R.id.cancel) Button cancelBtn; + + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_crop_image, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + if (id == R.id.doneButton){ + if (pickerView.showMode == GalleryCameraImagePickerView.ShowMode.Cropping){ + item.setIcon(R.drawable.crop_24dp); + pickerView.submitCrop(); + cancelBtn.setVisibility(View.VISIBLE); + submitBtn.setVisibility(View.VISIBLE); + }else if (pickerView.showMode == GalleryCameraImagePickerView.ShowMode.Showing){ + item.setIcon(R.drawable.done_flamingo_24dp); + pickerView.goToCropMode(); + cancelBtn.setVisibility(View.GONE); + submitBtn.setVisibility(View.GONE); + } + return true; + } + return super.onOptionsItemSelected(item); + } + + @OnClick(R.id.submit) public void onSubmit(){ + // TODO Refactoring: get REQUEST_ID from intent extras or pass the cropped bitmap and let + // the activity starter pager handle the case. + Bitmap cropAvatar = pickerView.getImage(); + if (ViewUtils.validateImage(cropAvatar, PROFILE_PIC_MIN_DIMENSION)) { + PoinilaNetService.uploadProfilePicture(cropAvatar); + onCancel(); + } + } + + @OnClick(R.id.cancel) public void onCancel(){ + finish(); + } + + @Override + protected void initUI() { + pickerView.policy = GalleryCameraImagePickerView.Policy.CropFullScreen; + pickerView.setImage(getIntent().getStringExtra(KEY_IMAGE_ADDRESS)); + pickerView.goToCropMode(); + } + + + @Override + protected int getLayoutResourceId() { + return R.layout.activity_crop_image; + } + + @Override + protected View getActivityView() { + ViewGroup vp = (ViewGroup) getLayoutInflater().inflate(getLayoutResourceId(), null); + toolbar = (Toolbar) getLayoutInflater().inflate(R.layout.toolbar_black, vp, false); + handleToolbar(); + vp.addView(toolbar, 0); + return vp; + } + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/EditInterestsActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/EditInterestsActivity.java new file mode 100755 index 0000000..8a569fe --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/EditInterestsActivity.java @@ -0,0 +1,30 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.app.Fragment; +import android.os.Bundle; +import android.support.v4.app.FragmentManager; + +import com.shaya.poinila.android.presentation.view.fragments.EditInterestsFragment; +public class EditInterestsActivity extends FragmentHostActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + protected android.support.v4.app.Fragment getHostedFragment() { + return EditInterestsFragment.newInstance(mainEntityID); + } + + @Override + protected boolean withToolbar() { + return true; + } + + @Override + protected void initUI() { + + } + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/ExploreActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/ExploreActivity.java new file mode 100755 index 0000000..d00fd54 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/ExploreActivity.java @@ -0,0 +1,16 @@ +package com.shaya.poinila.android.presentation.view.activity; + + + +import android.support.v4.app.Fragment; + +import com.shaya.poinila.android.presentation.view.fragments.PostListFragment; +import com.shaya.poinila.android.util.ConstantsUtils; + +public class ExploreActivity extends FragmentHostActivity{ + + @Override + protected Fragment getHostedFragment() { + return PostListFragment.newInstance(mainEntityID, ConstantsUtils.REQUEST_EXPLORE); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/FragmentHostActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/FragmentHostActivity.java new file mode 100755 index 0000000..7569e72 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/FragmentHostActivity.java @@ -0,0 +1,144 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.app.Fragment; +import android.os.Bundle; +import android.support.v7.app.ActionBar; +import android.support.v7.widget.Toolbar; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.util.ConstantsUtils; + +/** + * Created by iran on 2015-08-09. + */ +public abstract class FragmentHostActivity extends BaseActivity { + protected int requestID; + protected String mainEntityID; + protected String secondEntityID; + + public Toolbar getToolbar() { + return toolbar; + } + + protected Toolbar toolbar; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + handleIntentExtras(); + + /*if (getFragmentManager().findFragmentById(R.id.content) == null){ + addFragment(DataRepository.getInstance().getTempModel(BaseFragment.class), false); + }*/ + + getSupportFragmentManager().beginTransaction().add(R.id.content, + getHostedFragment(), getHostedFragmentTag()).commit(); + + } + + protected abstract android.support.v4.app.Fragment getHostedFragment(); + + private String getHostedFragmentTag() { + return null; + } + + /** + * Called in on create hook method. So beware of some conditions like constructing fragments + * after super.onCreate. in those conditions fragment doesn't received valid parameters. + * @return + */ + + protected boolean withToolbar(){ + return true; + } + + @Override + protected int getLayoutResourceId() { + return R.layout.activity_fragment_host; + } + + @Override + protected View getActivityView() { + ViewGroup vp = (ViewGroup) getLayoutInflater().inflate(getLayoutResourceId(), null); + if (withToolbar()){ + toolbar = (Toolbar) getLayoutInflater().inflate(R.layout.toolbar, vp, false); + //toolbar = ButterKnife.findById(vp, R.actorID.toolbar); + vp.addView(toolbar, 0); + handleToolbar(); + } + return vp; + } + + @Override + protected void initUI() { + + } + + @Override + protected void handleToolbar() { + //Toolbar toolbar = ButterKnife.findById(this, R.actorID.toolbar); + setSupportActionBar(toolbar); + + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null){ + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setHomeButtonEnabled(true); + + // TODO: corresponding fragments should handle this. They must generate activity title. + switch (requestID){ + /*----collections-----*/ + case ConstantsUtils.REQUEST_POST_REPOSTING_COLLECTIONS: + setTitle(R.string.title_activity_reposts_list); + break; + case ConstantsUtils.REQUEST_MEMBER_COLLECTIONS: + setTitle(R.string.title_activity_member_collections); + break; + case ConstantsUtils.REQUEST_MEMBER_FOLLOWED_COLLECTIONS: + setTitle(R.string.title_activity_member_followed_collections); + break; + + /*----members-----*/ + case ConstantsUtils.REQUEST_MEMBER_FRIENDS: + setTitle(R.string.title_activity_member_friends); + break; + case ConstantsUtils.REQUEST_POST_LIKERS: + setTitle(R.string.title_activity_post_likers); + break; + case ConstantsUtils.REQUEST_MEMBER_FOLLOWERS: + setTitle(R.string.title_activity_member_followers); + break; + + /*-----Posts----*/ + case ConstantsUtils.REQUEST_MEMBER_FAVED_POSTS: + setTitle(R.string.title_activity_member_faved_posts); + break; + case ConstantsUtils.REQUEST_MEMBER_POSTS: + setTitle(R.string.title_activity_member_posts); + break; + } + } + } + + public void handleIntentExtras(){ + Bundle b = getIntent().getExtras(); + if (b != null) { + requestID = b.getInt(ConstantsUtils.KEY_REQUEST_ID, -1); + mainEntityID = b.getString(ConstantsUtils.KEY_ENTITY); + secondEntityID = b.getString(ConstantsUtils.KEY_SECOND_ENTITY_ID); + titleParameter = b.getString(ConstantsUtils.KEY_PAGE_TITLE_PARAMETER, ""); + } + } + +/* public void addFragment(BaseFragment fragment, boolean addToBackStack){ + @SuppressLint("CommitTransaction") // committed few lines below + FragmentTransaction ft = getSupportFragmentManager().beginTransaction().replace(R.id.content, + fragment, fragment.getDefaultTag()); + if (addToBackStack) ft.addToBackStack(fragment.getDefaultTag()); + ft.commit(); + }*/ +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/FramesManagementActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/FramesManagementActivity.java new file mode 100755 index 0000000..ff07872 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/FramesManagementActivity.java @@ -0,0 +1,136 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.os.Bundle; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewProvider; +import com.shaya.poinila.android.presentation.uievent.DeleteFrameUIEvent; +import com.shaya.poinila.android.presentation.uievent.EditFrameNameUIEvent; +import com.shaya.poinila.android.presentation.uievent.PositiveButtonClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.SimpleSettingTextSetEvent; +import com.shaya.poinila.android.presentation.uievent.ViewFrameMembersUIEvent; +import com.shaya.poinila.android.presentation.view.dialog.ChangeFrameNameDialog; +import com.shaya.poinila.android.presentation.view.dialog.FrameCollectionsManagementDialog; +import com.shaya.poinila.android.presentation.view.dialog.NewFrameDialog; +import com.shaya.poinila.android.presentation.view.dialog.PoinilaAlertDialog; +import com.shaya.poinila.android.presentation.viewholder.FrameEditViewHolder; +import com.squareup.otto.Subscribe; + +import butterknife.Bind; +import data.PoinilaNetService; +import data.event.FrameReceivedEvent; +import data.model.Frame; +import manager.DBFacade; + +public class FramesManagementActivity extends ToolbarActivity { + + @Bind(R.id.recycler_view) + RecyclerView mRecyclerView; + private RecyclerViewAdapter mAdapter; + private int clickedFramePosition; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + protected void initUI() { + mRecyclerView = new RecyclerViewProvider(mRecyclerView). + setLinearLayoutManager(LinearLayoutManager.VERTICAL). + setAdapter(new RecyclerViewAdapter(getActivity(), R.layout.circle_edit_item) { + @Override + protected FrameEditViewHolder getProperViewHolder(View v, int viewType) { + return new FrameEditViewHolder(v); + } + }).bindViewToAdapter(); + mAdapter = (RecyclerViewAdapter)mRecyclerView.getAdapter(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_frame_management, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + if (id == R.id.action_add_circle){ + new NewFrameDialog().show(getSupportFragmentManager(), null); + } + //noinspection SimplifiableIfStatement + + return super.onOptionsItemSelected(item); + } + + @Override + protected void onStart() { + super.onStart(); + + mAdapter.resetData(DBFacade.getMyFrames()); + } + + @Subscribe public void onViewCollectionsEvent(ViewFrameMembersUIEvent event){ + String frameID = mAdapter.getItem(event.adapterPosition).getId(); + FrameCollectionsManagementDialog.newInstance(frameID).show(getSupportFragmentManager(), null);//ConstantsUtils.TAG_FRAME_MEMBERS_MANAGEMENT); + } + + + @Subscribe public void onEditFrameNameClick(final EditFrameNameUIEvent event){ + clickedFramePosition = event.adapterPosition; + ChangeFrameNameDialog.newInstance(mAdapter.getItem(clickedFramePosition).name).show(getSupportFragmentManager(), null); + } + + @Subscribe public void onDialogPositiveBtn(SimpleSettingTextSetEvent event){ + switch (event.settingType) { + case FRAME_NAME: + Frame editedFrame = mAdapter.getItem(clickedFramePosition); + editedFrame.name = event.value; + mAdapter.notifyItemChanged(clickedFramePosition); + // TODO: ghaedatan tu response server bayad save she! + PoinilaNetService.updateFrame(editedFrame); + break; + case NEW_FRAME: + PoinilaNetService.createFrame(event.value); + break; + } + } + + @Subscribe public void onDeleteFrame(DeleteFrameUIEvent event){ + clickedFramePosition = event.adapterPosition; + new PoinilaAlertDialog.Builder().setMessage(R.string.confirm_delete_frame). + setPositiveBtnText(getString(R.string.yes)).setNegativeBtnText(getString(R.string.no)). ///*setBody(new DeleteFrameDialog(mAdapter.getItem(event.adapterPosition))).build().*/ + build().show(getSupportFragmentManager(), null); + } + + @Subscribe + public void onPositiveDialogButtonClick(PositiveButtonClickedUIEvent event) { + PoinilaNetService.deleteFrame(mAdapter.getItem(clickedFramePosition)); + mAdapter.removeItem(clickedFramePosition); + } + + @Subscribe public void onFrameReceived(FrameReceivedEvent event){ + mAdapter.addItem(event.frame, 0); + } + + @Override + protected void handleToolbar() { + super.handleToolbar(); + setTitle(R.string.title_activity_frames_management); + } + + @Override + protected int getLayoutResourceId() { + return R.layout.activity_circle_management; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/FullImageActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/FullImageActivity.java new file mode 100755 index 0000000..8956b10 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/FullImageActivity.java @@ -0,0 +1,51 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.support.v7.widget.Toolbar; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.costom_view.TouchImageView; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.squareup.picasso.Picasso; + +import butterknife.Bind; + +public class FullImageActivity extends ToolbarActivity { + + @Bind(R.id.image_view) + TouchImageView imageView; + + private String imageUrl; + + @Override + protected void initUI() { + } + + @Override + protected void onStart() { + super.onStart(); + + Picasso.with(this).load(imageUrl).into(imageView); + } + + @Override + protected int getLayoutResourceId() { + return R.layout.activity_full_image; + } + + @Override + protected void handleIntentExtras() { + imageUrl = getIntent().getStringExtra(ConstantsUtils.KEY_CONTENT_URI); + } + + @Override + protected View getActivityView() { + ViewGroup vp = (ViewGroup) getLayoutInflater().inflate(getLayoutResourceId(), null); + toolbar = (Toolbar) getLayoutInflater().inflate(R.layout.toolbar_black, vp, false); + handleToolbar(); + vp.addView(toolbar, 0); + return vp; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/HelpActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/HelpActivity.java new file mode 100755 index 0000000..995e4fe --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/HelpActivity.java @@ -0,0 +1,200 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.os.Bundle; +import android.support.annotation.ColorRes; +import android.support.annotation.DrawableRes; +import android.support.annotation.StringRes; +import android.support.v4.content.ContextCompat; +import android.support.v4.view.ViewPager; +import android.view.View; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.TextView; + +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.presentation.view.dialog.DialogLauncher; +import com.shaya.poinila.android.presentation.view.fragments.BaseFragment; +import com.shaya.poinila.android.util.DeviceInfoUtils; +import com.shaya.poinila.android.util.PoinilaPreferences; +import com.squareup.picasso.Picasso; + +import java.util.HashMap; + +import butterknife.Bind; +import butterknife.OnClick; +import me.relex.circleindicator.CircleIndicator; + +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_STARTED_FROM_SETTING; + +public class HelpActivity extends BaseActivity implements ViewPager.OnPageChangeListener { + @Bind(R.id.view_pager) + ViewPager mViewPager; + @Bind(R.id.indicator) + CircleIndicator mIndicator; + @Bind(R.id.help_nextSlide) + ImageButton mNextSlideBtn; + HelpAdapter mPagerAdapter; + private boolean startedFromSetting; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + protected void initUI() { + mPagerAdapter = new HelpAdapter(getSupportFragmentManager()); + mViewPager.setAdapter(mPagerAdapter); + mIndicator.setViewPager(mViewPager); + mViewPager.addOnPageChangeListener(this); + PoinilaPreferences.setSeenHelp(false); + } + + @Override + protected void handleToolbar() { + } + + @Override + protected void handleIntentExtras() { + startedFromSetting = getIntent().getBooleanExtra(KEY_STARTED_FROM_SETTING, false); + } + + @Override + protected int getLayoutResourceId() { + return R.layout.activity_help; + } + + @OnClick(R.id.help_nextSlide) + public void onNextSlide() { + if (mViewPager.getCurrentItem() != mPagerAdapter.getCount() - 1) { + mViewPager.setCurrentItem(mViewPager.getCurrentItem() + 1, true); + } else if (startedFromSetting) { + finish(); + } else { + PoinilaPreferences.setSeenHelp(true); + PageChanger.goToLoginActivity(getActivity()); + } + } + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + + } + + @Override + public void onPageSelected(int position) { + mNextSlideBtn.setImageResource(position == HelpAdapter.POS_ADD_CONTENT + ? R.drawable.done_white_48dp : R.drawable.arrow_right_white_48dp); + } + + @Override + public void onPageScrollStateChanged(int state) { + + } + + + private class HelpAdapter extends android.support.v4.app.FragmentPagerAdapter { + public static final int ITEM_COUNT = 7; + public static final int POS_INTRO = 0; + public static final int POS_DASHBOARD = 1; + public static final int POS_COLLECTIONS = 2; + public static final int POS_SEARCH = 3; + public static final int POS_NOTIFICATIONS = 4; + public static final int POS_PROFILE = 5; + public static final int POS_ADD_CONTENT = 6; + + public HelpAdapter(android.support.v4.app.FragmentManager fm) { + super(fm); + } + + @Override + public android.support.v4.app.Fragment getItem(int position) { + return HelpFragment.newInstance(position); + } + + @Override + public int getCount() { + return ITEM_COUNT; + } + } + + public static class HelpFragment extends BaseFragment { + @Bind(R.id.root_view) + View mRootView; + @Bind(R.id.image_view) + ImageView mImageView; + @Bind(R.id.text_view) + TextView mTextView; + + private static final String ARG_POSITION = "position"; + + private int mPosition; + private HashMap mStatesMap; + + public static HelpFragment newInstance(int position) { + HelpFragment fragment = new HelpFragment(); + Bundle args = new Bundle(); + args.putInt(ARG_POSITION, position); + fragment.setArguments(args); + return fragment; + } + + public HelpFragment() { + + } + + @Override + public int getLayoutID() { + return R.layout.fragment_help; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getArguments() != null) { + mPosition = getArguments().getInt(ARG_POSITION); + } + mStatesMap = initStateMap(); + } + + private HashMap initStateMap() { + HashMap map = new HashMap<>(); + map.put(HelpAdapter.POS_INTRO, new State(R.color.help_intro_color, + R.drawable.splash_background, R.string.help_intro)); + map.put(HelpAdapter.POS_DASHBOARD, new State(R.color.help_dashboard_color, + R.drawable.help_dashboard, R.string.help_dashboard)); + map.put(HelpAdapter.POS_COLLECTIONS, new State(R.color.help_collections_color, + R.drawable.help_collections, R.string.help_collection)); + map.put(HelpAdapter.POS_SEARCH, new State(R.color.help_search_color, + R.drawable.help_search, R.string.help_search)); + map.put(HelpAdapter.POS_NOTIFICATIONS, new State(R.color.help_notifications_color, + R.drawable.help_notifications, R.string.help_notification)); + map.put(HelpAdapter.POS_PROFILE, new State(R.color.help_profile_color, + R.drawable.help_profile, R.string.help_profile)); + map.put(HelpAdapter.POS_ADD_CONTENT, new State(R.color.help_profile_color, + R.drawable.help_add_content, R.string.help_add_content)); + return map; + } + + @Override + protected void initUI() { + mRootView.setBackgroundColor(ContextCompat.getColor(getActivity(), mStatesMap.get(mPosition).mColor)); + Picasso.with(getActivity()).load(mStatesMap.get(mPosition).mImage).into(mImageView); + ViewUtils.setText(mTextView, getString(mStatesMap.get(mPosition).mText)); + } + + private static class State { + public int mText; + public int mColor; + public int mImage; + + public State(@ColorRes int color, @DrawableRes int image, @StringRes int text) { + this.mColor = color; + this.mImage = image; + this.mText = text; + } + } + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/InvitationNotifListActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/InvitationNotifListActivity.java new file mode 100755 index 0000000..ee6378c --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/InvitationNotifListActivity.java @@ -0,0 +1,31 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.app.Fragment; +import android.os.Bundle; + +import com.shaya.poinila.android.presentation.view.fragments.InvitationNotifListFragment; + +public class InvitationNotifListActivity extends FragmentHostActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + protected void initUI() { + + } + + @Override + protected android.support.v4.app.Fragment getHostedFragment() { + return InvitationNotifListFragment.newInstance(); + } + + @Override + protected boolean withToolbar() { + return true; + } + + //TODO accept all option! +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/MainActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/MainActivity.java new file mode 100755 index 0000000..c0af4d7 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/MainActivity.java @@ -0,0 +1,366 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.preference.PreferenceManager; +import android.support.design.widget.TabLayout; +import android.support.v4.view.ViewPager; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import com.crashlytics.android.Crashlytics; +import com.shaya.poinila.android.presentation.BuildConfig; +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.HelpMyFollowedCollectionListFragment; +import com.shaya.poinila.android.presentation.uievent.HelpMyProfileFragment; +import com.shaya.poinila.android.presentation.uievent.ShowVerifySnackbarEvent; +import com.shaya.poinila.android.presentation.view.NotificationNumberListener; +import com.shaya.poinila.android.presentation.view.PageSelectedListener; +import com.shaya.poinila.android.presentation.view.dialog.DialogLauncher; +import com.shaya.poinila.android.presentation.view.fragments.AnonymousInfoFragment; +import com.shaya.poinila.android.presentation.view.fragments.BaseFragment; +import com.shaya.poinila.android.presentation.view.fragments.BusFragment; +import com.shaya.poinila.android.presentation.view.fragments.DashboardFragment; +import com.shaya.poinila.android.presentation.view.fragments.MyFollowedCollectionsFragment; +import com.shaya.poinila.android.presentation.view.fragments.MyProfileFragment; +import com.shaya.poinila.android.presentation.view.fragments.NotificationFragment; +import com.shaya.poinila.android.presentation.view.fragments.SearchFragment; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.ConnectionUitls; +import com.shaya.poinila.android.util.DeviceInfoUtils; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.util.PoinilaPreferences; +import com.shaya.poinila.android.utils.PonilaAccountManager; +import com.shaya.poinila.android.utils.PonilaSnackbarManager; +import com.squareup.otto.Subscribe; +import com.tapstream.sdk.Config; +import com.tapstream.sdk.Tapstream; + +import java.util.Arrays; +import java.util.Calendar; + +import butterknife.Bind; +import data.event.MyInfoReceivedEvent; +import data.event.SystemPreferencesReceivedEvent; +import data.model.Member; +import manager.DBFacade; +import manager.DataRepository; + +import static com.shaya.poinila.android.util.StringUtils.isInteger; + +// testing git +public class MainActivity extends BaseActivity implements + NotificationNumberListener, ViewPager.OnPageChangeListener { + + private static final long DELAY_HIDE_KEYBOARD = 100; + private static final String TAG_HOME = "home page"; + private long start; + private TextView notifNumView; + private RelativeLayout notifTabView; + private SimpleFragmentPagerAdapter mAdapter; + private boolean backPressed = false; + + + + @Override + protected void onCreate(Bundle savedInstanceState) { + + if (BuildConfig.DEBUG) + Logger.toast("debug mode");//"***************DEBUG MODE!>*********"); + + if (savedInstanceState == null) + initRatingPrompt(); + + handleDeepLink(getIntent()); + + super.onCreate(savedInstanceState); + PreferenceManager.setDefaultValues(this, R.xml.preferences, false); + + setupTab(); + + if (!ConnectionUitls.isNetworkOnline()) + Logger.toast(R.string.warning_connect_to_network); + + + Config config = new Config("ponila", "_aOzJdJ9SMGNYGo08LxytA"); + Tapstream.create(getApplication(), config); + } + + /** + * Fabric(Crashlytics): Log User Info + */ + + + private void initRatingPrompt() { + PoinilaPreferences.updateOpenApplicationCount(); + PoinilaPreferences.setFirstLoginDateTimeIfNotSet(Calendar.getInstance()); + DataRepository.calculateIsTimeToAskAboutRating(); + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + handleDeepLink(intent); + } + + private void handleDeepLink(Intent intent) { + if (intent == null || intent.getData() == null) + return; + + Member cachedInfo = DBFacade.getCachedMyInfo(); + DataRepository.setUserAsAnonymous(cachedInfo == null || cachedInfo.isAnonymous); + + Uri shareUri = intent.getData(); + String mainEntityId, secondEntityId; + if (shareUri != null + && shareUri.getPathSegments() != null + && !shareUri.getPathSegments().isEmpty()) { + if(Arrays.asList("resetpassword", "register").contains(shareUri.getPathSegments().get(0))) { // ponila.com/register/{token}/ + PageChanger.goToLoginActivity(this, shareUri); + }//urls in form of ponila.com/post/{post_id} + else if (shareUri.getPathSegments().get(0).equals("post") && + isInteger(mainEntityId = shareUri.getPathSegments().get(1))) { + //actorID = shareUri.getPathSegments().get(postSegmentIndex); + PageChanger.goToPost(this, mainEntityId); + } //urls in form of ponila.com/{unique_name}/{collection_name} + else if (shareUri.getPathSegments().size() == 2) { + mainEntityId = shareUri.getPathSegments().get(1); // collection name + secondEntityId = shareUri.getPathSegments().get(0); // user name + PageChanger.goToCollection(this, null, mainEntityId, secondEntityId); + } //urls in form of ponila.com/{unique_name} + else if (shareUri.getPathSegments().size() == 1) { + mainEntityId = shareUri.getPathSegments().get(0); // user name + PageChanger.goToProfile(this, mainEntityId); + } else { + Logger.toast(R.string.error_invalid_url); + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { + @Override + public void run() { + MainActivity.this.finish(); + } + }, 1500); + } + } + } + + @Override + protected void initUI() { + + DataRepository.getInstance().getMyInfo(true, MyInfoReceivedEvent.MY_INFO_TYPE.VERIFY); + + } + + @Subscribe + public void onUserInfoReceived(MyInfoReceivedEvent event) { + + if (event.type != MyInfoReceivedEvent.MY_INFO_TYPE.VERIFY) return; + + if(!event.me.isEmailVerified && !event.me.isMobileVerified && !PoinilaPreferences.isUserAnonymous()) + PonilaSnackbarManager.getInstance().showVerifySnackbar(findViewById(R.id.main_content), this); + else if(!event.me.isPassword) + PonilaSnackbarManager.getInstance().showChangeUserPassSnackBar(findViewById(R.id.main_content), this); + + + new AsyncTask() { + + @Override + protected Void doInBackground(Object... params) { + DataRepository.syncWithMyInfoResponse((MyInfoReceivedEvent) params[0]); + return null; + } + + @Override + protected void onPostExecute(Void o) { + + } + + + }.execute(event); + + } + + @Override + public void onBackPressed() { + if(!backPressed){ + backPressed = true; + Logger.toast(R.string.exit_with_back_message); + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + backPressed = false; + } + }, 5000); + }else { + backPressed = false; + System.exit(0); +// super.onBackPressed(); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + return true; + } + + return super.onOptionsItemSelected(item); + } + + @Override + protected void handleToolbar() { + + } + + @Override + protected int getLayoutResourceId() { + return R.layout.activity_home; + } + + + // ----------TODO: just for testing if problem with viewpager is by nested fragments + + private static final int TABS_SIZE = 5; + @Bind(R.id.view_pager) + public ViewPager viewPager; + @Bind(R.id.tabs) + public TabLayout tabLayout; + + private void setupTab() { + int[] icons = new int[]{R.drawable.tab_dashboard_selector, R.drawable.tab_collection_selector, + R.drawable.tab_search_selector, R.drawable.tab_notification_selector, + R.drawable.tab_profile_selector}; + + // Get the ViewPager and set it's PagerAdapter so that it can display items + mAdapter = new SimpleFragmentPagerAdapter(getSupportFragmentManager(), + MainActivity.this); + viewPager.setAdapter(mAdapter); + + viewPager.setOffscreenPageLimit(5); + + viewPager.addOnPageChangeListener(this); + + // Give the TabLayout the ViewPager + tabLayout.setupWithViewPager(viewPager); + + + setupNotificationTab(); + + for (int i = 0; i < TABS_SIZE; i++){ + if( i == 3){ + TabLayout.Tab tab = tabLayout.getTabAt(i); + tab.setCustomView(notifTabView); + }else { + tabLayout.getTabAt(i).setIcon(icons[i]); + + } + } + } + + private void setupNotificationTab(){ + notifTabView = (RelativeLayout)LayoutInflater.from(MainActivity.this).inflate(R.layout.tab_notification_layout, null); + notifNumView = (TextView) notifTabView.findViewById(R.id.notification_number); + } + + @Override + public void onNotificationNumber(int number) { + if(number > -10){ + notifNumView.setVisibility(View.VISIBLE); + notifNumView.setText(String.valueOf(number)); + return; + } + notifNumView.setVisibility(View.GONE); + } + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + + } + + @Override + public void onPageSelected(int position) { + if(mAdapter.getItem(position) instanceof MyFollowedCollectionsFragment){ + BusProvider.getBus().post( new HelpMyFollowedCollectionListFragment()); + }else if(mAdapter.getItem(position) instanceof MyProfileFragment){ + BusProvider.getBus().post( new HelpMyProfileFragment()); + } + } + + @Override + public void onPageScrollStateChanged(int state) { + + } + + public class SimpleFragmentPagerAdapter extends android.support.v4.app.FragmentStatePagerAdapter { + private String[] tabTitles; + private Context context; + + public SimpleFragmentPagerAdapter(android.support.v4.app.FragmentManager fm, Context context) { + super(fm); + this.context = context; + tabTitles = context.getResources().getStringArray(R.array.tab_titles); + } + + + @Override + public int getCount() { + return TABS_SIZE; + } + + // in case of entering as an anonymous user, return static pages for collections, notifications, profile tabs. + @Override + public BaseFragment getItem(int position) { + switch (position) { + case 0: return DashboardFragment.newInstance(); + case 1: return DataRepository.isUserAnonymous() ? + AnonymousInfoFragment.newInstance(AnonymousInfoFragment.FOLLOWING_COLLECTIONS) : + MyFollowedCollectionsFragment.newInstance(); + //pages.add(FragmentPagerItem.of("", HomeFragment.TestFragment.class)); + case 2: return SearchFragment.newInstance(); + case 3: return DataRepository.isUserAnonymous() ? + AnonymousInfoFragment.newInstance(AnonymousInfoFragment.NOTIFICATIONS) : + NotificationFragment.newInstance(); + case 4: return DataRepository.isUserAnonymous() ? + AnonymousInfoFragment.newInstance(AnonymousInfoFragment.PROFILE) : + MyProfileFragment.newInstance(); + default: + return null; + } + + } + + @Override + public CharSequence getPageTitle(int position) { + // Generate title based on item position + return "";//tabTitles[position]; + } + } + + @Subscribe + public void keepSystemPreferences(SystemPreferencesReceivedEvent event){ + DataRepository.setSystemPreferences(event.systemPreferences); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/MemberListActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/MemberListActivity.java new file mode 100755 index 0000000..14aae5e --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/MemberListActivity.java @@ -0,0 +1,32 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.app.Fragment; +import android.os.Bundle; + +import com.shaya.poinila.android.presentation.view.fragments.MemberListFragment; + +public class MemberListActivity extends FragmentHostActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + handleIntentExtras(); + super.onCreate(savedInstanceState); + } + + @Override + protected void initUI() { + + } + + @Override + protected android.support.v4.app.Fragment getHostedFragment() { + return MemberListFragment.newInstance(mainEntityID, requestID); + } + + @Override + protected boolean withToolbar() { + return true; + } + + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/NewPostActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/NewPostActivity.java new file mode 100755 index 0000000..dee929f --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/NewPostActivity.java @@ -0,0 +1,81 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.support.v7.app.ActionBar; +import android.support.v7.widget.Toolbar; +import android.view.View; +import android.view.ViewGroup; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.fragments.NewWebSitePostInputURLFragment; +import com.shaya.poinila.android.presentation.view.fragments.NewWebSitePostSelectMediaFragment; + +import data.model.PostType; +import data.model.SuggestedWebPagePost; + +public class NewPostActivity extends BaseActivity { + + + private static final String TAG_INPUT_URL_FRAGMENT = NewWebSitePostInputURLFragment.class.getName(); + private static final String TAG_SELECT_IMAGE_FRAGMENT = NewWebSitePostSelectMediaFragment.class.getName(); + + + Toolbar toolbar; + + @Override + protected void initUI() { + + goToInputURLFragment(); + + toolbar = (Toolbar)findViewById(R.id.toolbar); + handleToolbar(); + + } + + @Override + protected void handleToolbar() { + setSupportActionBar(toolbar); + + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null){ + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setHomeButtonEnabled(true); + + } + } + + @Override + protected int getLayoutResourceId() { + return R.layout.activity_new_web_site_post; + } + + public void goToInputURLFragment() { + getSupportFragmentManager().beginTransaction(). + replace(R.id.container, new NewWebSitePostInputURLFragment(), TAG_INPUT_URL_FRAGMENT). + //addToBackStack(TAG_LOGIN_FRAGMENT). + commit(); + } + + public void goToSelectMediaFragment(PostType postType, String siteAddress) { + getSupportFragmentManager().beginTransaction(). + replace(R.id.container, NewWebSitePostSelectMediaFragment.newInstance(postType, siteAddress), TAG_SELECT_IMAGE_FRAGMENT). + //addToBackStack(TAG_LOGIN_FRAGMENT). + commit(); + } + + public void goToNewPostFragment(SuggestedWebPagePost suggestedPost){ + + } + + @Override + protected View getActivityView() { + ViewGroup vp = (ViewGroup) getLayoutInflater().inflate(getLayoutResourceId(), null); + toolbar = (Toolbar) getLayoutInflater().inflate(R.layout.toolbar, vp, false); + handleToolbar(); + vp.addView(toolbar, 0); + return vp; + } + + + + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/NotificationOpenedActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/NotificationOpenedActivity.java new file mode 100755 index 0000000..6cddb4c --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/NotificationOpenedActivity.java @@ -0,0 +1,64 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.support.v4.app.Fragment; +import android.text.TextUtils; + +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.fragments.notification.NCollectionListFragment; +import com.shaya.poinila.android.presentation.view.fragments.notification.NPostListFragment; +import com.shaya.poinila.android.utils.PushNotificationUtils; + +import java.lang.reflect.Type; +import java.util.List; + +import data.model.Collection; +import data.model.Post; + +/** + * Created by iran on 6/14/2016. + */ +public class NotificationOpenedActivity extends FragmentHostActivity { + + @Override + protected Fragment getHostedFragment() { + + PushNotificationUtils.NOTIFICATION_TYPE type = PushNotificationUtils.NOTIFICATION_TYPE.valueOf(getIntent().getStringExtra("type")); + String data = getIntent().getStringExtra("data"); + Type listType; + List list; + + switch (type){ + case POST_SUGGESTION: + setTitle(R.string.notification_suggestion_post_title); +// listType = new TypeToken>() {}.getType(); +// list = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss").create().fromJson(data, listType); + return NPostListFragment.newInstance(data, type); + case LIKE: + setTitle(R.string.notification_post_like_title); + listType = new TypeToken>() {}.getType(); + list = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss").create().fromJson(data, listType); + return NPostListFragment.newInstance(list, type); + case COMMENT: + setTitle(R.string.notification_post_comment_title); + listType = new TypeToken>() {}.getType(); + list = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss").create().fromJson(data, listType); + return NPostListFragment.newInstance(list, type); + case FOLLOW: + setTitle(R.string.notification_collection_follow_title); + listType = new TypeToken>() {}.getType(); + list = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss").create().fromJson(data, listType); + return NCollectionListFragment.newInstance(list); + default: + return new Fragment(); + } + } + + private List getList(String data){ + Type listType = new TypeToken>() {}.getType(); + List list = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss").create().fromJson(data, listType); + return list; + } + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/NotificationSwitchActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/NotificationSwitchActivity.java new file mode 100755 index 0000000..c38d735 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/NotificationSwitchActivity.java @@ -0,0 +1,43 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.app.Fragment; +import android.view.MenuItem; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.fragments.NotificationSwitchFragment; + + +public class NotificationSwitchActivity extends FragmentHostActivity { + + + @Override + protected android.support.v4.app.Fragment getHostedFragment() { + return NotificationSwitchFragment.newInstance(mainEntityID, requestID); + } + + @Override + protected boolean withToolbar() { + return true; + } + + + @Override + protected void initUI() { + } + + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + return true; + } + + return super.onOptionsItemSelected(item); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/OthersProfileActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/OthersProfileActivity.java new file mode 100755 index 0000000..dd54081 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/OthersProfileActivity.java @@ -0,0 +1,413 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.content.Intent; +import android.net.Uri; +import android.support.v4.widget.Space; +import android.text.TextUtils; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.FriendCirclesUpdated; +import com.shaya.poinila.android.presentation.uievent.NeutralDialogButtonClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.PositiveButtonClickedUIEvent; +import com.shaya.poinila.android.presentation.view.ViewInflater; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.presentation.view.dialog.DialogLauncher; +import com.shaya.poinila.android.presentation.view.dialog.PoinilaAlertDialog; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.util.NavigationUtils; +import com.shaya.poinila.android.util.ResourceUtils; +import com.shaya.poinila.android.util.StringUtils; +import com.squareup.otto.Subscribe; + +import org.apmem.tools.layouts.FlowLayout; + +import java.util.List; + +import butterknife.Bind; +import butterknife.ButterKnife; +import butterknife.OnClick; +import data.PoinilaNetService; +import data.event.MemberReceivedEvent; +import data.model.Collection; +import data.model.FriendRequestAnswer; +import data.model.FriendshipStatus; +import data.model.ImageUrls; +import data.model.Member; +import manager.DBFacade; +import manager.DataRepository; + +import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setImage; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_CONTENT_URI; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_ENTITY; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_MEMBER_ID; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_REQUEST_ID; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_MEMBER_COLLECTIONS; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_MEMBER_FAVED_POSTS; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_MEMBER_FOLLOWED_COLLECTIONS; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_MEMBER_FOLLOWERS; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_MEMBER_FRIENDS; + +public class OthersProfileActivity extends ToolbarActivity { + + @Bind(R.id.profile_general_info) + ViewGroup profileGeneralInfo; + @Bind(R.id.followers) + ViewGroup followersStatViewGroup; + @Bind(R.id.favorited) + ViewGroup favoritedStatViewGroup; + @Bind(R.id.posts) + ViewGroup postsStatViewGroup; + @Bind(R.id.friends) + ViewGroup friendsStatViewGroup; + @Bind(R.id.owning_collections_container) + View owningCollections; + @Bind(R.id.following_collections_container) + View followingCollections; + @Bind(R.id.interest_container) + View interestsContainer; + @Bind(R.id.blog_info) + View blogInfo; + @Bind(R.id.about_me) + TextView aboutMe; + + // TODO: get member from cache if exist else request the server + Member member; + + private ImageView friendIcon; + private String memberID; + + @Override + protected void handleIntentExtras() { + memberID = getIntent().getStringExtra(KEY_MEMBER_ID); + // TODO: wtf? + /*titleParameter = ""; + //Uri shareUri = getIntent().getData();*/ + + } + + @Override + public void onStart() { + super.onStart(); + if (requestOnFirstTime) + DataRepository.getInstance().getProfile(memberID); + } + + @Override + protected void initUI() { + friendIcon = ButterKnife.findById(profileGeneralInfo, R.id.icon); + //friendIcon.setImageResource(R.drawable.add_friend); + //friendIcon.setVisibility(View.INVISIBLE); + interestsContainer.setVisibility(View.GONE); + + generalProfileInit(); + + getActivity().setTitle(titleParameter); + + member = DataRepository.getInstance().getTempModel(Member.class); + if (member != null) + fill(member); + } + + + @Subscribe + public void onProfileReceived(MemberReceivedEvent event) { + this.member = event.member; + requestOnFirstTime = false; + + fill(member); + } + + private void showAddingAsFriendDialog() { + // TODO: if is friend launch remove friend dialog. + // if not launch add as friend dialog + new PoinilaAlertDialog.Builder(). + setTitle(getString(R.string.friend_request)). + setMessage(getString(R.string.approve_send_friend_request)). + setPositiveBtnText(getString(R.string.yes)). + setNegativeBtnText(getString(R.string.no)). + build().show(getSupportFragmentManager(), null); + } + + private void showEditFriendDialog() { + + new PoinilaAlertDialog.Builder(). + setTitle(R.string.edit_friendship). + setPositiveBtnText(R.string.remove_friend). + setNegativeBtnText(R.string.cancel). + setNeutralBtnText(R.string.edit_circle). + build().show(getSupportFragmentManager(), ConstantsUtils.TAG_EDIT_FRIENDSHIP); + } + + @Subscribe + public void onPositiveDialogButton(PositiveButtonClickedUIEvent event) { + switch (member.friendshipStatus) { + case NotFriend: + PoinilaNetService.friendRequest(member.getId(), DBFacade.getDefaultCircle().id); + member.friendshipStatus = FriendshipStatus.Pending; + friendIcon.setImageResource(R.drawable.pending_friendship_request); + break; + case WaitingForAction: // sending request + PoinilaNetService.answerFriendRequest(member.id, (FriendRequestAnswer)event.getData(), DBFacade.getDefaultCircle().id); + member.friendshipStatus = FriendshipStatus.IsFriend; + friendIcon.setImageResource(R.drawable.friends); + break; + case IsFriend: // removing friend + PoinilaNetService.removeFriend(member.getId()); + member.friendshipStatus = FriendshipStatus.NotFriend; + friendIcon.setImageResource(R.drawable.add_friend_selector); + break; + case Pending: // no action yet + break; + } + //friendIcon.setImageResource(member.isFriend ? R.drawable.friends : R.drawable.add_friend_selector); + } + + + @Subscribe + public void onNeutralDialogButton(NeutralDialogButtonClickedUIEvent event) { + DialogLauncher.launchChangeFriendCircle(getSupportFragmentManager(), member); + } + + @Override + protected int getLayoutResourceId() { + return R.layout.activity_others_profile; + } + + @OnClick(R.id.followers) + public void onShowFollowers() { + NavigationUtils.goToActivity(MemberListActivity.class, getActivity(), + KEY_ENTITY, member.getId(), KEY_REQUEST_ID, REQUEST_MEMBER_FOLLOWERS); + } + + @OnClick(R.id.friends) + public void onShowFriends() { + NavigationUtils.goToActivity(MemberListActivity.class, getActivity(), + KEY_ENTITY, member.getId(), KEY_REQUEST_ID, REQUEST_MEMBER_FRIENDS); + } + + @OnClick(R.id.favorited) + public void onShowFavorited() { + NavigationUtils.goToActivity(PostListActivity.class, getActivity(), + KEY_ENTITY, member.getId(), KEY_REQUEST_ID, REQUEST_MEMBER_FAVED_POSTS); + } + + @OnClick(R.id.posts) + public void onShowPosts() { + PageChanger.goToMemberPosts(getActivity(), member.getId(), member.fullName); + } + + + @OnClick(R.id.owning_collections_container) + public void onShowOwniningCollections() { + NavigationUtils.goToActivity(CollectionListActivity.class, getActivity(), + KEY_ENTITY, member.getId(), KEY_REQUEST_ID, REQUEST_MEMBER_COLLECTIONS); + } + + @OnClick(R.id.following_collections_container) + public void onShowFollowingCollections() { + if(member == null)return; + NavigationUtils.goToActivity(CollectionListActivity.class, getActivity(), + KEY_ENTITY, member.getId(), KEY_REQUEST_ID, REQUEST_MEMBER_FOLLOWED_COLLECTIONS); + } + + + private void fill(final Member member) { + if (member == null) return; + + titleParameter = member.uniqueName; + getActivity().setTitle(titleParameter); + + // TODO: fill page with actual posts + friendIcon.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (DataRepository.isUserAnonymous()){ + Logger.toastError(R.string.error_guest_action); + return; + } + DialogLauncher.launchFriendshipDialog(member, getSupportFragmentManager()); + } + }); + + setImage((ImageView) profileGeneralInfo.findViewById(R.id.image), member.imageUrls, + ImageUrls.ImageType.MEMBER, ImageUrls.ImageSize.BIG); + if (member.imageUrls != null && member.imageUrls.isNotEmpty()) { + profileGeneralInfo.findViewById(R.id.image).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = NavigationUtils.makeNavigationIntent(FullImageActivity.class, getActivity()); + intent.putExtra(KEY_CONTENT_URI, member.imageUrls.properMemberImage(ImageUrls.ImageSize.FULL_SIZE).url); + startActivity(intent); + } + }); + } + setText((TextView) profileGeneralInfo.findViewById(R.id.title), member.fullName); + setText((TextView) profileGeneralInfo.findViewById(R.id.subtitle), member.uniqueName); + + //friendIcon.setBackgroundResource(0); // to clear old background + if (DataRepository.getInstance().isMe(member.id)) { + // TODO: this is terrible! must use the profile fragment instead. + friendIcon.setVisibility(View.GONE); + } else if (member.friendshipStatus == null) { + friendIcon.setVisibility(View.INVISIBLE); + } else { + friendIcon.setVisibility(View.VISIBLE); + switch (member.friendshipStatus) { + case NotFriend: + friendIcon.setImageResource(R.drawable.add_friend_selector); + break; + case WaitingForAction: + friendIcon.setImageResource(R.drawable.pending_friendship_request); + break; + case IsFriend: + friendIcon.setImageResource(R.drawable.friends); + break; + case Pending: + friendIcon.setImageResource(R.drawable.pending_friendship_request); + break; + } + } + + setText(aboutMe, member.aboutMe); + + if (TextUtils.isEmpty(member.url)) + blogInfo.setVisibility(View.GONE); + else { + setText((TextView) blogInfo.findViewById(R.id.title), member.urlName); + setText((TextView) blogInfo.findViewById(R.id.url), member.url); + blogInfo.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + NavigationUtils.goToUrl(getActivity(), member.url.toLowerCase(), null); + } + }); + } + + setText((TextView) followersStatViewGroup.findViewById(R.id.top_text), member.followerCount); + setText((TextView) favoritedStatViewGroup.findViewById(R.id.top_text), member.likesCount); + setText((TextView) postsStatViewGroup.findViewById(R.id.top_text), member.postsCount); + setText((TextView) friendsStatViewGroup.findViewById(R.id.top_text), member.friendsCount); + + setText((TextView) owningCollections.findViewById(R.id.card_title), + getString(R.string.member_collections_formatted, member.fullName)); + + /*-----OWNING COLLECTIONS-------*/ + fillCollectionsSummery(owningCollections, member.owningCollections, member.owningCollectionsCount); + /*----FOLLOWING COLLECTIONS-----*/ + fillCollectionsSummery(followingCollections, member.followingCollections, member.followingCollectionsCount); + + /*-------Interests--------*/ + if (member.interests != null && !member.interests.isEmpty()) { + FlowLayout flowLayout = ButterKnife.findById(interestsContainer, R.id.tags_container); + for (int i = 0; i < 5 && i < member.interests.size(); i++) { + ViewInflater.addTagToContainer(flowLayout, member.interests.get(i)); + } + } + } + + private void fillCollectionsSummery(View collectionsViewContainer, List collections, int itemCount) { + LinearLayout ll = (LinearLayout) collectionsViewContainer.findViewById(R.id.cards_container); + if (collections == null || collections.isEmpty()) { + ll.setVisibility(View.GONE); + return; + } + ll.removeAllViews(); + ll.setVisibility(View.VISIBLE); + int collectionsCount = getResources().getInteger(R.integer.profile_page_collection_summary_count); + for (int i = 0; i < collectionsCount && i < collections.size(); i++) { + final Collection collection = collections.get(i); + View card = ViewInflater.inflateImageCaption(ll, collection.name, collection.coverImageUrls); + card.findViewById(R.id.image).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onCollectionClicked(collection); + } + }); + ll.addView(card, new LinearLayout.LayoutParams(0, WRAP_CONTENT, 1)); + if (i != collectionsCount - 1) { + Space margin = new Space(getActivity()); + ll.addView(margin, new LinearLayout.LayoutParams( + (int) ResourceUtils.getDimen(R.dimen.margin_lvl1), 1)); + } + } + TextView itemCountView = ButterKnife.findById(collectionsViewContainer, R.id.item_count); + itemCountView.setVisibility(View.VISIBLE); + ViewUtils.setText(itemCountView, StringUtils.getStringWithPersianNumber("(%d)", itemCount)); + } + + private void onCollectionClicked(final Collection collection) { + PageChanger.goToCollection(getActivity(), collection); + } + + private void generalProfileInit() { + ((TextView) followersStatViewGroup.findViewById(R.id.bottom_text)) + .setText(getString(R.string.follower)); + ((TextView) favoritedStatViewGroup.findViewById(R.id.bottom_text)) + .setText(getString(R.string.favorited)); + ((TextView) postsStatViewGroup.findViewById(R.id.bottom_text)) + .setText(getString(R.string.post)); + ((TextView) friendsStatViewGroup.findViewById(R.id.bottom_text)) + .setText(getString(R.string.friend)); + + + ((TextView) followingCollections.findViewById(R.id.card_title)). + setText(getString(R.string.follows)); + + ((TextView) interestsContainer.findViewById(R.id.card_title)). + setText(getString(R.string.interest)); + // TODO: member ro chejuri avval set konim? + } + + @Subscribe + public void onFriendCirclesUpdated(FriendCirclesUpdated event) { + if (member == null) return; + if (event.member.id == member.id) + member.circle_ids = event.selectedCirclesIDs; + } + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_post, menu); // TODO: create a new menu xml resource + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // handle item selection + switch (item.getItemId()) { + case R.id.menu_item_share: + // Handle this selection + launchShareMenu(); + return true; + case R.id.menu_item_report: + // Handle this selection + DialogLauncher.launchReportDialog(getSupportFragmentManager(), R.string.report_user, member.id); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + private void launchShareMenu() { + Intent shareIntent = new Intent(Intent.ACTION_SEND); + shareIntent.setType("text/plain"); + String extra = getString(R.string.checkout_this_member) + "\n" + + getString(R.string.member_share_url, + ConstantsUtils.POINILA_ORIGIN_ADDRESS, + Uri.encode(member.uniqueName)) + "\n" + + getString(R.string.ponila_world_of_interest); + shareIntent.putExtra(Intent.EXTRA_TEXT, extra); + startActivity(Intent.createChooser(shareIntent, getString(R.string.share_dialog_title))); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/PostListActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/PostListActivity.java new file mode 100755 index 0000000..27e93e4 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/PostListActivity.java @@ -0,0 +1,48 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.app.Fragment; +import android.content.pm.ActivityInfo; + +import com.shaya.poinila.android.presentation.view.fragments.CollectionPageFragment; +import com.shaya.poinila.android.presentation.view.fragments.PostAndRelatedPostFragment; +import com.shaya.poinila.android.presentation.view.fragments.PostListFragment; + +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_COLLECTION_POSTS; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_EXPLORE; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_MEMBER_POSTS; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_POST_RELATED_POSTS; + +public class PostListActivity extends FragmentHostActivity { + + @Override + public void handleIntentExtras() { + super.handleIntentExtras(); + // handling shareable urls + } + + @Override + protected void initUI() { + + } + + @Override + protected android.support.v4.app.Fragment getHostedFragment() { + switch (requestID) { + case REQUEST_COLLECTION_POSTS: + return CollectionPageFragment.newInstance(mainEntityID, secondEntityID, requestID); + case REQUEST_EXPLORE: + return PostListFragment.newInstance(mainEntityID, secondEntityID, requestID); + case REQUEST_MEMBER_POSTS: + return PostListFragment.newInstance(mainEntityID, secondEntityID, requestID); + case REQUEST_POST_RELATED_POSTS: + return PostAndRelatedPostFragment.newInstance(mainEntityID, secondEntityID, requestID); + default: // never call default mode + return PostListFragment.newInstance(mainEntityID, secondEntityID, requestID); + } + } + + @Override + protected boolean withToolbar() { + return true; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/ProfileSettingActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/ProfileSettingActivity.java new file mode 100755 index 0000000..9dc436a --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/ProfileSettingActivity.java @@ -0,0 +1,251 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.os.Bundle; +import android.support.v4.app.NavUtils; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.TextView; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.SimpleSettingTextSetEvent; +import com.shaya.poinila.android.presentation.view.dialog.ChangeNameDialog; +import com.shaya.poinila.android.presentation.view.dialog.EditAboutMeDialog; +import com.shaya.poinila.android.presentation.view.dialog.PoinilaAlertDialog; +import com.shaya.poinila.android.util.Logger; +import com.squareup.otto.Subscribe; + +import butterknife.Bind; +import butterknife.BindString; +import butterknife.OnClick; +import data.PoinilaNetService; +import data.event.ProfileSettingReceivedEvent; +import data.event.UpdateProfileSettingResponse; +import data.model.Member; + +import static com.shaya.poinila.android.util.StringUtils.emptyIfNull; + +public class ProfileSettingActivity extends ToolbarActivity { + + private static final String TAG_CHANGE_NAME_DIALOG = "change name"; + //private static final String TAG_CHANGE_USERNAME_DIALOG = "change username"; + private static final String TAG_CHANGE_EMAIL_DIALOG = "change email"; + private static final String TAG_CHANGE_PHONE_DIALOG = "change phone"; + @Bind(R.id.name) View nameItem; + //@Bind(R.actorID.username) View usernameItem; + //@Bind(R.id.password) View passwordItem; + @Bind(R.id.email) View emailItem; + @Bind(R.id.about_me) View aboutMeItem; + @Bind(R.id.phone) View phoneItem; + //@Bind(R.actorID.gender) View genderItem; + @Bind(R.id.website) View websiteItem; + /*@Bind(R.id.deactivate) View deactivateItem; + @Bind(R.id.deactivate_switch) Switch deactivateSwitch;*/ + + @BindString(R.string.name) String name; + //@BindString (R.string.username) String username; + //@BindString (R.string.password) String passwordStringRes; + @BindString (R.string.email) String email; + @BindString (R.string.about_me) String aboutMe; + @BindString (R.string.phone) String phone; + //@BindString (R.string.gender) String gender; + @BindString (R.string.website) String website; + /* @BindString(R.string.deactivate) String deactivate;*/ + + private Member originalProfile; + private Member changedProfile; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + protected void initUI() { //TODO: gender nadarim tu requestesh! :)) + String[] labels = {name, email, phone, aboutMe, website}; + View[] items = {nameItem, emailItem, phoneItem, aboutMeItem, websiteItem}; + for (int i = 0; i < items.length; i++){ + ((TextView) items[i].findViewById(R.id.label)).setText(labels[i]); + } + + showProgressDialog(); + /*deactivateSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + changedProfile.isActive = isChecked; + } + });*/ + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_done_action, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + if (id == R.id.action_submit_changes){ + if (changedProfile != null) { +// PoinilaNetService.updateProfileSetting(changedProfile); + return true; + } + } + return super.onOptionsItemSelected(item); + } + + @Subscribe public void onResponse(UpdateProfileSettingResponse response){ + if (response.success){ + Logger.toast(R.string.successfully_updated); + getActivity().finish(); + } + } + + @Override + protected int getLayoutResourceId() { + return R.layout.activity_profile_setting; + } + + @OnClick(R.id.name) + public void onName(){ + ChangeNameDialog.newInstance(changedProfile.fullName).show(getSupportFragmentManager(), null); + } + + /* @OnClick(R.actorID.username) + public void onUsername(){ + //TODO + new PoinilaAlertDialog.Builder().setPositiveText(ResourceUtils.getString(R.string.submit)). + setNegativeText(ResourceUtils.getString(R.string.cancelBtn)). + setBody(new ChangeUserameDialog()). + build().show(getSupportFragmentManager(), TAG_CHANGE_USERNAME_DIALOG); + }*/ + + //TODO: this is temporary. future releases must let user to change phone/email through verification process +// @OnClick(R.id.email) +// public void onEmail(){ +// //TODO +// ChangeEmailDialog.newInstance(changedProfile.email).show(getFragmentManager(), TAG_CHANGE_EMAIL_DIALOG); +// } + + @OnClick(R.id.about_me) + public void onAboutMe(){ + EditAboutMeDialog.newInstance(changedProfile.aboutMe).show(getSupportFragmentManager(), null); + } + + + /*@OnClick( R.id.phone) + public void onPhone(){ + ChangePhoneDialog.newInstance(changedProfile.mobileNumber).show(getFragmentManager(), TAG_CHANGE_PHONE_DIALOG); + }*/ + + /*@OnClick(R.actorID.gender) public void onGender(){ + new PoinilaAlertDialog.Builder().setPositiveText(ResourceUtils.getString(R.string.submit)). + setNegativeText(ResourceUtils.getString(R.string.cancelBtn)). + setBody(new CHANGE_GENDER_DIALOG()). + build().show(getSupportFragmentManager(), null); + }*/ + + @OnClick(R.id.website) public void onWebsite(){ +// ChangeWebsiteDialog.newInstance(changedProfile.url, changedProfile.urlName).show(getSupportFragmentManager(), null); + } + + @Subscribe public void onSimpleSettingValueSet(SimpleSettingTextSetEvent event){ + switch (event.settingType){ + case FullName: + changedProfile.fullName = event.value; + ((TextView) nameItem.findViewById(R.id.value)).setText(changedProfile.fullName); + break; + case EMAIL: + changedProfile.email = event.value; + ((TextView) emailItem.findViewById(R.id.value)).setText(changedProfile.email); + break; + case PHONE: + changedProfile.mobileNumber = event.value; + ((TextView) phoneItem.findViewById(R.id.value)).setText(changedProfile.mobileNumber); + break; + case ABOUT_ME: + changedProfile.aboutMe = event.value; + ((TextView) aboutMeItem.findViewById(R.id.value)).setText(changedProfile.aboutMe); + break; + case WEBSITE: + String[] nameUrl = event.value.split("&"); + ((TextView)websiteItem.findViewById(R.id.value)).setText(nameUrl[0] + "\n" + nameUrl[1]); + changedProfile.urlName = nameUrl[0]; + changedProfile.url = nameUrl[1]; + break; + /*case PHONE: + ((TextView)phoneItem.findViewById(R.id.value)).setText(event.value); + changedProfile.mobileNumber = event.value; + break;*/ + } + + + } + + @Subscribe public void onUserProfileSettingReceived(ProfileSettingReceivedEvent event){ + originalProfile = event.member; + requestOnFirstTime = false; + changedProfile = new Member(originalProfile); + fill(originalProfile); + onSuccessfulResponse(); + } + + private void fill(Member originalProfile) { + String website = emptyIfNull(originalProfile.urlName) + "\n" + emptyIfNull(originalProfile.url); + String[] values = new String[]{originalProfile.fullName, originalProfile.email, originalProfile.aboutMe, website}; + View[] items = {nameItem, emailItem, aboutMeItem, websiteItem}; + for (int i = 0; i < values.length; i++) { + ((TextView) items[i].findViewById(R.id.value)).setText(values[i]); + } + } + + @Override + protected void onStart() { + super.onStart(); + if (requestOnFirstTime) + PoinilaNetService.getProfileSettings(); + } + + // in first look it's possible for user not to notice save action menu and lose his/her data accidentally + @Override + protected void handleUpNavigation() { + if (originalProfile != null && !originalProfile.equals(changedProfile)) { + new PoinilaAlertDialog.Builder(). + setMessage(R.string.discared_changes). + setPositiveBtnText(R.string.yes). + setNegativeBtnText(R.string.no). + setPositiveBtnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + NavUtils.navigateUpFromSameTask(getActivity()); + } + }). + build().show(getSupportFragmentManager(), null); + }else{ + super.handleUpNavigation(); + } + } + + @Override + public void onBackPressed() { + if (originalProfile != null && !originalProfile.equals(changedProfile)) { + new PoinilaAlertDialog.Builder(). + setMessage(R.string.discared_changes). + setPositiveBtnText(R.string.yes). + setNegativeBtnText(R.string.no). + setPositiveBtnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + ProfileSettingActivity.super.onBackPressed(); + } + }). + build().show(getSupportFragmentManager(), null); + }else{ + super.onBackPressed(); + } + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/SelectInterestActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/SelectInterestActivity.java new file mode 100755 index 0000000..720b721 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/SelectInterestActivity.java @@ -0,0 +1,229 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; + +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewProvider; +import com.shaya.poinila.android.presentation.uievent.CheckBoxClickUIEvent; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.presentation.view.fragments.LoginFragment; +import com.shaya.poinila.android.presentation.viewholder.SelectableInterestViewHolder; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.util.StringUtils; +import com.squareup.otto.Subscribe; + +import java.util.ArrayList; +import java.util.List; + +import butterknife.Bind; +import butterknife.OnClick; +import data.PoinilaNetService; +import data.event.BaseEvent; +import data.event.InterestsReceivedEvent; +import data.event.ServerResponseEvent; +import data.event.UserInterestsReceivedEvent; +import data.model.ImageTag; +import data.model.Tag; +import manager.DataRepository; + +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_FIRST_LOGIN; + +public class SelectInterestActivity extends BaseActivity { + + public static final int MINIMUM_ACCEPTABLE_INTERESTS = 5; + private RecyclerViewAdapter mAdapter; + //private HashSet userInterestsIDs; + private List userInterests; + private boolean firstTimeAfterRegister; + private int checkedInterestCount; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // TODO: save and restore checkedInterestCount; + } + + @Bind(R.id.selected_count) + TextView checkedItemsCountTextView; + @Bind(R.id.submit) Button submitBtn; + @Bind(R.id.cancel) Button cancelBtn; + @Bind(R.id.recycler_view) RecyclerView mRecyclerView; + + @Override + protected void initUI() { + mRecyclerView = new RecyclerViewProvider(mRecyclerView). + setGridLayoutManager(GridLayoutManager.VERTICAL, + getResources().getInteger(R.integer.column_count)). + setAdapter(new RecyclerViewAdapter( + getActivity(), R.layout.selectable_interest) { + @Override + protected SelectableInterestViewHolder getProperViewHolder(View v, int viewType) { + return new SelectableInterestViewHolder(v); + } + }). + bindViewToAdapter(); + mRecyclerView.getItemAnimator().setChangeDuration(0); // avoiding blink on rendering + mAdapter = ((RecyclerViewAdapter) mRecyclerView.getAdapter()); + //TODO: handle first url + userInterests = DataRepository.getInstance().getTempModel(List.class); + if (userInterests == null) + userInterests = new ArrayList<>(MINIMUM_ACCEPTABLE_INTERESTS); + + if (firstTimeAfterRegister){ + cancelBtn.setVisibility(View.GONE); + updateCheckedInterestsCount(); + } else // we must show and update counter only directly after registering + checkedItemsCountTextView.setVisibility(View.GONE); + + } + + @Override + protected void onStart() { + super.onStart(); + if (requestOnFirstTime) + PoinilaNetService.getInterests(); + + if (!firstTimeAfterRegister && userInterests.isEmpty()) + PoinilaNetService.getMemberInterests(DataRepository.getInstance().getMyId()); + } + + public List selectUserInterests(List receivedInterests) { + if (!userInterests.isEmpty()) { + for (ImageTag interest : receivedInterests) { + interest.selected = userInterests.contains(interest); + if (interest.selected) + PoinilaNetService.getSubInterests(interest.getId()); + } + } + return receivedInterests; + } + + private void updateInterests() { + progressDialog.setMessage(getString(R.string.please_wait)); + progressDialog.show(); + List selectedInterests = new ArrayList<>(); + for (Object tag : mAdapter.getItems()) { + if (((Tag)tag).selected) + selectedInterests.add(((Tag)tag).id); + } + PoinilaNetService.updateUserInterests(selectedInterests); + } + + @OnClick(R.id.submit) + public void onSubmit() { + if (firstTimeAfterRegister && !hasSelectedEnoughInterest(checkedInterestCount)) { + Logger.toast(R.string.error_min_selected_interest); + return; + } + updateInterests(); + } + + @OnClick(R.id.cancel) + public void onCancel() { // when first time after register, cancel button is hidden + finish(); + } + + // I hate my coding style! ServerResponse could be for any request :( + @Subscribe public void onServerResponse(ServerResponseEvent event){ + progressDialog.dismiss(); + if (event.receiverName != BaseEvent.ReceiverName.SelectInterest) + return; + if (!event.succeed){ + Logger.toastError(R.string.error_add_interests); + Logger.debugToast("updating interests failed"); + } else if (firstTimeAfterRegister) { + progressDialog.setMessage(getString(R.string.loading_creating_suggestions)); + progressDialog.show(); + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { + @Override + public void run() { + if (progressDialog != null){ + progressDialog.dismiss(); + PageChanger.goToDashboard(SelectInterestActivity.this); + } + } + }, 30000); // isn't it too much for user? + }else{ + onCancel(); + } + } + + /*-----------------------*/ + // important: either of responses might be received first so we call update function in each. + // if app interest were not null and user interests were received, pre conditions is met. + @Subscribe + public void onInterestsReceived(InterestsReceivedEvent event) { // general interest! not related to the user + requestOnFirstTime = false; + if (!firstTimeAfterRegister) + selectUserInterests(event.interests); + mAdapter.addItems(event.interests); + } + + @Subscribe + public void onUserInterestsReceived(UserInterestsReceivedEvent event) { + if (requestOnFirstTime) { + userInterests = event.userInterests; + selectUserInterests(mAdapter.getItems()); + } + } + /*------------------------*/ + + @Subscribe + public void onInterestChecked(CheckBoxClickUIEvent event) { + ImageTag interest = mAdapter.getItem(event.adapterPosition); + interest.selected = event.checked; + + userInterests.add(interest); + + mAdapter.notifyItemChanged(event.adapterPosition); + if (firstTimeAfterRegister) + updateCheckedInterestsCount(); + PoinilaNetService.getSubInterests(interest.getId()); + } + + private void updateCheckedInterestsCount() { + int checkedInterestsCount = 0; + for (Object imageTag : mAdapter.getItems()) { + if (((ImageTag)imageTag).selected) + checkedInterestsCount++; + } + this.checkedInterestCount = checkedInterestsCount; + if (!hasSelectedEnoughInterest(checkedInterestsCount)) { + ViewUtils.setText(checkedItemsCountTextView, StringUtils.getStringWithPersianNumber( + getString(R.string.x_outof_y_formatted), checkedInterestsCount, MINIMUM_ACCEPTABLE_INTERESTS)); + checkedItemsCountTextView.setTextColor(ContextCompat.getColor(getActivity(), R.color.flamingo)); + } else { + ViewUtils.setText(checkedItemsCountTextView, StringUtils.persianNumber(checkedInterestsCount)); + checkedItemsCountTextView.setTextColor(ContextCompat.getColor(getActivity(), android.R.color.holo_green_dark)); + } + } + + private boolean hasSelectedEnoughInterest(int checkedInterestsCount) { + return checkedInterestsCount >= MINIMUM_ACCEPTABLE_INTERESTS; + } + + @Override + protected void handleToolbar() { + } + + @Override + protected int getLayoutResourceId() { + return R.layout.activity_select_interests; + } + + @Override + protected void handleIntentExtras() { + super.handleIntentExtras(); + firstTimeAfterRegister = getIntent().getBooleanExtra(KEY_FIRST_LOGIN, false); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/SettingActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/SettingActivity.java new file mode 100755 index 0000000..846aacf --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/SettingActivity.java @@ -0,0 +1,413 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.text.TextUtils; +import android.view.View; +import android.widget.TextView; +import android.widget.Toast; + +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.Result; +import com.google.android.gms.common.api.ResultCallback; +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.AfterVerifyResponse; +import com.shaya.poinila.android.presentation.uievent.PositiveButtonClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.SimpleSettingTextSetEvent; +import com.shaya.poinila.android.presentation.view.dialog.ChangeEmailDialog; +import com.shaya.poinila.android.presentation.view.dialog.ChangeNameDialog; +import com.shaya.poinila.android.presentation.view.dialog.ChangePhoneDialog; +import com.shaya.poinila.android.presentation.view.dialog.ChangeWebsiteDialog; +import com.shaya.poinila.android.presentation.view.dialog.DialogLauncher; +import com.shaya.poinila.android.presentation.view.dialog.EditAboutMeDialog; +import com.shaya.poinila.android.presentation.view.dialog.PoinilaAlertDialog; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.util.NavigationUtils; +import com.shaya.poinila.android.utils.PonilaAccountManager; +import com.squareup.otto.Subscribe; + +import butterknife.Bind; +import butterknife.BindString; +import butterknife.OnClick; +import data.PoinilaNetService; +import data.event.ProfileSettingReceivedEvent; +import data.event.UpdateProfileSettingResponse; +import data.model.Member; +import manager.DataRepository; + +import static com.shaya.poinila.android.util.StringUtils.emptyIfNull; + +public class SettingActivity extends ToolbarActivity { + +// @Bind(R.id.profile) +// View profileItem; + + @Bind(R.id.name) View nameItem; + //@Bind(R.actorID.username) View usernameItem; + //@Bind(R.id.password) View passwordItem; + @Bind(R.id.email) View emailItem; + @Bind(R.id.about_me) View aboutMeItem; + @Bind(R.id.phone) View phoneItem; + //@Bind(R.actorID.gender) View genderItem; + @Bind(R.id.website) View websiteItem; + /*@Bind(R.id.deactivate) View deactivateItem; + @Bind(R.id.deactivate_switch) Switch deactivateSwitch;*/ + + @Bind(R.id.password) + View passwordItem; + @Bind(R.id.app_settings) + View appSettingsItem; + @Bind(R.id.frames) + View framesItem; + @Bind(R.id.circles) + View circlesItem; + @Bind(R.id.app_notifications) + View appNotifsItem; + //@Bind(R.id.email_notifications) View emailNotifsItem; + @Bind(R.id.logout) + View logoutItem; + @Bind(R.id.help) + View helpItem; + @Bind(R.id.contact_us) + View contactUsItem; + @Bind(R.id.about_poinila) + View aboutPoinilaItem; + @Bind(R.id.rules) + View rulesItem; + +// @BindString(R.string.profile) +// String profileText; + + @BindString(R.string.name) String name; + //@BindString (R.string.username) String username; + //@BindString (R.string.password) String passwordStringRes; + @BindString (R.string.email) String email; + @BindString (R.string.about_me) String aboutMe; + @BindString (R.string.phone) String phone; + //@BindString (R.string.gender) String gender; + @BindString (R.string.website) String website; + /* @BindString(R.string.deactivate) String deactivate;*/ + + @BindString (R.string.setting_rules_item) String rulesTxt; + + @BindString(R.string.password) + String passwordText; + @BindString(R.string.app_settings) + String appSettingsTxt; + @BindString(R.string.frames_management) + String frameManagementTxt; + @BindString(R.string.manage_circle) + String circleManagementTxt; + @BindString(R.string.app_notifs) + String appNotifsTxt; + //@BindString(R.string.email_notifs) String emailNotifsTxt; + @BindString(R.string.logout) + String logoutTxt; + @BindString(R.string.tutorial) + String tutorialTxt; + @BindString(R.string.contact_us) + String contact_usTxt; + @BindString(R.string.about_poinila) + String aboutPoinilaTxt; + + + private Member originalProfile; + private Member changedProfile; + + private static final String TAG_CHANGE_PHONE_DIALOG = "change phone"; + private static final String TAG_CHANGE_EMAIL_DIALOG = "change email"; + + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + PonilaAccountManager.getInstance().initGoogleAPIClient(this, new GoogleApiClient.OnConnectionFailedListener() { + @Override + public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { + + } + }); + } + + @Override + protected void initUI() { + String[] labels = {passwordText, appSettingsTxt, frameManagementTxt, circleManagementTxt, appNotifsTxt, + logoutTxt, tutorialTxt, contact_usTxt, aboutPoinilaTxt, rulesTxt}; + View[] items = {passwordItem, appSettingsItem, framesItem, circlesItem, appNotifsItem, + logoutItem, helpItem, contactUsItem, aboutPoinilaItem, rulesItem}; + for (int i = 0; i < items.length; i++) { + ((TextView) items[i].findViewById(R.id.label)).setText(labels[i]); + if(items[i].equals(framesItem)){ + items[i].findViewById(R.id.help_btn).setVisibility(View.VISIBLE); + items[i].setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + DialogLauncher.launchMessageDialog(getSupportFragmentManager(), R.string.frame, R.string.frame_about); + } + }); + } + + if(items[i].equals(circlesItem)){ + items[i].findViewById(R.id.help_btn).setVisibility(View.VISIBLE); + items[i].setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + DialogLauncher.launchMessageDialog(getSupportFragmentManager(), R.string.circle, R.string.circle_about); + } + }); + } + + + } + + // Profile Frame + String[] profileLabels = {name, email, phone, aboutMe, website}; + View[] profileItems = {nameItem, emailItem, phoneItem, aboutMeItem, websiteItem}; + for (int i = 0; i < profileItems.length; i++){ + ((TextView) profileItems[i].findViewById(R.id.label)).setText(profileLabels[i]); + } + + } + + @Override + protected void onStart() { + super.onStart(); + if(requestOnFirstTime) + PoinilaNetService.getProfileSettings(); + } + + @Subscribe public void onUserProfileSettingReceived(ProfileSettingReceivedEvent event){ + originalProfile = event.member; + requestOnFirstTime = false; + changedProfile = new Member(originalProfile); + fill(originalProfile); + } + + @Subscribe public void onSimpleSettingValueSet(SimpleSettingTextSetEvent event){ + switch (event.settingType){ + case FullName: + changedProfile.fullName = event.value; + ((TextView) nameItem.findViewById(R.id.value)).setText(changedProfile.fullName); + break; + case EMAIL: + changedProfile.email = event.value; + ((TextView) emailItem.findViewById(R.id.value)).setText(changedProfile.email); + break; + case PHONE: + changedProfile.mobileNumber = event.value; + ((TextView) phoneItem.findViewById(R.id.value)).setText(changedProfile.mobileNumber); + break; + case ABOUT_ME: + changedProfile.aboutMe = event.value; + ((TextView) aboutMeItem.findViewById(R.id.value)).setText(changedProfile.aboutMe); + break; + case WEBSITE: + String[] nameUrl = event.value.split("&"); + ((TextView)websiteItem.findViewById(R.id.value)).setText(nameUrl[0] + "\n" + nameUrl[1]); + changedProfile.urlName = nameUrl[0]; + changedProfile.url = nameUrl[1]; + break; + } + + showProgressDialog(); + PoinilaNetService.updateProfileSetting(changedProfile, event.settingType); + } + +// @OnClick(R.id.profile) +// public void onProfile() { +// NavigationUtils.goToActivity(ProfileSettingActivity.class, getActivity()); +// } + + + @Subscribe + public void onUpdateProfileSettingResponse(UpdateProfileSettingResponse event){ + dismissProgressDialog(); + switch (event.settingType){ + case EMAIL: + DialogLauncher.launchRequestVerificationDialog(getSupportFragmentManager(), R.string.email_change, changedProfile.email, true); + break; + case PHONE: + DialogLauncher.launchRequestVerificationDialog(getSupportFragmentManager(), R.string.phone_change, changedProfile.mobileNumber, false); + break; + default: + Logger.toast(R.string.successfully_updated); + originalProfile = changedProfile; + fill(originalProfile); + } + } + + @Subscribe + public void onAfterVerifyResponse(AfterVerifyResponse event){ + originalProfile = changedProfile; + fill(originalProfile); + } + + @OnClick(R.id.email) + public void onEmail(){ + //TODO + DialogLauncher.launchRequestVerificationDialog(getSupportFragmentManager(), R.string.email_change, changedProfile.email, true); +// ChangeEmailDialog.newInstance(changedProfile.email).show(getSupportFragmentManager(), TAG_CHANGE_EMAIL_DIALOG); + } + + @OnClick(R.id.phone) + public void onPhone(){ + DialogLauncher.launchRequestVerificationDialog(getSupportFragmentManager(), R.string.phone_change, changedProfile.mobileNumber, false); +// ChangePhoneDialog.newInstance(changedProfile.mobileNumber).show(getSupportFragmentManager(), TAG_CHANGE_PHONE_DIALOG); + } + + @OnClick(R.id.password) + public void onPassword() { + NavigationUtils.goToActivity(ChangePasswordActivity.class, getActivity()); + } + + + + @OnClick(R.id.frames) + public void onFrames() { + NavigationUtils.goToActivity(FramesManagementActivity.class, getActivity()); + } + + @OnClick(R.id.circles) + public void onCircles() { + // TODO + NavigationUtils.goToActivity(CirclesManagementActivity.class, getActivity()); + } + + @OnClick(R.id.app_settings) + public void onAppSettings() { + NavigationUtils.goToActivity(AppSettingActivity.class, getActivity()); + } + + @OnClick(R.id.app_notifications) + public void onAppNotifs() { + Intent intent = NavigationUtils.makeNavigationIntent(NotificationSwitchActivity.class, getActivity()); + intent.putExtra(ConstantsUtils.KEY_REQUEST_ID, ConstantsUtils.REQUEST_APPLICATION_NOTIFICATION); + startActivity(intent); + } + + /*@OnClick(R.id.email_notifications) public void onEmailNotifs(){ + Intent intent = NavigationUtils.makeNavigationIntent(NotificationSwitchActivity.class, getActivity()); + intent.putExtra(ConstantsUtils.KEY_REQUEST_ID, ConstantsUtils.REQUEST_EMAIL_NOTIFICATION); + startActivity(intent); + }*/ + + + private String getValue(String value){ + return TextUtils.isEmpty(value) ? "" : value; + } + + + @OnClick(R.id.name) + public void onName(){ + if(changedProfile != null) + ChangeNameDialog.newInstance(getValue(changedProfile.fullName)).show(getSupportFragmentManager(), null); + else + Logger.toastError(R.string.profile_change_not_found); + } + + @OnClick(R.id.about_me) + public void onAboutMe(){ + + if(changedProfile != null) + EditAboutMeDialog.newInstance(getValue(changedProfile.aboutMe)).show(getSupportFragmentManager(), null); + else + Logger.toastError(R.string.profile_change_not_found); + } + + @OnClick(R.id.website) public void onWebsite(){ + + if(changedProfile != null) + ChangeWebsiteDialog.newInstance(getValue(changedProfile.url), getValue(changedProfile.urlName)).show(getSupportFragmentManager(), null); + else + Logger.toastError(R.string.profile_change_not_found); + } + + + @OnClick(R.id.rules) + public void onRules(){ + DialogLauncher.launchMessageDialog(getSupportFragmentManager(), R.string.setting_rules_item, R.string.setting_rule); + } + + private void fill(Member originalProfile) { + String website = ""; + if(!TextUtils.isEmpty(originalProfile.url)) + website = emptyIfNull(originalProfile.urlName) + "\n" + emptyIfNull(originalProfile.url); + else + website = emptyIfNull(originalProfile.urlName); + + String[] values = new String[]{originalProfile.fullName, originalProfile.email, originalProfile.mobileNumber, originalProfile.aboutMe, website}; + View[] items = {nameItem, emailItem, phoneItem, aboutMeItem, websiteItem}; + for (int i = 0; i < values.length; i++) { + ((TextView) items[i].findViewById(R.id.value)).setText(values[i]); + } + } + + @OnClick(R.id.logout) + public void onLogout() { + new PoinilaAlertDialog.Builder().setTitle(R.string.logout). + setMessage(R.string.warning_logout_earase_data). + setPositiveBtnText(R.string.logout). + setNegativeBtnText(R.string.cancel). + build().show(getSupportFragmentManager(), null); + } + + @OnClick(R.id.help) + public void onHelp() { + PageChanger.goToHelpActivity(getActivity(), true); + } + + @OnClick(R.id.contact_us) + public void onContactUs() { + DialogLauncher.launchContactUsDialog(getSupportFragmentManager()); + } + + @OnClick(R.id.about_poinila) + public void onAboutPoinila() { + DialogLauncher.launchAboutPoinila(getFragmentManager()); + } + + @Subscribe + public void onPositiveDialogButton(PositiveButtonClickedUIEvent event) { + if(PonilaAccountManager.getInstance().isSignInWithGoogle()){ + if(PonilaAccountManager.getInstance().isConnectedGoogleApiClient()) { + PonilaAccountManager.getInstance().signOutWithGoogleAPI(new ResultCallback() { + @Override + public void onResult(@NonNull Result result) { + //TODO ANY + } + }); + }else { + Logger.toastError(R.string.error_google_connection); + return; + } + } + + PonilaAccountManager.getInstance().removeUserTag(); + DataRepository.logoutEvent(); + } + + @Override + protected int getLayoutResourceId() { + return R.layout.activity_setting; + } + + public enum SettingType { + USERNAME, + EMAIL, + CIRCLE_NAME, + NEW_CIRCLE, + NEW_FRAME, + FRAME_NAME, + GENDER, + WEBSITE, + ABOUT_ME, + FullName, + PHONE + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/SignUpLoginActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/SignUpLoginActivity.java new file mode 100755 index 0000000..353ceb9 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/SignUpLoginActivity.java @@ -0,0 +1,220 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.TextView; + +import com.shaya.poinila.android.presentation.BuildConfig; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.dialog.DialogLauncher; +import com.shaya.poinila.android.presentation.view.dialog.ForgotPasswordFragment; +import com.shaya.poinila.android.presentation.view.fragments.LoginFragment; +import com.shaya.poinila.android.presentation.view.fragments.RegisterFragment; +import com.shaya.poinila.android.presentation.view.fragments.ResetPasswordFragment; +import com.shaya.poinila.android.presentation.view.fragments.VerificationRequestFragment; +import com.shaya.poinila.android.util.DeviceInfoUtils; +import com.shaya.poinila.android.util.Logger; + +import butterknife.Bind; + +public class SignUpLoginActivity extends BaseActivity { + + private static final String KEY_STATE = "state"; + private static final String TAG_LOGIN_FRAGMENT = "login"; + // public static final String PHASE_LOGIN = "select interest"; + @Bind(R.id.welcome_text) + TextView welcome_text; + + private int state; + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (getIntent().getData() != null) { // TODO + if (BuildConfig.DEBUG) + Logger.toast(getIntent().getData().toString()); + handleUrl(getIntent().getData()); + } else if (savedInstanceState != null) { + // TODO + state = savedInstanceState.getInt(KEY_STATE); + switch (state) { + case ForgotPassword: + goToForgotPassword(); + break; + case Login: + goToLoginFragment(true); + break; + case Register: + goToRegister(null); + break; + case RequestVerificationCode: + goToVerificationRequest(); + break; + case ResetPassword: + goToResetPassword(null); + break; + } + } else { + goToLoginFragment(true); + } + } + + private void handleUrl(Uri uri) { + if (uri.getPath().contains("register")) { + goToRegister(uri.getLastPathSegment()); + /*RegisterFragment fragment = ((RegisterFragment) getSupportFragmentManager().findFragmentById(R.id.content)); + if (fragment.isVisible()) fragment.fillVerificationCode(uri.getLastPathSegment());*/ + + } else if (uri.getPath().contains("resetpassword")) { + goToResetPassword(uri.getLastPathSegment()); + /* ResetPasswordFragment fragment = ((ResetPasswordFragment) getSupportFragmentManager().findFragmentById(R.id.content)); + if (fragment.isVisible()) fragment.fillIdentificationCodeInput();*/ + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + outState.putInt(KEY_STATE, state); + super.onSaveInstanceState(outState); + } + + @Override + protected void initUI() { + + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_sign_up_login, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + return true; + } + + return super.onOptionsItemSelected(item); + } + + @Override + protected void handleToolbar() { + } + + @Override + protected int getLayoutResourceId() { + return R.layout.activity_sign_up_login; + } + + public void goToLoginFragment(boolean proceed) { + if (proceed) { + getSupportFragmentManager().beginTransaction(). + replace(R.id.container, new LoginFragment(), TAG_LOGIN_FRAGMENT). + //addToBackStack(TAG_LOGIN_FRAGMENT). + commit(); + } else { // recede + //getSupportFragmentManager().popBackStack(TAG_LOGIN_FRAGMENT, FragmentManager.POP_BACK_STACK_INCLUSIVE); + // temp + getSupportFragmentManager().beginTransaction(). + replace(R.id.container, new LoginFragment(), TAG_LOGIN_FRAGMENT). + //addToBackStack(TAG_LOGIN_FRAGMENT). + commit(); + } + welcome_text.setText(R.string.welcome_text); + welcome_text.setOnClickListener(null); + + state = Login; + } + + public void goToRegister(String code, boolean byEmail, String emailOrPhone) { + getSupportFragmentManager().beginTransaction(). + setCustomAnimations(R.anim.fade_in, R.anim.fade_out). + replace(R.id.container, RegisterFragment.newInstance(code, byEmail, emailOrPhone)). + //addToBackStack(null). + commit(); + + state = Register; + } + + public void goToRegister(String code) { + getSupportFragmentManager().beginTransaction(). + setCustomAnimations(R.anim.fade_in, R.anim.fade_out). + replace(R.id.container, code != null ? RegisterFragment.newInstance(code) : new RegisterFragment()). + //addToBackStack(null). + commit(); + + state = Register; + } + + public void goToVerificationRequest() { + getSupportFragmentManager().beginTransaction(). + setCustomAnimations(R.anim.fade_in, R.anim.fade_out). + replace(R.id.container, new VerificationRequestFragment()). + //addToBackStack(null). + commit(); + + showLoginText(); + + state = RequestVerificationCode; + } + + private void showLoginText() { + welcome_text.setText(R.string.already_registered); + welcome_text.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + goToLoginFragment(false); + } + }); + } + + public void goToResetPassword(String code) { + getSupportFragmentManager().beginTransaction(). + setCustomAnimations(R.anim.fade_in, R.anim.fade_out). + replace(R.id.container, code != null ? ResetPasswordFragment.newInstance(code) : new ResetPasswordFragment()). + //addToBackStack(null). + commit(); + + state = ResetPassword; + } + + public void goToForgotPassword() { + getSupportFragmentManager().beginTransaction(). + setCustomAnimations(R.anim.fade_in, R.anim.fade_out). + replace(R.id.container, new ForgotPasswordFragment()). + //addToBackStack(null). + commit(); + + showLoginText(); + + state = ForgotPassword; + /*new PoinilaDialog.Builder().setTitle(R.string.recover_password) + .setMessage(R.string.recover_password_message).setPositiveText(R.string.submit).setNegativeText(R.string.cancel) + .setBody(new ForgotPasswordFragment()) + .build().show(getChildFragmentManager(), null);*/ + } + + private static final int Login = 1; + private static final int RequestVerificationCode = 2; + private static final int ResetPassword = 3; + private static final int ForgotPassword = 4; + private static final int Register = 5; +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/SplashActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/SplashActivity.java new file mode 100755 index 0000000..9ed7e1f --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/SplashActivity.java @@ -0,0 +1,115 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.os.Bundle; +import android.os.Handler; + +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.util.ConnectionUitls; +import com.shaya.poinila.android.util.NavigationUtils; +import com.shaya.poinila.android.util.PoinilaPreferences; +import com.shaya.poinila.android.utils.PonilaAccountManager; +import com.squareup.otto.Subscribe; + +import data.PoinilaNetService; +import data.event.MyInfoReceivedEvent; +import data.model.Member; +import manager.DBFacade; +import manager.DataRepository; + + +public class SplashActivity extends BaseActivity { + + // Splash screen timer + private static int SPLASH_TIME_OUT = 2500; + private long start; + private boolean responseIsReceived = false; + private long MIN_SPLASH_DURATION = 1000; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); + } + + @Override + protected void handleToolbar() { + + } + + @Override + protected void onStart() { + super.onStart(); + + sendPreliminaryRequests(); + new Handler().postDelayed(new Runnable() { + + /* + * Showing splash screen with a timer. This will be useful when you + * want to show case your app logo / company + */ + + @Override + public void run() { + // This method will be executed once the timer is over + // Start your app main activity + if (!responseIsReceived) { + navigateFromSplashScreen(); + } + } + }, SPLASH_TIME_OUT); + } + + @Override + protected void initUI() { + + } + + private void navigateFromSplashScreen() { +// Member cachedMember = DBFacade.getCachedMyInfo(); +// boolean hasAnonymouslyLoggedInBefore = responseIsReceived ? +// DataRepository.isUserAnonymous() : +// (cachedMember == null || cachedMember.isAnonymous); // first time || has cached guest info + + if(PonilaAccountManager.getInstance().ponilaAccountExists()){ + goToDashboard(); + }else if (!PoinilaPreferences.hasSeenHelp()) + PageChanger.goToHelpActivity(getActivity(), false); + else + goToLogin(); + +// if (!TextUtils.isEmpty(PoinilaPreferences.getAuthToken()) && !hasAnonymouslyLoggedInBefore) { +// // normal authenticated user +// goToDashboard(); +// } else if (!PoinilaPreferences.hasSeenHelp()) +// PageChanger.goToHelpActivity(getActivity(), false); +// else { +// goToLogin(); +// } + } + + private void sendPreliminaryRequests() { + //start = System.currentTimeMillis(); + DataRepository.getInstance(); + PoinilaNetService.getRemainedInvites(); // DataRepository receives the event + PoinilaNetService.getServerTime(); + PoinilaNetService.getSystemPreferences(); + } + + private void goToLogin() { + PageChanger.goToLoginActivity(getActivity()); + finish(); + } + + private void goToDashboard() { + NavigationUtils.goToActivity(MainActivity.class, getActivity()); + finish(); + } + + @Override + protected int getLayoutResourceId() { + return R.layout.activity_splash; + } + + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/ToolbarActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/ToolbarActivity.java new file mode 100755 index 0000000..f84efdd --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/ToolbarActivity.java @@ -0,0 +1,45 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.os.Bundle; +import android.support.v7.app.ActionBar; +import android.support.v7.widget.Toolbar; +import android.view.View; +import android.view.ViewGroup; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.activity.BaseActivity; + +/** + * Created by iran on 2015-08-09. + */ +public abstract class ToolbarActivity extends BaseActivity { + + protected Toolbar toolbar; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + protected View getActivityView() { + ViewGroup vp = (ViewGroup) getLayoutInflater().inflate(getLayoutResourceId(), null); + toolbar = (Toolbar) getLayoutInflater().inflate(R.layout.toolbar, vp, false); + handleToolbar(); + vp.addView(toolbar, 0); + return vp; + } + + @Override + protected void handleToolbar() { + //Toolbar toolbar = ButterKnife.findById(this, R.actorID.toolbar); + setSupportActionBar(toolbar); + + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null){ + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setHomeButtonEnabled(true); + } + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/activity/WebviewActivity.java b/src/main/java/com/shaya/poinila/android/presentation/view/activity/WebviewActivity.java new file mode 100755 index 0000000..1431d6d --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/activity/WebviewActivity.java @@ -0,0 +1,222 @@ +package com.shaya.poinila.android.presentation.view.activity; + +import android.annotation.SuppressLint; +import android.app.Fragment; +import android.os.Build; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.util.Patterns; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.webkit.WebChromeClient; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.ProgressBar; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.fragments.BusFragment; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.util.NavigationUtils; + +import java.util.HashMap; +import java.util.Map; + +import butterknife.Bind; +import data.PoinilaNetService; +import manager.DataRepository; + +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_ENTITY; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_PAGE_TITLE_PARAMETER; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_POST_ID; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_WEBSITE_URL; + +public class WebviewActivity extends FragmentHostActivity { + + @Override + protected android.support.v4.app.Fragment getHostedFragment() { + return PoinilaWebViewFragment.newInstance( + getIntent().getStringExtra(KEY_WEBSITE_URL), + getIntent().getStringExtra(KEY_PAGE_TITLE_PARAMETER), + getIntent().getStringExtra(KEY_ENTITY)); + } + + @Override + protected boolean withToolbar() { + return true; + } + + @Override + protected void initUI() { + } + + public static String getPostUrl(String postId){ + return ConstantsUtils.POINILA_SERVER_ADDRESS.concat("post/" + postId + "/"); + } + + public static class PoinilaWebViewFragment extends BusFragment { + @Bind(R.id.webview) + WebView webview; + ProgressBar progressBar; + + private String postId, url, pageTitle; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + + url = getArguments().getString(KEY_WEBSITE_URL); + pageTitle = getArguments().getString(KEY_PAGE_TITLE_PARAMETER); + postId = getArguments().getString(KEY_POST_ID); + + if (!DataRepository.isUserAnonymous()) + PoinilaNetService.informServerOfInlineBrowsing(postId); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + menu.clear(); + inflater.inflate(R.menu.menu_inline_browser, menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.browser) { + if (!DataRepository.isUserAnonymous()) + PoinilaNetService.informServerOfExternalBrowsing(postId); + + Bundle extraHeaders = new Bundle(); + extraHeaders.putString("Referer", getPostUrl(postId)); + NavigationUtils.goToUrl(getActivity(), url, extraHeaders); + return true; + } + return super.onOptionsItemSelected(item); + } + + @Override + protected boolean sendsRequestAutomatically() { + return true; + } + + @Override + protected void requestInitialData() { + } + + @Override + public ViewGroup getLoadableView() { + return webview; + } + + @Override + public boolean mustShowProgressView() { + return true; + } + + @Override + public int getLayoutID() { + return R.layout.fragment_webview; + } + + /*@Override + public ProgressBar getProgressBar() { + ProgressBar progressBar = ButterKnife.findById(progressView, R.id.progress_bar); + if (progressBar == null){ + progressBar = new ProgressBar(getActivity(), null, android.R.attr.progressBarStyleHorizontal); + progressBar.setIndeterminate(false); + } + return progressBar; + }*/ + + @Override + protected void initProgressBar(ProgressBar progressBar) { + + } + + @Override + protected int getProgressViewLayoutID() { + return R.layout.progress_determinate; + } + + @SuppressLint("SetJavaScriptEnabled") + @Override + protected void initUI() { + if (url == null || !Patterns.WEB_URL.matcher(url).matches()) { + Logger.toastError(R.string.error_invalid_url); + getActivity().finish(); + return; + } + getActivity().setTitle(pageTitle); + + webview.getSettings().setJavaScriptEnabled(true); + webview.getSettings().setAllowContentAccess(false); + webview.getSettings().setAllowFileAccess(false); + webview.getSettings().setDomStorageEnabled(true); + webview.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); + webview.getSettings().setAppCacheEnabled(true); + webview.getSettings().setRenderPriority(WebSettings.RenderPriority.HIGH); + webview.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS); + + webview.getSettings().setSupportZoom(true); + webview.getSettings().setBuiltInZoomControls(true); + webview.getSettings().setDisplayZoomControls(false); + + webview.setInitialScale(1); + webview.getSettings().setLoadWithOverviewMode(true); + webview.getSettings().setUseWideViewPort(true); + webview.getSettings().setSaveFormData(true); + + if (Build.VERSION.SDK_INT >= 17) { + webview.getSettings().setMediaPlaybackRequiresUserGesture(false); + } + + + + if (Build.VERSION.SDK_INT >= 19) { + webview.setLayerType(View.LAYER_TYPE_HARDWARE, null); + } + else { + webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null); + } + + webview.setWebChromeClient(new WebChromeClient() { + public void onProgressChanged(WebView view, int progress) { + // Activities and WebViews measure progress with different scales. + // The progress meter will automatically disappear when we reach 100% + getProgressBar().setProgress(progress); + if (progress == 100) + onGettingInitDataResponse(null); + } + }); + webview.setWebViewClient(new WebViewClient() { + // disable page navigation through webview. webview must show only the initial url + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + if (url != null && Patterns.WEB_URL.matcher(url).matches()) { + NavigationUtils.goToUrl(getActivity(), url, null); + } + return true; + } + }); + + // from Wikipedia: The HTTP referrer (originally a misspelling of referrer)... + // So don't change the string!! + Map extraHeaders = new HashMap<>(); + extraHeaders.put("Referer", getPostUrl(postId)); + webview.loadUrl(url, extraHeaders); + } + + public static android.support.v4.app.Fragment newInstance(String url, String pageTitle, String postId) { + PoinilaWebViewFragment fragment = new PoinilaWebViewFragment(); + Bundle b = new Bundle(); + b.putString(KEY_WEBSITE_URL, url); + b.putString(KEY_PAGE_TITLE_PARAMETER, pageTitle); + b.putString(KEY_POST_ID, postId); + fragment.setArguments(b); + return fragment; + } + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/ActivityResultPermissionDelegate.java b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/ActivityResultPermissionDelegate.java new file mode 100755 index 0000000..b210483 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/ActivityResultPermissionDelegate.java @@ -0,0 +1,131 @@ +package com.shaya.poinila.android.presentation.view.costom_view; + +import android.app.Activity; +import android.app.Fragment; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v13.app.FragmentCompat; +import android.support.v4.app.ActivityCompat; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.PermissionEvent; +import com.shaya.poinila.android.presentation.view.activity.BaseActivity; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.util.PoinilaPreferences; + +import static android.app.Activity.RESULT_OK; + +/** + * Created by iran on 12/13/2015. + */ +public interface ActivityResultPermissionDelegate { + void onActivityResult(int requestCode, int resultCode, Intent data); + + void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults); + + + // -------------------- + + + abstract class SimpleActivityResultPermissionDelegate implements ActivityResultPermissionDelegate { + + public void startForResult(AppCompatActivity activity, Intent intent, int requestCode) { + activity.startActivityForResult(intent, requestCode); + } + + public void startForResult(android.support.v4.app.Fragment fragment, Intent intent, int requestCode) { + fragment.startActivityForResult(intent, requestCode); + } + + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode != RESULT_OK) + return; + handleValidResults(requestCode, data); + } + + public abstract void handleValidResults(int requestCode, Intent data); + + public void askForPermission(Activity activity, String permission, int requestCode){ + ActivityCompat.requestPermissions(activity, new String[]{permission}, requestCode); + } + + public void askForPermission(android.support.v4.app.Fragment fragment, String permission, int requestCode){ + fragment.requestPermissions(new String[]{permission}, requestCode); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + if (requestCode == BaseActivity.MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE){ + if (grantResults.length > 0 + && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + // permission was granted, yay! + handlePermissionGranted(); + } else { + // permission denied, boo! Disable the + // functionality that depends on this permission. + handlePermissionDenied(); + } + } + } + + public abstract void handlePermissionDenied(); + + public abstract void handlePermissionGranted(); + } + + // --------------------- + + abstract class ImagePickerResultPermissionDelegate extends SimpleActivityResultPermissionDelegate { + protected String imageAddress; + public String getImageAddress() { + return imageAddress; + } + + @Override + public void handleValidResults(int requestCode, Intent data) { + + switch (requestCode) { + case ConstantsUtils.REQUEST_CODE_TAKE_PHOTO: + imageAddress = PoinilaPreferences.getCapturedImageAddress(); // using no media_output in capture image intent + break; + case ConstantsUtils.REQUEST_CODE_PICK_IMAGE: + imageAddress = data.getData().toString(); + break; + } + } + + public void handlePermissionDenied(){ + Logger.toast(R.string.permission_reason_camera); + } + } + + // ----------------------- +/* + class SmsPermissionHandler extends SimpleActivityResultPermissionDelegate{ + + @Override + public void handlePermissionDenied() { + Logger.longToast(getString(R.string.permission_reason_sms)); + smsPermissionDeclined = true; + } + + @Override + public void handlePermissionGranted() { + getActivity().registerReceiver(smsReceiver, new IntentFilter("android.provider.Telephony.SMS_RECEIVED")); + } + + public registerSmsReceiver(Activity activity){ + + } + + @Override + public void handleValidResults(int requestCode, Intent data) {}// we don't start activity for result, a better design perhaps. + }*/ +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/AspectRatioImageView.java b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/AspectRatioImageView.java new file mode 100755 index 0000000..3e75dff --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/AspectRatioImageView.java @@ -0,0 +1,110 @@ +package com.shaya.poinila.android.presentation.view.costom_view; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.widget.ImageView; + +import com.shaya.poinila.android.presentation.R; + +// Copyright 2012 Square, Inc. + +/** Maintains an aspect ratio based on either width or height. Disabled by default. + * Created by iran on 2015-08-05. + */ +public class AspectRatioImageView extends ImageView { + // NOTE: These must be kept in sync with the AspectRatioImageView attributes in attrs.xml. + public static final int MEASUREMENT_WIDTH = 0; + public static final int MEASUREMENT_HEIGHT = 1; + + private static final float DEFAULT_ASPECT_RATIO = 1f; + private static final boolean DEFAULT_ASPECT_RATIO_ENABLED = false; + private static final int DEFAULT_DOMINANT_MEASUREMENT = MEASUREMENT_WIDTH; + + private float aspectRatio; + private boolean aspectRatioEnabled; + private int dominantMeasurement; + + public AspectRatioImageView(Context context) { + this(context, null); + } + + public AspectRatioImageView(Context context, AttributeSet attrs) { + super(context, attrs); + + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AspectRatioImageView); + aspectRatio = a.getFloat(R.styleable.AspectRatioImageView_aspectRatio, DEFAULT_ASPECT_RATIO); + aspectRatioEnabled = a.getBoolean(R.styleable.AspectRatioImageView_aspectRatioEnabled, + DEFAULT_ASPECT_RATIO_ENABLED); + dominantMeasurement = a.getInt(R.styleable.AspectRatioImageView_dominantMeasurement, + DEFAULT_DOMINANT_MEASUREMENT); + a.recycle(); + } + + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + if (!aspectRatioEnabled) return; + + int newWidth; + int newHeight; + switch (dominantMeasurement) { + case MEASUREMENT_WIDTH: + newWidth = getMeasuredWidth(); + newHeight = (int) (newWidth * aspectRatio); + break; + + case MEASUREMENT_HEIGHT: + newHeight = getMeasuredHeight(); + newWidth = (int) (newHeight * aspectRatio); + break; + + default: + throw new IllegalStateException("Unknown measurement with ID " + dominantMeasurement); + } + + setMeasuredDimension(newWidth, newHeight); + } + + /** Get the aspect ratio for this image view. */ + public float getAspectRatio() { + return aspectRatio; + } + + /** Set the aspect ratio for this image view. This will update the view instantly. */ + public void setAspectRatio(float aspectRatio) { + this.aspectRatio = aspectRatio; + if (aspectRatioEnabled) { + requestLayout(); + } + } + + /** Get whether or not forcing the aspect ratio is enabled. */ + public boolean getAspectRatioEnabled() { + return aspectRatioEnabled; + } + + /** set whether or not forcing the aspect ratio is enabled. This will re-layout the view. */ + public void setAspectRatioEnabled(boolean aspectRatioEnabled) { + this.aspectRatioEnabled = aspectRatioEnabled; + requestLayout(); + } + + /** Get the dominant measurement for the aspect ratio. */ + public int getDominantMeasurement() { + return dominantMeasurement; + } + + /** + * Set the dominant measurement for the aspect ratio. + * + * @see #MEASUREMENT_WIDTH + * @see #MEASUREMENT_HEIGHT + */ + public void setDominantMeasurement(int dominantMeasurement) { + if (dominantMeasurement != MEASUREMENT_HEIGHT && dominantMeasurement != MEASUREMENT_WIDTH) { + throw new IllegalArgumentException("Invalid measurement type."); + } + this.dominantMeasurement = dominantMeasurement; + requestLayout(); + } +} \ No newline at end of file diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/BackForthButtonsBox.java b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/BackForthButtonsBox.java new file mode 100755 index 0000000..fb75110 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/BackForthButtonsBox.java @@ -0,0 +1,89 @@ +package com.shaya.poinila.android.presentation.view.costom_view; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.widget.Button; +import android.widget.LinearLayout; + +import com.shaya.poinila.android.presentation.R; + +import butterknife.Bind; +import butterknife.ButterKnife; +import butterknife.OnClick; + +/** + * Created by iran on 12/6/2015. + */ +public class BackForthButtonsBox extends LinearLayout { + + String lastButtonText = null; + String nextButtonText = null; + + @Bind(R.id.next_button) + Button nextButton; + @Bind(R.id.last_button) + Button lastButton; + + public BackForthButtonsBox(Context context) { + super(context); + + init(context); + } + + private void init(Context context) { + setOrientation(LinearLayout.HORIZONTAL); + setGravity(Gravity.CENTER_HORIZONTAL); + setWeightSum(2); + + LayoutInflater.from(context).inflate(R.layout.next_last_buttons, this, true); + ButterKnife.bind(this, this); + + nextButton.setText(nextButtonText == null ? context.getString(R.string.next_phase) : nextButtonText); + lastButton.setText(lastButtonText == null ? context.getString(R.string.last_phase) : lastButtonText); + } + + public BackForthButtonsBox(Context context, AttributeSet attrs) { + super(context, attrs); + TypedArray typedArray; + typedArray = context.obtainStyledAttributes(attrs, R.styleable.BackForthButtonsBox); + lastButtonText = typedArray.getString(R.styleable.BackForthButtonsBox_last_button_text); + nextButtonText = typedArray.getString(R.styleable.BackForthButtonsBox_next_button_text); + + typedArray.recycle(); + + init(context); + } + + @OnClick(R.id.next_button) + public void onForth() { + if (listener != null) { + listener.onForth(); + } + } + + @OnClick(R.id.last_button) + public void onBack() { + if (listener != null) { + listener.onBack(); + } + } + + public OnBackForthListener getBackForthListener() { + return listener; + } + + public void setBackForthListener(OnBackForthListener listener) { + this.listener = listener; + } + + private OnBackForthListener listener; + + public interface OnBackForthListener { + void onBack(); + + void onForth(); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/CustomScrollView.java b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/CustomScrollView.java new file mode 100755 index 0000000..0fb5121 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/CustomScrollView.java @@ -0,0 +1,62 @@ +package com.shaya.poinila.android.presentation.view.costom_view; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.widget.ScrollView; + +/** + * Created by iran on 2015-09-01. + */ +public class CustomScrollView extends ScrollView { + private ScrollViewListener scrollViewListener = null; + public CustomScrollView(Context context) { + super(context); + } + + public CustomScrollView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public CustomScrollView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public void setScrollViewListener(ScrollViewListener scrollViewListener) { + this.scrollViewListener = scrollViewListener; + } + + @Override + protected void onScrollChanged(int l, int t, int oldl, int oldt) { + super.onScrollChanged(l, t, oldl, oldt); + if (scrollViewListener != null) { + scrollViewListener.onScrollChanged(this, l, t, oldl, oldt); + } + } + + /*---Post activity. related post----*/ + public void setLastNoneListViewItem(View lastNoneListViewItem) { + this.lastNoneListViewItem = lastNoneListViewItem; + } + + View lastNoneListViewItem; + + @Override + public boolean onInterceptTouchEvent(@NonNull MotionEvent ev) { + /* + * This method JUST determines whether we want to intercept the motion. + * If we return true, onTouchEvent will be called and we do the actual + * scrolling there. + */ + if (lastNoneListViewItem != null) { + int diff = (getScrollY() - lastNoneListViewItem.getBottom()); + boolean lastScrollViewItemVisible = (diff < 0); + if (!lastScrollViewItemVisible || !canScrollVertically(-1)){ + return false; + } + } + return super.onInterceptTouchEvent(ev); + } +} \ No newline at end of file diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/EditCollectionImagePickerView.java b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/EditCollectionImagePickerView.java new file mode 100755 index 0000000..2b802b9 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/EditCollectionImagePickerView.java @@ -0,0 +1,63 @@ +package com.shaya.poinila.android.presentation.view.costom_view; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.ImageButton; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.ImageClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.PermissionEvent; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.presentation.view.dialog.DialogLauncher; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.Logger; +import com.squareup.otto.Subscribe; + +import butterknife.Bind; +import butterknife.OnClick; +import butterknife.OnLongClick; + +/** + * Created by AlirezaF on 12/24/2015. + */ +public class EditCollectionImagePickerView extends GalleryCameraImagePickerView{ + @Bind(R.id.cover_from_posts) ImageButton pickCoverFromPostBtn; + + public EditCollectionImagePickerView(Context context) { + this(context, null); + } + + public EditCollectionImagePickerView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public EditCollectionImagePickerView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @OnLongClick public boolean showPickFromPostsHint(){ + Logger.toast(R.string.hint_coverFromPosts); + return true; + } + + @OnClick(R.id.cover_from_posts) public void onPickCoverFromPosts(){ + if (listener != null) listener.onPickCoverFromPosts(); + } + + + @Override + protected void init() { + ViewUtils.setViewsVisibilityToVisible(findViewById(R.id.pick_from_posts_left_divider), pickCoverFromPostBtn); + super.init(); + } + + OnPickCoverFromPostsListener listener; + public interface OnPickCoverFromPostsListener{ + void onPickCoverFromPosts(); + } + + public void setOnPickCoverFromPostsListener(OnPickCoverFromPostsListener listener){ + this.listener = listener; + } + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/GalleryCameraImagePickerView.java b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/GalleryCameraImagePickerView.java new file mode 100755 index 0000000..9a0cc03 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/GalleryCameraImagePickerView.java @@ -0,0 +1,423 @@ +package com.shaya.poinila.android.presentation.view.costom_view; + +import android.Manifest; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.IntDef; +import android.support.annotation.LayoutRes; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageButton; +import android.widget.LinearLayout; + +import com.isseiaoki.simplecropview.CropImageView; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.PermissionEvent; +import com.shaya.poinila.android.presentation.uievent.SelectImageEvent; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.DeviceInfoUtils; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.util.ResourceUtils; +import com.shaya.poinila.android.util.StorageUtils; +import com.squareup.picasso.Picasso; +import com.squareup.picasso.Target; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; + +import butterknife.Bind; +import butterknife.ButterKnife; +import butterknife.OnClick; +import butterknife.OnLongClick; + +import static com.shaya.poinila.android.presentation.view.ViewUtils.setViewsVisibilityToGone; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setViewsVisibilityToVisible; + +/** + * Created by iran on 12/1/2015. + */ +public class GalleryCameraImagePickerView extends FrameLayout implements ImagePickerCropper { + + // related to crop + @Bind(R.id.cropView) + CropImageView mCropImageView; + + @Bind(R.id.doneButton) + ImageButton mDoneButton; + @Bind(R.id.removeButton) + ImageButton mRemoveButton; + @Bind(R.id.cropButton) + ImageButton mCropButton; + // related to rotate + @Bind(R.id.rotateRightButton) + ImageButton mRotateRightButton; + @Bind(R.id.rotateLeftButton) + ImageButton mRotateLeftButton; + + //TODO: ability to rotate image in factor of 90 degrees + //@Bind(R.id.doneButton) ImageButton mRotateButton; + + // related to pick image + @Bind(R.id.pick_image_container) + LinearLayout mPickerLayout; + @Bind(R.id.galleryButton) + ImageButton mGalleryButton; + @Bind(R.id.cameraButton) + ImageButton mCameraButton; + @Bind(R.id.image_container) + ViewGroup mCropViewContainer; + + public ShowMode showMode = ShowMode.Selecting; + public Policy policy = Policy.FullFeatures; + private boolean waitingForPermissionGrant = false; + protected Target picassoTarget = new Target() { + @Override + public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { + setImage(bitmap); + } + @Override + public void onBitmapFailed(Drawable errorDrawable) {} + @Override + public void onPrepareLoad(Drawable placeHolderDrawable) {} + }; + + + public Bitmap getImage() { + switch (showMode) { + case Showing: + return getImageBitmap(); + case Cropping: + return getCroppedImageBitmap(); + } + return null; + } + + public boolean hasImage() { + return showMode != ShowMode.Selecting; + } + + public boolean isInCropMode() { + return showMode == ShowMode.Cropping; + } + + public enum ShowMode { + Cropping, + Showing, + Selecting + } + + public enum Policy { + FullFeatures, + NoFeature, + CropFullScreen, + } + + public GalleryCameraImagePickerView(Context context) { + this(context, null); + } + + public GalleryCameraImagePickerView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public GalleryCameraImagePickerView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + LayoutInflater inflater = ((LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)); + inflater.inflate(getLayoutId(), this, true); + ButterKnife.bind(this, this); + + TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.GalleryCameraImagePickerView, defStyle, 0); + if (ta.getInt(R.styleable.GalleryCameraImagePickerView_crop_ratio, 1) == 2) + mCropImageView.setCropMode(CropImageView.CropMode.RATIO_FREE); + ta.recycle(); + + init(); + } + + protected void init() { + switch (showMode) { + case Cropping: + goToCropMode(); + break; + case Showing: + goToShowMode(); + break; + case Selecting: + goToSelectMode(); + break; + } + } + + private + @LayoutRes + int getLayoutId() { + return R.layout.crop_image; + } + + /*---------------ImagePickerCropper------------*/ + + /** + * Manually sets picker view image to passed bitmap. Usage is discouraged. Use {@link #setImage(String)} instead + * + * @param bitmap + */ + @Override + public void setImage(Bitmap bitmap) { + mCropImageView.setImageBitmap(bitmap); + onImageSet(); + } + + /** + * Gives an address in form of absolute file address, content uri and url and renders it using + * picasso api and set it as cropView image. + * + * @param address + */ + @Override + public void setImage(String address) { + // Picasso can't handle addresses in form of "/mnt/sdcard0..." "/storage/..." + + address = StorageUtils.isLocalNonContentFile(address) ? StorageUtils.toSchemedFileAddress(address) : address; + + Picasso.with(getContext()).load(address). + resize(ResourceUtils.getDisplayMetrics().widthPixels, ResourceUtils.getDisplayMetrics().heightPixels). + centerInside(). + config(Bitmap.Config.ARGB_8888). + into(mCropImageView); + + onImageSet(); + } + + // good place for declaring custom behaviors + private void onImageSet() { + mPickerLayout.setVisibility(View.GONE); + switch (policy){ + case CropFullScreen: + case FullFeatures: + goToCropMode(); + break; + case NoFeature: + goToShowMode(); + break; + } + } + + /** + * Changes the state of the view (updates the buttons) and replaces the bitmap with cropped bitmap + */ + @OnClick(R.id.doneButton) + public void submitCrop() { + cropImage(); + goToShowMode(); + } + + public void goToShowMode() { + mCropViewContainer.setVisibility(View.VISIBLE); + if (policy == Policy.FullFeatures) { + setViewsVisibilityToVisible(mRemoveButton, mCropButton); + setViewsVisibilityToGone(mDoneButton, mRotateLeftButton, mRotateRightButton); + } else if (policy == Policy.NoFeature || policy == Policy.CropFullScreen) { + setViewsVisibilityToGone(new ArrayList(Arrays.asList( + mDoneButton, mRemoveButton, mCropButton, mRotateLeftButton, mRotateRightButton))); + } + mCropImageView.setCropEnabled(false); + showMode = ShowMode.Showing; + } + + /** + * Not used normally. If you just want to invoke crop process call {@link #submitCrop()} instead. + */ + @OnClick(R.id.cropButton) + public void goToCropMode() { + mCropViewContainer.setVisibility(View.VISIBLE); + + if (policy == Policy.FullFeatures) { + setViewsVisibilityToVisible(new ArrayList(Arrays.asList(mRemoveButton, mDoneButton, mRotateLeftButton, mRotateRightButton))); + setViewsVisibilityToGone(new ArrayList(Collections.singletonList(mCropButton))); + } else if (policy == Policy.CropFullScreen) { + setViewsVisibilityToVisible(new ArrayList(Arrays.asList(mRotateLeftButton, mRotateRightButton))); + setViewsVisibilityToGone(new ArrayList(Arrays.asList(mCropButton, mDoneButton, mRemoveButton))); + } + mCropImageView.setCropEnabled(true); + showMode = ShowMode.Cropping; + } + + @Override + @OnClick(R.id.removeButton) + public void removeImage() { + goToSelectMode(); + } + + private void goToSelectMode() { + mPickerLayout.setVisibility(View.VISIBLE); + mCropViewContainer.setVisibility(View.GONE); + showMode = ShowMode.Selecting; + } + + /** + * Not used normally. If you just want to invoke crop process call {@link #submitCrop()} instead.
+ * Crops the image and set it as new image resource + */ + @Override + public void cropImage() { + mCropImageView.setImageBitmap(mCropImageView.getCroppedBitmap()); + } + + /** + * For getting bitmap considering picker view state call {@link #getImage()} + * + * @return picker view initial bitmap + */ + @Override + public Bitmap getImageBitmap() { + return mCropImageView.getImageBitmap(); + } + + // TODO: use BitmapRegionDecoder for cropping original image + + /** + * For getting bitmap considering picker view state call {@link #getImage()} + * + * @return bitmap in crop region + */ + @Override + public Bitmap getCroppedImageBitmap() { + return mCropImageView.getCroppedBitmap(); + } + + + //Define the list of accepted constants + @IntDef({ROTATE_RIGHT_90, ROTATE_LEFT_90}) + //Tell the compiler not to store annotation data in the .class file + @Retention(RetentionPolicy.SOURCE) + //Declare the NavigationMode annotation + public @interface RotateStep { + } + + public static final int ROTATE_RIGHT_90 = 270; + public static final int ROTATE_LEFT_90 = 90; + + @Override + public void rotate(@RotateStep int degrees) { + if (degrees == ROTATE_RIGHT_90) { + mCropImageView.rotateImage(CropImageView.RotateDegrees.ROTATE_90D); + } else if (degrees == ROTATE_LEFT_90) { + mCropImageView.rotateImage(CropImageView.RotateDegrees.ROTATE_270D); + } else { + throw new IllegalArgumentException("use either ROTATE_RIGHT_90 or ROTATE_LEFT_90"); + } + } + + @OnClick(R.id.rotateRightButton) + void rotateRight90Degrees() { + rotate(ROTATE_RIGHT_90); + } + + @OnClick(R.id.rotateLeftButton) + void rotateLeft90Degrees() { + rotate(ROTATE_LEFT_90); + } + + @OnClick(R.id.cameraButton) + public void pickFromCamera() { + BusProvider.getBus().post(new PermissionEvent(Manifest.permission.WRITE_EXTERNAL_STORAGE)); + } + + @OnLongClick(R.id.cameraButton) + public boolean showCameraHint() { + Logger.toast(R.string.hint_take_picture); + return true; + } + + @OnClick(R.id.galleryButton) + public void pickFromGallery() { + BusProvider.getBus().post(new SelectImageEvent( + StorageUtils.dispatchSelectImageIntent(), ConstantsUtils.REQUEST_CODE_PICK_IMAGE)); + } + + @OnLongClick(R.id.galleryButton) + public boolean showGalleryHint() { + Logger.toast(R.string.hint_pick_from_gallery); + return true; + } + + public void startPickingFromCamera() { + + } + + + // Save/Restore support //////////////////////////////////////////////////////////////////////// + + @Override + public Parcelable onSaveInstanceState() { + Parcelable superState = super.onSaveInstanceState(); + SavedState ss = new SavedState(superState); + ss.showMode = this.showMode; + ss.policy = this.policy; + return ss; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + SavedState ss = (SavedState) state; + super.onRestoreInstanceState(ss.getSuperState()); + this.showMode = ss.showMode; + this.policy = ss.policy; + } + + public static class SavedState extends BaseSavedState { + ShowMode showMode; + Policy policy; + + SavedState(Parcelable superState) { + super(superState); + } + + private SavedState(Parcel in) { + super(in); + showMode = (ShowMode) in.readSerializable(); + policy = (Policy) in.readSerializable(); + } + + @Override + public void writeToParcel(Parcel out, int flag) { + super.writeToParcel(out, flag); + out.writeSerializable(showMode); + out.writeSerializable(policy); + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public SavedState createFromParcel(final Parcel inParcel) { + return new SavedState(inParcel); + } + + public SavedState[] newArray(final int inSize) { + return new SavedState[inSize]; + } + }; + } + + /*@Subscribe public void onPermissionResult(PermissionEvent event){ + if (!waitingForPermissionGrant) + return; + + if (event.requestCode == BaseActivity.MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE){ + if (event.granted){ + BusProvider.getBus().post(new SelectImageEvent( + StorageUtils.dispatchCapturePhotoIntent(), ConstantsUtils.REQUEST_CODE_TAKE_PHOTO)); + } else + Logger.longToast(getContext().getString(R.string.permission_reason_camera)); + } + }*/ +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/HorizontalChipsLayout.java b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/HorizontalChipsLayout.java new file mode 100755 index 0000000..82245fc --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/HorizontalChipsLayout.java @@ -0,0 +1,86 @@ +package com.shaya.poinila.android.presentation.view.costom_view; + +import android.content.Context; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; +import android.view.View; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewProvider; +import com.shaya.poinila.android.presentation.viewholder.RemovableTagViewHolder; + +/** + * Created by iran on 12/16/2015. + */ +public class HorizontalChipsLayout extends RecyclerView { + + private RecyclerViewAdapter> mAdapter; + + public HorizontalChipsLayout(Context context) { + this(context, null); + } + + public HorizontalChipsLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public HorizontalChipsLayout(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context, attrs); + } + + private void init(Context context, AttributeSet attrs) { + // Load attributes + //final TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.RemovableChipView, defStyle, 0); + //a.recycle(); + + mAdapter = new RecyclerViewAdapter>(context, R.layout.removable_chip) { + @Override + protected RemovableTagViewHolder getProperViewHolder(View v, int viewType) { + return new RemovableTagViewHolder(v) { + @Override + public void fill(String s) { + textView.setText(s); + } + }; + } + }; + new RecyclerViewProvider(new RecyclerView(context)). + setAdapter(mAdapter). + setLinearLayoutManager(VERTICAL). + bindViewToAdapter(); + + for (int i = 0; i < 4; i++) { + mAdapter.addItem("fsdlkjf"); + } + } + + + + /*private static class ChipsAdapter extends RecyclerView.Adapter{ + + + public ChipsAdapter(){ + + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + return null; + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + + } + + @Override + public int getItemCount() { + return 0; + } + }*/ + + + //public static class RemovableChip extends {} +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/ImagePickerCropper.java b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/ImagePickerCropper.java new file mode 100755 index 0000000..a974778 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/ImagePickerCropper.java @@ -0,0 +1,16 @@ +package com.shaya.poinila.android.presentation.view.costom_view; + +import android.graphics.Bitmap; + +/** + * Created by iran on 12/1/2015. + */ +public interface ImagePickerCropper { + void setImage(Bitmap bitmap); + void setImage(String address); + Bitmap getImageBitmap(); + Bitmap getCroppedImageBitmap(); + void cropImage(); + void removeImage(); + void rotate(int CWWDegrees); +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/MaterialSpinner.java b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/MaterialSpinner.java new file mode 100755 index 0000000..957cdbc --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/MaterialSpinner.java @@ -0,0 +1,852 @@ +/* +package com.shaya.poinila.android.presentation.view.costom_view; + +*/ +/** + * Created by iran on 1/10/2016. + *//* + + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.Point; +import android.graphics.Typeface; +import android.os.Build; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.AppCompatSpinner; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.util.AttributeSet; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.LinearInterpolator; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.SpinnerAdapter; +import android.widget.TextView; + +import com.nineoldandroids.animation.ObjectAnimator; +import com.nineoldandroids.animation.ValueAnimator; +import com.shaya.poinila.android.presentation.R; + + +public class MaterialSpinner extends AppCompatSpinner implements ValueAnimator.AnimatorUpdateListener { + + public static final int DEFAULT_ARROW_WIDTH_DP = 12; + + private static final String TAG = MaterialSpinner.class.getSimpleName(); + + //Paint objects + private Paint paint; + private TextPaint textPaint; + private StaticLayout staticLayout; + + + private Path selectorPath; + private Point[] selectorPoints; + + //Inner padding = "Normal" android padding + private int innerPaddingLeft; + private int innerPaddingRight; + private int innerPaddingTop; + private int innerPaddingBottom; + + //Private padding to add space for FloatingLabel and ErrorLabel + private int extraPaddingTop; + private int extraPaddingBottom; + + //@see dimens.xml + private int underlineTopSpacing; + private int underlineBottomSpacing; + private int errorLabelSpacing; + private int floatingLabelTopSpacing; + private int floatingLabelBottomSpacing; + private int floatingLabelInsideSpacing; + private int rightLeftSpinnerPadding; + private int minContentHeight; + + //Properties about Error Label + private int lastPosition; + private ObjectAnimator errorLabelAnimator; + private int errorLabelPosX; + private int minNbErrorLines; + private float currentNbErrorLines; + + + //Properties about Floating Label ( + private float floatingLabelPercent; + private ObjectAnimator floatingLabelAnimator; + private boolean isSelected; + private boolean floatingLabelVisible; + private int baseAlpha; + + + //AttributeSet + private int baseColor; + private int highlightColor; + private int errorColor; + private int disabledColor; + private CharSequence error; + private CharSequence hint; + private int hintColor; + private CharSequence floatingLabelText; + private int floatingLabelColor; + private boolean multiline; + private Typeface typeface; + private boolean alignLabels; + private float thickness; + private float thicknessError; + private int arrowColor; + private float arrowSize; + private boolean enableErrorLabel; + private boolean enableFloatingLabel; + private boolean isRtl; + + private HintAdapter hintAdapter; + + */ +/* + * ********************************************************************************** + * CONSTRUCTORS + * ********************************************************************************** + *//* + + + public MaterialSpinner(Context context) { + super(context); + init(context, null); + } + + public MaterialSpinner(Context context, AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + + } + + public MaterialSpinner(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context, attrs); + } + + + */ +/* + * ********************************************************************************** + * INITIALISATION METHODS + * ********************************************************************************** + *//* + + + private void init(Context context, AttributeSet attrs) { + + initAttributes(context, attrs); + initPaintObjects(); + initDimensions(); + initPadding(); + initFloatingLabelAnimator(); + initOnItemSelectedListener(); + setMinimumHeight(getPaddingTop() + getPaddingBottom() + minContentHeight); + //Erase the drawable selector not to be affected by new size (extra paddings) + // alireza farahani + setBackgroundResource(fr.ganfra.materialspinner.R.drawable.my_background); + //setBackgroundResource(R.drawable.bordered_focusable_float_hint_margin); + + } + + private void initAttributes(Context context, AttributeSet attrs) { + + TypedArray defaultArray = context.obtainStyledAttributes(new int[]{R.attr.colorControlNormal, R.attr.colorAccent}); + int defaultBaseColor = defaultArray.getColor(0, 0); + int defaultHighlightColor = defaultArray.getColor(1, 0); + int defaultErrorColor = ContextCompat.getColor(context, R.color.error_color); + defaultArray.recycle(); + + TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MaterialSpinner); + baseColor = array.getColor(R.styleable.MaterialSpinner_ms_baseColor, defaultBaseColor); + highlightColor = array.getColor(R.styleable.MaterialSpinner_ms_highlightColor, defaultHighlightColor); + errorColor = array.getColor(R.styleable.MaterialSpinner_ms_errorColor, defaultErrorColor); + disabledColor = context.getResources().getColor(R.color.disabled_color); + error = array.getString(R.styleable.MaterialSpinner_ms_error); + hint = array.getString(R.styleable.MaterialSpinner_ms_hint); + hintColor = array.getColor(R.styleable.MaterialSpinner_ms_hintColor, baseColor); + floatingLabelText = array.getString(R.styleable.MaterialSpinner_ms_floatingLabelText); + floatingLabelColor = array.getColor(R.styleable.MaterialSpinner_ms_floatingLabelColor, baseColor); + multiline = array.getBoolean(R.styleable.MaterialSpinner_ms_multiline, true); + minNbErrorLines = array.getInt(R.styleable.MaterialSpinner_ms_nbErrorLines, 1); + alignLabels = array.getBoolean(R.styleable.MaterialSpinner_ms_alignLabels, true); + thickness = array.getDimension(R.styleable.MaterialSpinner_ms_thickness, 1); + thicknessError = array.getDimension(R.styleable.MaterialSpinner_ms_thickness_error, 2); + arrowColor = array.getColor(R.styleable.MaterialSpinner_ms_arrowColor, baseColor); + arrowSize = array.getDimension(R.styleable.MaterialSpinner_ms_arrowSize, dpToPx(DEFAULT_ARROW_WIDTH_DP)); + enableErrorLabel = array.getBoolean(R.styleable.MaterialSpinner_ms_enableErrorLabel, true); + enableFloatingLabel = array.getBoolean(R.styleable.MaterialSpinner_ms_enableFloatingLabel, true); + isRtl = array.getBoolean(R.styleable.MaterialSpinner_ms_isRtl, false); + + String typefacePath = array.getString(R.styleable.MaterialSpinner_ms_typeface); + if (typefacePath != null) { + typeface = Typeface.createFromAsset(getContext().getAssets(), typefacePath); + } + + array.recycle(); + + floatingLabelPercent = 0f; + errorLabelPosX = 0; + isSelected = false; + floatingLabelVisible = false; + lastPosition = -1; + currentNbErrorLines = minNbErrorLines; + + } + + + @Override + public void setSelection(final int position) { + this.post(new Runnable() { + @Override + public void run() { + MaterialSpinner.super.setSelection(position); + } + }); + } + + private void initPaintObjects() { + + int labelTextSize = getResources().getDimensionPixelSize(R.dimen.label_text_size); + + paint = new Paint(Paint.ANTI_ALIAS_FLAG); + + textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + textPaint.setTextSize(labelTextSize); + if (typeface != null) { + textPaint.setTypeface(typeface); + } + textPaint.setColor(baseColor); + baseAlpha = textPaint.getAlpha(); + + selectorPath = new Path(); + selectorPath.setFillType(Path.FillType.EVEN_ODD); + + selectorPoints = new Point[3]; + for (int i = 0; i < 3; i++) { + selectorPoints[i] = new Point(); + } + } + + @Override + public int getSelectedItemPosition() { + return super.getSelectedItemPosition(); + } + + private void initPadding() { + + innerPaddingTop = getPaddingTop(); + innerPaddingLeft = getPaddingLeft(); + innerPaddingRight = getPaddingRight(); + innerPaddingBottom = getPaddingBottom(); + + extraPaddingTop = enableFloatingLabel ? floatingLabelTopSpacing + floatingLabelInsideSpacing + floatingLabelBottomSpacing : floatingLabelBottomSpacing; + updateBottomPadding(); + + } + + private void updateBottomPadding() { + Paint.FontMetrics textMetrics = textPaint.getFontMetrics(); + extraPaddingBottom = underlineTopSpacing + underlineBottomSpacing; + if (enableErrorLabel) { + extraPaddingBottom += (int) ((textMetrics.descent - textMetrics.ascent) * currentNbErrorLines); + } + updatePadding(); + } + + private void initDimensions() { + underlineTopSpacing = getResources().getDimensionPixelSize(R.dimen.underline_top_spacing); + underlineBottomSpacing = getResources().getDimensionPixelSize(R.dimen.underline_bottom_spacing); + floatingLabelTopSpacing = getResources().getDimensionPixelSize(R.dimen.floating_label_top_spacing); + floatingLabelBottomSpacing = getResources().getDimensionPixelSize(R.dimen.floating_label_bottom_spacing); + rightLeftSpinnerPadding = alignLabels ? getResources().getDimensionPixelSize(R.dimen.right_left_spinner_padding) : 0; + floatingLabelInsideSpacing = getResources().getDimensionPixelSize(R.dimen.floating_label_inside_spacing); + errorLabelSpacing = (int) getResources().getDimension(R.dimen.error_label_spacing); + minContentHeight = (int) getResources().getDimension(R.dimen.min_content_height); + } + + private void initOnItemSelectedListener() { + setOnItemSelectedListener(null); + } + + */ +/* + * ********************************************************************************** + * ANIMATION METHODS + * ********************************************************************************** + *//* + + + private void initFloatingLabelAnimator() { + if (floatingLabelAnimator == null) { + floatingLabelAnimator = ObjectAnimator.ofFloat(this, "floatingLabelPercent", 0f, 1f); + floatingLabelAnimator.addUpdateListener(this); + } + } + + private void showFloatingLabel() { + if (floatingLabelAnimator != null) { + floatingLabelVisible = true; + if (floatingLabelAnimator.isRunning()) { + floatingLabelAnimator.reverse(); + } else { + floatingLabelAnimator.start(); + } + } + } + + private void hideFloatingLabel() { + if (floatingLabelAnimator != null) { + floatingLabelVisible = false; + floatingLabelAnimator.reverse(); + } + } + + private void startErrorScrollingAnimator() { + + int textWidth = Math.round(textPaint.measureText(error.toString())); + if (errorLabelAnimator == null) { + errorLabelAnimator = ObjectAnimator.ofInt(this, "errorLabelPosX", 0, textWidth + getWidth() / 2); + errorLabelAnimator.setStartDelay(1000); + errorLabelAnimator.setInterpolator(new LinearInterpolator()); + errorLabelAnimator.setDuration(150 * error.length()); + errorLabelAnimator.addUpdateListener(this); + errorLabelAnimator.setRepeatCount(ValueAnimator.INFINITE); + } else { + errorLabelAnimator.setIntValues(0, textWidth + getWidth() / 2); + } + errorLabelAnimator.start(); + } + + + private void startErrorMultilineAnimator(float destLines) { + if (errorLabelAnimator == null) { + errorLabelAnimator = ObjectAnimator.ofFloat(this, "currentNbErrorLines", destLines); + + } else { + errorLabelAnimator.setFloatValues(destLines); + } + errorLabelAnimator.start(); + } + + + */ +/* + * ********************************************************************************** + * UTILITY METHODS + * ********************************************************************************** + *//* + + + private int dpToPx(float dp) { + final DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics(); + float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, displayMetrics); + return Math.round(px); + } + + private float pxToDp(float px) { + final DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics(); + return px * displayMetrics.density; + } + + private void updatePadding() { + int left = innerPaddingLeft; + int top = innerPaddingTop + extraPaddingTop; + int right = innerPaddingRight; + int bottom = innerPaddingBottom + extraPaddingBottom; + super.setPadding(left, top, right, bottom); + setMinimumHeight(top + bottom + minContentHeight); + } + + private boolean needScrollingAnimation() { + if (error != null) { + float screenWidth = getWidth() - rightLeftSpinnerPadding; + float errorTextWidth = textPaint.measureText(error.toString(), 0, error.length()); + return errorTextWidth > screenWidth ? true : false; + } + return false; + } + + private int prepareBottomPadding() { + + int targetNbLines = minNbErrorLines; + if (error != null) { + staticLayout = new StaticLayout(error, textPaint, getWidth() - getPaddingRight() - getPaddingLeft(), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true); + int nbErrorLines = staticLayout.getLineCount(); + targetNbLines = Math.max(minNbErrorLines, nbErrorLines); + } + return targetNbLines; + } + + private boolean isSpinnerEmpty() { + return (hintAdapter.getCount() == 0 && hint == null) || (hintAdapter.getCount() == 1 && hint != null); + } + + */ +/* + * ********************************************************************************** + * DRAWING METHODS + * ********************************************************************************** + *//* + + + + @Override + protected void onDraw(Canvas canvas) { + + super.onDraw(canvas); + int startX = 0; + int endX = getWidth(); + int lineHeight; + + int startYLine = getHeight() - getPaddingBottom() + underlineTopSpacing; + int startYFloatingLabel = (int) (getPaddingTop() - floatingLabelPercent * floatingLabelBottomSpacing); + + + if (error != null && enableErrorLabel) { + lineHeight = dpToPx(thicknessError); + int startYErrorLabel = startYLine + errorLabelSpacing + lineHeight; + paint.setColor(errorColor); + textPaint.setColor(errorColor); + //Error Label Drawing + if (multiline) { + canvas.save(); + canvas.translate(startX + rightLeftSpinnerPadding, startYErrorLabel - errorLabelSpacing); + staticLayout.draw(canvas); + canvas.restore(); + + } else { + //scrolling + canvas.drawText(error.toString(), startX + rightLeftSpinnerPadding - errorLabelPosX, startYErrorLabel, textPaint); + if (errorLabelPosX > 0) { + canvas.save(); + canvas.translate(textPaint.measureText(error.toString()) + getWidth() / 2, 0); + canvas.drawText(error.toString(), startX + rightLeftSpinnerPadding - errorLabelPosX, startYErrorLabel, textPaint); + canvas.restore(); + } + } + + } else { + lineHeight = dpToPx(thickness); + if (isSelected || hasFocus()) { + paint.setColor(highlightColor); + } else { + paint.setColor(isEnabled() ? baseColor : disabledColor); + } + } + + // Underline Drawing + canvas.drawRect(startX, startYLine, endX, startYLine + lineHeight, paint); + + //Floating Label Drawing + if ((hint != null || floatingLabelText != null) && enableFloatingLabel) { + if (isSelected || hasFocus()) { + textPaint.setColor(highlightColor); + } else { + textPaint.setColor(isEnabled() ? floatingLabelColor : disabledColor); + } + if (floatingLabelAnimator.isRunning() || !floatingLabelVisible) { + textPaint.setAlpha((int) ((0.8 * floatingLabelPercent + 0.2) * baseAlpha * floatingLabelPercent)); + } + String textToDraw = floatingLabelText != null ? floatingLabelText.toString() : hint.toString(); + if (isRtl) { + canvas.drawText(textToDraw, getWidth() - rightLeftSpinnerPadding - textPaint.measureText(textToDraw), startYFloatingLabel, textPaint); + } else { + canvas.drawText(textToDraw, startX + rightLeftSpinnerPadding, startYFloatingLabel, textPaint); + } + } + + drawSelector(canvas, getWidth() - rightLeftSpinnerPadding, getPaddingTop() + dpToPx(8)); + + + } + + private void drawSelector(Canvas canvas, int posX, int posY) { + if (isSelected || hasFocus()) { + paint.setColor(highlightColor); + } else { + paint.setColor(isEnabled() ? arrowColor : disabledColor); + } + + Point point1 = selectorPoints[0]; + Point point2 = selectorPoints[1]; + Point point3 = selectorPoints[2]; + + point1.set(posX, posY); + point2.set((int) (posX - (arrowSize)), posY); + point3.set((int) (posX - (arrowSize / 2)), (int) (posY + (arrowSize / 2))); + + selectorPath.reset(); + selectorPath.moveTo(point1.x, point1.y); + selectorPath.lineTo(point2.x, point2.y); + selectorPath.lineTo(point3.x, point3.y); + selectorPath.close(); + canvas.drawPath(selectorPath, paint); + } + + */ +/* + * ********************************************************************************** + * LISTENER METHODS + * ********************************************************************************** + *//* + + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (isEnabled()) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + isSelected = true; + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + isSelected = false; + break; + } + invalidate(); + } + return super.onTouchEvent(event); + } + + @Override + public void setOnItemSelectedListener(final OnItemSelectedListener listener) { + + final OnItemSelectedListener onItemSelectedListener = new OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + + if (hint != null || floatingLabelText != null) { + if (!floatingLabelVisible && position != 0) { + showFloatingLabel(); + } else if (floatingLabelVisible && position == 0) { + hideFloatingLabel(); + } + } + + if (position != lastPosition && error != null) { + setError(null); + } + lastPosition = position; + + if (listener != null) { + position = hint != null ? position - 1 : position; + listener.onItemSelected(parent, view, position, id); + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + if (listener != null) { + listener.onNothingSelected(parent); + } + } + }; + + super.setOnItemSelectedListener(onItemSelectedListener); + } + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + invalidate(); + } + + + */ +/* + * ********************************************************************************** + * GETTERS AND SETTERS + * ********************************************************************************** + *//* + + + public int getBaseColor() { + return baseColor; + } + + public void setBaseColor(int baseColor) { + this.baseColor = baseColor; + textPaint.setColor(baseColor); + baseAlpha = textPaint.getAlpha(); + invalidate(); + } + + public int getHighlightColor() { + return highlightColor; + } + + public void setHighlightColor(int highlightColor) { + this.highlightColor = highlightColor; + invalidate(); + } + + public int getHintColor() { + return hintColor; + } + + public void setHintColor(int hintColor) { + this.hintColor = hintColor; + invalidate(); + } + + public int getErrorColor() { + return errorColor; + } + + public void setErrorColor(int errorColor) { + this.errorColor = errorColor; + invalidate(); + } + + public void setHint(CharSequence hint) { + this.hint = hint; + invalidate(); + } + + public void setHint(int resid) { + CharSequence hint = getResources().getString(resid); + setHint(hint); + } + + public CharSequence getHint() { + return hint; + } + + public void setFloatingLabelText(CharSequence floatingLabelText) { + this.floatingLabelText = floatingLabelText; + invalidate(); + } + + public void setFloatingLabelText(int resid) { + String floatingLabelText = getResources().getString(resid); + setFloatingLabelText(floatingLabelText); + } + + public CharSequence getFloatingLabelText() { + return this.floatingLabelText; + } + + public void setError(CharSequence error) { + this.error = error; + if (errorLabelAnimator != null) { + errorLabelAnimator.end(); + } + + if (multiline) { + startErrorMultilineAnimator(prepareBottomPadding()); + } else if (needScrollingAnimation()) { + startErrorScrollingAnimator(); + } + requestLayout(); + } + + public void setError(int resid) { + CharSequence error = getResources().getString(resid); + setError(error); + } + + @Override + public void setEnabled(boolean enabled) { + if (!enabled) { + isSelected = false; + invalidate(); + } + super.setEnabled(enabled); + } + + public CharSequence getError() { + return this.error; + } + + public void setRtl() { + isRtl = true; + invalidate(); + } + + public boolean isRtl() { + return isRtl; + } + + */ +/** + * @deprecated {use @link #setPaddingSafe(int, int, int, int)} to keep internal computation OK + *//* + + @Deprecated + @Override + public void setPadding(int left, int top, int right, int bottom) { + super.setPadding(left, top, right, bottom); + } + + + public void setPaddingSafe(int left, int top, int right, int bottom) { + innerPaddingRight = right; + innerPaddingLeft = left; + innerPaddingTop = top; + innerPaddingBottom = bottom; + + updatePadding(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @Override + public void setAdapter(SpinnerAdapter adapter) { + hintAdapter = new HintAdapter(adapter, getContext()); + super.setAdapter(hintAdapter); + } + + @Override + public SpinnerAdapter getAdapter() { + return hintAdapter != null ? hintAdapter.getWrappedAdapter() : null; + } + + private float getFloatingLabelPercent() { + return floatingLabelPercent; + } + + private void setFloatingLabelPercent(float floatingLabelPercent) { + this.floatingLabelPercent = floatingLabelPercent; + } + + private int getErrorLabelPosX() { + return errorLabelPosX; + } + + private void setErrorLabelPosX(int errorLabelPosX) { + this.errorLabelPosX = errorLabelPosX; + } + + private float getCurrentNbErrorLines() { + return currentNbErrorLines; + } + + private void setCurrentNbErrorLines(float currentNbErrorLines) { + this.currentNbErrorLines = currentNbErrorLines; + updateBottomPadding(); + } + + @Override + public Object getItemAtPosition(int position) { + if (hint != null) { + position++; + } + return (hintAdapter == null || position < 0) ? null : hintAdapter.getItem(position); + } + + @Override + public long getItemIdAtPosition(int position) { + if (hint != null) { + position++; + } + return (hintAdapter == null || position < 0) ? INVALID_ROW_ID : hintAdapter.getItemId(position); + } + + */ +/* + * ********************************************************************************** + * INNER CLASS + * ********************************************************************************** + *//* + + + private class HintAdapter extends BaseAdapter { + + private static final int HINT_TYPE = -1; + + private SpinnerAdapter mSpinnerAdapter; + private Context mContext; + + public HintAdapter(SpinnerAdapter spinnerAdapter, Context context) { + mSpinnerAdapter = spinnerAdapter; + mContext = context; + } + + @Override + public int getViewTypeCount() { + //Workaround waiting for a Google correction (https://code.google.com/p/android/issues/detail?id=79011) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + return 1; + } + int viewTypeCount = mSpinnerAdapter.getViewTypeCount(); + return viewTypeCount; + } + + @Override + public int getItemViewType(int position) { + position = hint != null ? position - 1 : position; + return (position == -1) ? HINT_TYPE : mSpinnerAdapter.getItemViewType(position); + } + + @Override + public int getCount() { + int count = mSpinnerAdapter.getCount(); + return hint != null ? count + 1 : count; + } + + @Override + public Object getItem(int position) { + position = hint != null ? position - 1 : position; + return (position == -1) ? hint : mSpinnerAdapter.getItem(position); + } + + @Override + public long getItemId(int position) { + position = hint != null ? position - 1 : position; + return (position == -1) ? 0 : mSpinnerAdapter.getItemId(position); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + return buildView(position, convertView, parent, false); + } + + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + return buildView(position, convertView, parent, true); + } + + private View buildView(int position, View convertView, ViewGroup parent, boolean isDropDownView) { + if (getItemViewType(position) == HINT_TYPE) { + return getHintView(convertView, parent, isDropDownView); + } + //workaround to have multiple types in spinner + if (convertView != null) { + convertView = (convertView.getTag() != null && convertView.getTag() instanceof Integer && (Integer) convertView.getTag() != HINT_TYPE) ? convertView : null; + } + position = hint != null ? position - 1 : position; + return isDropDownView ? mSpinnerAdapter.getDropDownView(position, convertView, parent) : mSpinnerAdapter.getView(position, convertView, parent); + } + + private View getHintView(final View convertView, final ViewGroup parent, final boolean isDropDownView) { + + final LayoutInflater inflater = LayoutInflater.from(mContext); + final int resid = isDropDownView ? android.R.layout.simple_spinner_dropdown_item : android.R.layout.simple_spinner_item; + final TextView textView = (TextView) inflater.inflate(resid, parent, false); + textView.setText(hint); + textView.setTextColor(MaterialSpinner.this.isEnabled() ? hintColor : disabledColor); + textView.setTag(HINT_TYPE); + return textView; + } + + private SpinnerAdapter getWrappedAdapter() { + return mSpinnerAdapter; + } + } +}*/ diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/PonilaChoiceView.java b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/PonilaChoiceView.java new file mode 100755 index 0000000..646181a --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/PonilaChoiceView.java @@ -0,0 +1,152 @@ +package com.shaya.poinila.android.presentation.view.costom_view; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.shaya.poinila.android.presentation.R; + +import butterknife.Bind; +import butterknife.ButterKnife; + +/** + * Created by iran on 8/15/2016. + */ +public class PonilaChoiceView extends LinearLayout implements View.OnClickListener { + + + public static final int + FIRST_OPTION = 0, + SECOND_OPTION = 1, + THIRD_OPTION = 2; + + private int optionSelected = FIRST_OPTION; + + private OnOptionSelected onOptionSelected; + + @Bind(R.id.first_option) + TextView firstOption; + + @Bind(R.id.second_option) + TextView secondOption; + + @Bind(R.id.third_option) + TextView thirdOption; + + public PonilaChoiceView(Context context) { + super(context); + init(); + } + + public PonilaChoiceView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public PonilaChoiceView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + private void init(){ + inflate(getContext(), R.layout.view_ponila_choice, this); + + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + ButterKnife.bind(this); + + optionSelected = FIRST_OPTION; + firstOption.setSelected(true); + secondOption.setSelected(false); + thirdOption.setSelected(false); + + firstOption.setOnClickListener(this); + secondOption.setOnClickListener(this); + thirdOption.setOnClickListener(this); + } + + public void setOptionsText(String firstOptionText, String secondOptionText, String thirdOptionText){ + firstOption.setText(firstOptionText); + secondOption.setText(secondOptionText); + thirdOption.setText(thirdOptionText); + } + + public void setOptionsText(int firstOptionText, int secondOptionText, int thirdOptionText){ + firstOption.setText(firstOptionText); + secondOption.setText(secondOptionText); + thirdOption.setText(thirdOptionText); + } + + @Override + public void onClick(View view) { + switch (view.getId()){ + case R.id.first_option: + optionSelected = FIRST_OPTION; + firstOption.setSelected(true); + secondOption.setSelected(false); + thirdOption.setSelected(false); + if(onOptionSelected != null) + onOptionSelected.onFirstOption(); + break; + case R.id.second_option: + optionSelected = SECOND_OPTION; + firstOption.setSelected(false); + secondOption.setSelected(true); + thirdOption.setSelected(false); + if(onOptionSelected != null) + onOptionSelected.onSecondOption(); + break; + case R.id.third_option: + optionSelected = THIRD_OPTION; + firstOption.setSelected(false); + secondOption.setSelected(false); + thirdOption.setSelected(true); + if(onOptionSelected != null) + onOptionSelected.onThirdOption(); + break; + } + } + + public PonilaChoiceView setOnOptionSelected(OnOptionSelected onOptionSelected) { + this.onOptionSelected = onOptionSelected; + return this; + } + + public void setDefaultSelected(int option){ + switch (option){ + case FIRST_OPTION: + optionSelected = FIRST_OPTION; + firstOption.setSelected(true); + secondOption.setSelected(false); + thirdOption.setSelected(false); + break; + case SECOND_OPTION: + optionSelected = SECOND_OPTION; + firstOption.setSelected(false); + secondOption.setSelected(true); + thirdOption.setSelected(false); + break; + case THIRD_OPTION: + optionSelected = THIRD_OPTION; + firstOption.setSelected(false); + secondOption.setSelected(false); + thirdOption.setSelected(true); + break; + } + } + + public interface OnOptionSelected{ + void onFirstOption(); + void onSecondOption(); + void onThirdOption(); + } + + public int getOptionSelected(){ + return optionSelected; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/RemovableChipView.java b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/RemovableChipView.java new file mode 100755 index 0000000..bf2690d --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/RemovableChipView.java @@ -0,0 +1,268 @@ +/* +package com.shaya.poinila.android.presentation.view.costom_view; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; +import android.text.TextPaint; +import android.util.AttributeSet; +import android.view.View; + +import com.shaya.poinila.android.presentation.R; + +*/ +/** + * TODO: document your custom view class. + *

+ * Gets the example string attribute value. + * + * @return The example string attribute value. + *

+ * Sets the view's example string attribute value. In the example view, this string + * is the text to draw. + * @param exampleString The example string attribute value to use. + *

+ * Gets the example color attribute value. + * @return The example color attribute value. + *

+ * Sets the view's example color attribute value. In the example view, this color + * is the font color. + * @param exampleColor The example color attribute value to use. + *

+ * Gets the example dimension attribute value. + * @return The example dimension attribute value. + *

+ * Sets the view's example dimension attribute value. In the example view, this dimension + * is the font size. + * @param exampleDimension The example dimension attribute value to use. + *

+ * Gets the example drawable attribute value. + * @return The example drawable attribute value. + *

+ * Sets the view's example drawable attribute value. In the example view, this drawable is + * drawn above the text. + * @param exampleDrawable The example drawable attribute value to use. + *

+ * Gets the example string attribute value. + * @return The example string attribute value. + *

+ * Sets the view's example string attribute value. In the example view, this string + * is the text to draw. + * @param exampleString The example string attribute value to use. + *

+ * Gets the example color attribute value. + * @return The example color attribute value. + *

+ * Sets the view's example color attribute value. In the example view, this color + * is the font color. + * @param exampleColor The example color attribute value to use. + *

+ * Gets the example dimension attribute value. + * @return The example dimension attribute value. + *

+ * Sets the view's example dimension attribute value. In the example view, this dimension + * is the font size. + * @param exampleDimension The example dimension attribute value to use. + *

+ * Gets the example drawable attribute value. + * @return The example drawable attribute value. + *

+ * Sets the view's example drawable attribute value. In the example view, this drawable is + * drawn above the text. + * @param exampleDrawable The example drawable attribute value to use. + *//* + +public class RemovableChipView extends View { + private String mExampleString; // TODO: use a default from R.string... + private int mExampleColor = Color.RED; // TODO: use a default from R.color... + private float mExampleDimension = 0; // TODO: use a default from R.dimen... + private Drawable mExampleDrawable; + + private TextPaint mTextPaint; + private float mTextWidth; + private float mTextHeight; + + public RemovableChipView(Context context) { + super(context); + init(null, 0); + } + + public RemovableChipView(Context context, AttributeSet attrs) { + super(context, attrs); + init(attrs, 0); + } + + public RemovableChipView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(attrs, defStyle); + } + + private void init(AttributeSet attrs, int defStyle) { + // Load attributes + final TypedArray a = getContext().obtainStyledAttributes( + attrs, R.styleable.RemovableChipView, defStyle, 0); + + mExampleString = a.getString( + R.styleable.RemovableChipView_exampleString); + mExampleColor = a.getColor( + R.styleable.RemovableChipView_exampleColor, + mExampleColor); + // Use getDimensionPixelSize or getDimensionPixelOffset when dealing with + // values that should fall on pixel boundaries. + mExampleDimension = a.getDimension( + R.styleable.RemovableChipView_exampleDimension, + mExampleDimension); + + if (a.hasValue(R.styleable.RemovableChipView_exampleDrawable)) { + mExampleDrawable = a.getDrawable( + R.styleable.RemovableChipView_exampleDrawable); + mExampleDrawable.setCallback(this); + } + + a.recycle(); + + // Set up a default TextPaint object + mTextPaint = new TextPaint(); + mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG); + mTextPaint.setTextAlign(Paint.Align.LEFT); + + // Update TextPaint and text measurements from attributes + invalidateTextPaintAndMeasurements(); + } + + private void invalidateTextPaintAndMeasurements() { + mTextPaint.setTextSize(mExampleDimension); + mTextPaint.setColor(mExampleColor); + mTextWidth = mTextPaint.measureText(mExampleString); + + Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics(); + mTextHeight = fontMetrics.bottom; + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + // TODO: consider storing these as member variables to reduce + // allocations per draw cycle. + int paddingLeft = getPaddingLeft(); + int paddingTop = getPaddingTop(); + int paddingRight = getPaddingRight(); + int paddingBottom = getPaddingBottom(); + + int contentWidth = getWidth() - paddingLeft - paddingRight; + int contentHeight = getHeight() - paddingTop - paddingBottom; + + // Draw the text. + canvas.drawText(mExampleString, + paddingLeft + (contentWidth - mTextWidth) / 2, + paddingTop + (contentHeight + mTextHeight) / 2, + mTextPaint); + + // Draw the example drawable on top of the text. + if (mExampleDrawable != null) { + mExampleDrawable.setBounds(paddingLeft, paddingTop, + paddingLeft + contentWidth, paddingTop + contentHeight); + mExampleDrawable.draw(canvas); + } + } + + */ +/** + * Gets the example string attribute value. + * + * @return The example string attribute value. + *//* + + public String getExampleString() { + return mExampleString; + } + + */ +/** + * Sets the view's example string attribute value. In the example view, this string + * is the text to draw. + * + * @param exampleString The example string attribute value to use. + *//* + + public void setExampleString(String exampleString) { + mExampleString = exampleString; + invalidateTextPaintAndMeasurements(); + } + + */ +/** + * Gets the example color attribute value. + * + * @return The example color attribute value. + *//* + + public int getExampleColor() { + return mExampleColor; + } + + */ +/** + * Sets the view's example color attribute value. In the example view, this color + * is the font color. + * + * @param exampleColor The example color attribute value to use. + *//* + + public void setExampleColor(int exampleColor) { + mExampleColor = exampleColor; + invalidateTextPaintAndMeasurements(); + } + + */ +/** + * Gets the example dimension attribute value. + * + * @return The example dimension attribute value. + *//* + + public float getExampleDimension() { + return mExampleDimension; + } + + */ +/** + * Sets the view's example dimension attribute value. In the example view, this dimension + * is the font size. + * + * @param exampleDimension The example dimension attribute value to use. + *//* + + public void setExampleDimension(float exampleDimension) { + mExampleDimension = exampleDimension; + invalidateTextPaintAndMeasurements(); + } + + */ +/** + * Gets the example drawable attribute value. + * + * @return The example drawable attribute value. + *//* + + public Drawable getExampleDrawable() { + return mExampleDrawable; + } + + */ +/** + * Sets the view's example drawable attribute value. In the example view, this drawable is + * drawn above the text. + * + * @param exampleDrawable The example drawable attribute value to use. + *//* + + public void setExampleDrawable(Drawable exampleDrawable) { + mExampleDrawable = exampleDrawable; + } +} +*/ diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/ScrollViewListener.java b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/ScrollViewListener.java new file mode 100755 index 0000000..315c39c --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/ScrollViewListener.java @@ -0,0 +1,9 @@ +package com.shaya.poinila.android.presentation.view.costom_view; + +/** + * Created by iran on 2015-09-01. + */ +public interface ScrollViewListener { + void onScrollChanged(CustomScrollView scrollView, + int x, int y, int oldx, int oldy); +} \ No newline at end of file diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/SvgMaskedImageView.java b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/SvgMaskedImageView.java new file mode 100755 index 0000000..1a358ab --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/SvgMaskedImageView.java @@ -0,0 +1,214 @@ +package com.shaya.poinila.android.presentation.view.costom_view; +/* + * Copyright 2014 Mostafa Gazar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PorterDuff.Mode; +import android.graphics.PorterDuffXfermode; +import android.graphics.RectF; +import android.graphics.Xfermode; +import android.graphics.drawable.Drawable; +import android.support.annotation.NonNull; +import android.util.AttributeSet; +import android.util.Log; +import android.widget.ImageView; + +/*import com.larvalabs.svgandroid.SVG; +import com.larvalabs.svgandroid.SVGParser; +import com.ninja.sms.R; +import com.ninja.sms.utils.Log;*/ + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.svgandroid.SVG; +import com.shaya.poinila.android.presentation.svgandroid.SVGParser; + +import java.lang.ref.WeakReference; + +/** + * @author Mostafa Gazar + */ +public class SvgMaskedImageView extends ImageView { + + private static final String TAG = SvgMaskedImageView.class.getSimpleName(); + + public static final int DEFAULT_SVG_RAW_RES = R.raw.hexagon; + + private int mSvgRawRes = DEFAULT_SVG_RAW_RES; + + protected Context mContext; + + private static final Xfermode sXfermode = new PorterDuffXfermode(Mode.DST_IN); + private Bitmap mMaskBitmap; + private Paint mPaint; + private WeakReference mSrcWeakBitmap; + + private int mLastWidth; + private int mLastHeight; + + public SvgMaskedImageView(Context context) { + super(context); + + sharedConstructor(context, null); + } + + public SvgMaskedImageView(Context context, AttributeSet attrs) { + super(context, attrs); + + sharedConstructor(context, attrs); + } + + public SvgMaskedImageView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + + sharedConstructor(context, attrs); + } + + private void sharedConstructor(Context context, AttributeSet attrs) { + mContext = context; + + mPaint = new Paint(); + + if (attrs != null) { + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SvgMaskedImageView); + + mSvgRawRes = a != null ? a.getResourceId(R.styleable.SvgMaskedImageView_mask, DEFAULT_SVG_RAW_RES) : DEFAULT_SVG_RAW_RES; + a.recycle(); + } + } + + public static void drawBitmap(Canvas canvas, Bitmap bitmap, + Paint paint) { + drawBitmap(canvas, bitmap, paint, 0, 0); + } + + public static void drawBitmap(Canvas canvas, Bitmap bitmap, + Paint paint, int left, int top) { + paint.reset(); + paint.setFilterBitmap(false); + paint.setXfermode(sXfermode); + + canvas.drawBitmap(bitmap, left, top, paint); + } + + public void invalidate() { + mSrcWeakBitmap = null; + if (mMaskBitmap != null) { + mMaskBitmap.recycle(); + } + mLastWidth = 0; + mLastHeight = 0; + + super.invalidate(); + } + + @SuppressLint("DrawAllocation") + @Override + protected void onDraw(@NonNull Canvas canvas) { + if (!isInEditMode()) { + int width = getWidth(); + int height = getHeight(); + + int i = canvas.saveLayer(0.0F, 0.0F, width, height, null, Canvas.ALL_SAVE_FLAG); + try { + Bitmap srcBitmap = mSrcWeakBitmap != null ? mSrcWeakBitmap.get() : null; + if (srcBitmap == null || srcBitmap.isRecycled()) { + Drawable srcDrawable = getDrawable(); + if (srcDrawable != null) { + srcBitmap = Bitmap.createBitmap(getWidth(), + getHeight(), Bitmap.Config.ARGB_8888); + Canvas srcBitmapCanvas = new Canvas(srcBitmap); + srcDrawable.setBounds(0, 0, getWidth(), getHeight()); + srcDrawable.draw(srcBitmapCanvas); + + // Skip and use cached mask. + if (mMaskBitmap == null || mMaskBitmap.isRecycled() || + mLastWidth != width || mLastHeight != height) { + mMaskBitmap = getMask(width, height); + } + + drawBitmap(srcBitmapCanvas, mMaskBitmap, mPaint); + mSrcWeakBitmap = new WeakReference(srcBitmap); + } + } + + if (srcBitmap != null) { + mPaint.setXfermode(null); + canvas.drawBitmap(srcBitmap, 0.0F, 0.0F, mPaint); + } + } catch (Exception e) { + System.gc(); + + Log.e(TAG, String.format("Unable to draw, view Id :: %s. Error occurred :: %s", getId(), e.toString())); + } finally { + canvas.restoreToCount(i); + } + } else { + super.onDraw(canvas); + } + } + + private Bitmap getDefaultMask(int width, int height) { + Bitmap bitmap = Bitmap.createBitmap(width, height, + Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setColor(Color.BLACK); + canvas.drawRect(new RectF(0.0F, 0.0F, width, height), paint); + + return bitmap; + } + + private Bitmap getMask(int width, int height) { + SVG svgMask = null; + if (mLastWidth != width || mLastHeight != height) { + svgMask = SVGParser.getSVGFromInputStream( + mContext.getResources().openRawResource(mSvgRawRes), width, height); + + mLastWidth = width; + mLastHeight = height; + } + + if (svgMask != null) { + Bitmap bitmap = Bitmap.createBitmap(width, height, + Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setColor(Color.BLACK); + + canvas.drawPicture(svgMask.getPicture()); + + return bitmap; + } + + // In case everything failed, return square. + return getDefaultMask(width, height); + } + + public void updateMask(int svgRawRes) { + if (mSvgRawRes != svgRawRes) { + mSvgRawRes = svgRawRes; + + invalidate(); + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/TouchImageView.java b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/TouchImageView.java new file mode 100755 index 0000000..30bea9a --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/costom_view/TouchImageView.java @@ -0,0 +1,1265 @@ +package com.shaya.poinila.android.presentation.view.costom_view; + +/** + * Created by hossein on 8/30/16. + */ +import android.annotation.TargetApi; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.PointF; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Build; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; +import android.os.Bundle; +import android.os.Parcelable; +import android.util.AttributeSet; +import android.util.Log; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.ScaleGestureDetector; +import android.view.View; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.widget.ImageView; +import android.widget.OverScroller; +import android.widget.Scroller; + +public class TouchImageView extends ImageView { + + private static final String DEBUG = "DEBUG"; + + // + // SuperMin and SuperMax multipliers. Determine how much the image can be + // zoomed below or above the zoom boundaries, before animating back to the + // min/max zoom boundary. + // + private static final float SUPER_MIN_MULTIPLIER = .75f; + private static final float SUPER_MAX_MULTIPLIER = 1.25f; + + // + // Scale of image ranges from minScale to maxScale, where minScale == 1 + // when the image is stretched to fit view. + // + private float normalizedScale; + + // + // Matrix applied to image. MSCALE_X and MSCALE_Y should always be equal. + // MTRANS_X and MTRANS_Y are the other values used. prevMatrix is the matrix + // saved prior to the screen rotating. + // + private Matrix matrix, prevMatrix; + + private static enum State { NONE, DRAG, ZOOM, FLING, ANIMATE_ZOOM } + + private State state; + + private float minScale; + private float maxScale; + private float superMinScale; + private float superMaxScale; + private float[] m; + + private Context context; + private Fling fling; + + private ScaleType mScaleType; + + private boolean imageRenderedAtLeastOnce; + private boolean onDrawReady; + + private ZoomVariables delayedZoomVariables; + + // + // Size of view and previous view size (ie before rotation) + // + private int viewWidth, viewHeight, prevViewWidth, prevViewHeight; + + // + // Size of image when it is stretched to fit view. Before and After rotation. + // + private float matchViewWidth, matchViewHeight, prevMatchViewWidth, prevMatchViewHeight; + + private ScaleGestureDetector mScaleDetector; + private GestureDetector mGestureDetector; + private GestureDetector.OnDoubleTapListener doubleTapListener = null; + private OnTouchListener userTouchListener = null; + private OnTouchImageViewListener touchImageViewListener = null; + + public TouchImageView(Context context) { + super(context); + sharedConstructing(context); + } + + public TouchImageView(Context context, AttributeSet attrs) { + super(context, attrs); + sharedConstructing(context); + } + + public TouchImageView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + sharedConstructing(context); + } + + private void sharedConstructing(Context context) { + super.setClickable(true); + this.context = context; + mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); + mGestureDetector = new GestureDetector(context, new GestureListener()); + matrix = new Matrix(); + prevMatrix = new Matrix(); + m = new float[9]; + normalizedScale = 1; + if (mScaleType == null) { + mScaleType = ScaleType.FIT_CENTER; + } + minScale = 1; + maxScale = 3; + superMinScale = SUPER_MIN_MULTIPLIER * minScale; + superMaxScale = SUPER_MAX_MULTIPLIER * maxScale; + setImageMatrix(matrix); + setScaleType(ScaleType.MATRIX); + setState(State.NONE); + onDrawReady = false; + super.setOnTouchListener(new PrivateOnTouchListener()); + } + + @Override + public void setOnTouchListener(OnTouchListener l) { + userTouchListener = l; + } + + public void setOnTouchImageViewListener(OnTouchImageViewListener l) { + touchImageViewListener = l; + } + + public void setOnDoubleTapListener(GestureDetector.OnDoubleTapListener l) { + doubleTapListener = l; + } + + @Override + public void setImageResource(int resId) { + super.setImageResource(resId); + savePreviousImageValues(); + fitImageToView(); + } + + @Override + public void setImageBitmap(Bitmap bm) { + super.setImageBitmap(bm); + savePreviousImageValues(); + fitImageToView(); + } + + @Override + public void setImageDrawable(Drawable drawable) { + super.setImageDrawable(drawable); + savePreviousImageValues(); + fitImageToView(); + } + + @Override + public void setImageURI(Uri uri) { + super.setImageURI(uri); + savePreviousImageValues(); + fitImageToView(); + } + + @Override + public void setScaleType(ScaleType type) { + if (type == ScaleType.FIT_START || type == ScaleType.FIT_END) { + throw new UnsupportedOperationException("TouchImageView does not support FIT_START or FIT_END"); + } + if (type == ScaleType.MATRIX) { + super.setScaleType(ScaleType.MATRIX); + + } else { + mScaleType = type; + if (onDrawReady) { + // + // If the image is already rendered, scaleType has been called programmatically + // and the TouchImageView should be updated with the new scaleType. + // + setZoom(this); + } + } + } + + @Override + public ScaleType getScaleType() { + return mScaleType; + } + + /** + * Returns false if image is in initial, unzoomed state. False, otherwise. + * @return true if image is zoomed + */ + public boolean isZoomed() { + return normalizedScale != 1; + } + + /** + * Return a Rect representing the zoomed image. + * @return rect representing zoomed image + */ + public RectF getZoomedRect() { + if (mScaleType == ScaleType.FIT_XY) { + throw new UnsupportedOperationException("getZoomedRect() not supported with FIT_XY"); + } + PointF topLeft = transformCoordTouchToBitmap(0, 0, true); + PointF bottomRight = transformCoordTouchToBitmap(viewWidth, viewHeight, true); + + float w = getDrawable().getIntrinsicWidth(); + float h = getDrawable().getIntrinsicHeight(); + return new RectF(topLeft.x / w, topLeft.y / h, bottomRight.x / w, bottomRight.y / h); + } + + /** + * Save the current matrix and view dimensions + * in the prevMatrix and prevView variables. + */ + private void savePreviousImageValues() { + if (matrix != null && viewHeight != 0 && viewWidth != 0) { + matrix.getValues(m); + prevMatrix.setValues(m); + prevMatchViewHeight = matchViewHeight; + prevMatchViewWidth = matchViewWidth; + prevViewHeight = viewHeight; + prevViewWidth = viewWidth; + } + } + + @Override + public Parcelable onSaveInstanceState() { + Bundle bundle = new Bundle(); + bundle.putParcelable("instanceState", super.onSaveInstanceState()); + bundle.putFloat("saveScale", normalizedScale); + bundle.putFloat("matchViewHeight", matchViewHeight); + bundle.putFloat("matchViewWidth", matchViewWidth); + bundle.putInt("viewWidth", viewWidth); + bundle.putInt("viewHeight", viewHeight); + matrix.getValues(m); + bundle.putFloatArray("matrix", m); + bundle.putBoolean("imageRendered", imageRenderedAtLeastOnce); + return bundle; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + if (state instanceof Bundle) { + Bundle bundle = (Bundle) state; + normalizedScale = bundle.getFloat("saveScale"); + m = bundle.getFloatArray("matrix"); + prevMatrix.setValues(m); + prevMatchViewHeight = bundle.getFloat("matchViewHeight"); + prevMatchViewWidth = bundle.getFloat("matchViewWidth"); + prevViewHeight = bundle.getInt("viewHeight"); + prevViewWidth = bundle.getInt("viewWidth"); + imageRenderedAtLeastOnce = bundle.getBoolean("imageRendered"); + super.onRestoreInstanceState(bundle.getParcelable("instanceState")); + return; + } + + super.onRestoreInstanceState(state); + } + + @Override + protected void onDraw(Canvas canvas) { + onDrawReady = true; + imageRenderedAtLeastOnce = true; + if (delayedZoomVariables != null) { + setZoom(delayedZoomVariables.scale, delayedZoomVariables.focusX, delayedZoomVariables.focusY, delayedZoomVariables.scaleType); + delayedZoomVariables = null; + } + super.onDraw(canvas); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + savePreviousImageValues(); + } + + /** + * Get the max zoom multiplier. + * @return max zoom multiplier. + */ + public float getMaxZoom() { + return maxScale; + } + + /** + * Set the max zoom multiplier. Default value: 3. + * @param max max zoom multiplier. + */ + public void setMaxZoom(float max) { + maxScale = max; + superMaxScale = SUPER_MAX_MULTIPLIER * maxScale; + } + + /** + * Get the min zoom multiplier. + * @return min zoom multiplier. + */ + public float getMinZoom() { + return minScale; + } + + /** + * Get the current zoom. This is the zoom relative to the initial + * scale, not the original resource. + * @return current zoom multiplier. + */ + public float getCurrentZoom() { + return normalizedScale; + } + + /** + * Set the min zoom multiplier. Default value: 1. + * @param min min zoom multiplier. + */ + public void setMinZoom(float min) { + minScale = min; + superMinScale = SUPER_MIN_MULTIPLIER * minScale; + } + + /** + * Reset zoom and translation to initial state. + */ + public void resetZoom() { + normalizedScale = 1; + fitImageToView(); + } + + /** + * Set zoom to the specified scale. Image will be centered by default. + * @param scale + */ + public void setZoom(float scale) { + setZoom(scale, 0.5f, 0.5f); + } + + /** + * Set zoom to the specified scale. Image will be centered around the point + * (focusX, focusY). These floats range from 0 to 1 and denote the focus point + * as a fraction from the left and top of the view. For example, the top left + * corner of the image would be (0, 0). And the bottom right corner would be (1, 1). + * @param scale + * @param focusX + * @param focusY + */ + public void setZoom(float scale, float focusX, float focusY) { + setZoom(scale, focusX, focusY, mScaleType); + } + + /** + * Set zoom to the specified scale. Image will be centered around the point + * (focusX, focusY). These floats range from 0 to 1 and denote the focus point + * as a fraction from the left and top of the view. For example, the top left + * corner of the image would be (0, 0). And the bottom right corner would be (1, 1). + * @param scale + * @param focusX + * @param focusY + * @param scaleType + */ + public void setZoom(float scale, float focusX, float focusY, ScaleType scaleType) { + // + // setZoom can be called before the image is on the screen, but at this point, + // image and view sizes have not yet been calculated in onMeasure. Thus, we should + // delay calling setZoom until the view has been measured. + // + if (!onDrawReady) { + delayedZoomVariables = new ZoomVariables(scale, focusX, focusY, scaleType); + return; + } + + if (scaleType != mScaleType) { + setScaleType(scaleType); + } + resetZoom(); + scaleImage(scale, viewWidth / 2, viewHeight / 2, true); + matrix.getValues(m); + m[Matrix.MTRANS_X] = -((focusX * getImageWidth()) - (viewWidth * 0.5f)); + m[Matrix.MTRANS_Y] = -((focusY * getImageHeight()) - (viewHeight * 0.5f)); + matrix.setValues(m); + fixTrans(); + setImageMatrix(matrix); + } + + /** + * Set zoom parameters equal to another TouchImageView. Including scale, position, + * and ScaleType. + * @param TouchImageView + */ + public void setZoom(TouchImageView img) { + PointF center = img.getScrollPosition(); + setZoom(img.getCurrentZoom(), center.x, center.y, img.getScaleType()); + } + + /** + * Return the point at the center of the zoomed image. The PointF coordinates range + * in value between 0 and 1 and the focus point is denoted as a fraction from the left + * and top of the view. For example, the top left corner of the image would be (0, 0). + * And the bottom right corner would be (1, 1). + * @return PointF representing the scroll position of the zoomed image. + */ + public PointF getScrollPosition() { + Drawable drawable = getDrawable(); + if (drawable == null) { + return null; + } + int drawableWidth = drawable.getIntrinsicWidth(); + int drawableHeight = drawable.getIntrinsicHeight(); + + PointF point = transformCoordTouchToBitmap(viewWidth / 2, viewHeight / 2, true); + point.x /= drawableWidth; + point.y /= drawableHeight; + return point; + } + + /** + * Set the focus point of the zoomed image. The focus points are denoted as a fraction from the + * left and top of the view. The focus points can range in value between 0 and 1. + * @param focusX + * @param focusY + */ + public void setScrollPosition(float focusX, float focusY) { + setZoom(normalizedScale, focusX, focusY); + } + + /** + * Performs boundary checking and fixes the image matrix if it + * is out of bounds. + */ + private void fixTrans() { + matrix.getValues(m); + float transX = m[Matrix.MTRANS_X]; + float transY = m[Matrix.MTRANS_Y]; + + float fixTransX = getFixTrans(transX, viewWidth, getImageWidth()); + float fixTransY = getFixTrans(transY, viewHeight, getImageHeight()); + + if (fixTransX != 0 || fixTransY != 0) { + matrix.postTranslate(fixTransX, fixTransY); + } + } + + /** + * When transitioning from zooming from focus to zoom from center (or vice versa) + * the image can become unaligned within the view. This is apparent when zooming + * quickly. When the content size is less than the view size, the content will often + * be centered incorrectly within the view. fixScaleTrans first calls fixTrans() and + * then makes sure the image is centered correctly within the view. + */ + private void fixScaleTrans() { + fixTrans(); + matrix.getValues(m); + if (getImageWidth() < viewWidth) { + m[Matrix.MTRANS_X] = (viewWidth - getImageWidth()) / 2; + } + + if (getImageHeight() < viewHeight) { + m[Matrix.MTRANS_Y] = (viewHeight - getImageHeight()) / 2; + } + matrix.setValues(m); + } + + private float getFixTrans(float trans, float viewSize, float contentSize) { + float minTrans, maxTrans; + + if (contentSize <= viewSize) { + minTrans = 0; + maxTrans = viewSize - contentSize; + + } else { + minTrans = viewSize - contentSize; + maxTrans = 0; + } + + if (trans < minTrans) + return -trans + minTrans; + if (trans > maxTrans) + return -trans + maxTrans; + return 0; + } + + private float getFixDragTrans(float delta, float viewSize, float contentSize) { + if (contentSize <= viewSize) { + return 0; + } + return delta; + } + + private float getImageWidth() { + return matchViewWidth * normalizedScale; + } + + private float getImageHeight() { + return matchViewHeight * normalizedScale; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + Drawable drawable = getDrawable(); + if (drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0) { + setMeasuredDimension(0, 0); + return; + } + + int drawableWidth = drawable.getIntrinsicWidth(); + int drawableHeight = drawable.getIntrinsicHeight(); + int widthSize = MeasureSpec.getSize(widthMeasureSpec); + int widthMode = MeasureSpec.getMode(widthMeasureSpec); + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + int heightMode = MeasureSpec.getMode(heightMeasureSpec); + viewWidth = setViewSize(widthMode, widthSize, drawableWidth); + viewHeight = setViewSize(heightMode, heightSize, drawableHeight); + + // + // Set view dimensions + // + setMeasuredDimension(viewWidth, viewHeight); + + // + // Fit content within view + // + fitImageToView(); + } + + /** + * If the normalizedScale is equal to 1, then the image is made to fit the screen. Otherwise, + * it is made to fit the screen according to the dimensions of the previous image matrix. This + * allows the image to maintain its zoom after rotation. + */ + private void fitImageToView() { + Drawable drawable = getDrawable(); + if (drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0) { + return; + } + if (matrix == null || prevMatrix == null) { + return; + } + + int drawableWidth = drawable.getIntrinsicWidth(); + int drawableHeight = drawable.getIntrinsicHeight(); + + // + // Scale image for view + // + float scaleX = (float) viewWidth / drawableWidth; + float scaleY = (float) viewHeight / drawableHeight; + + switch (mScaleType) { + case CENTER: + scaleX = scaleY = 1; + break; + + case CENTER_CROP: + scaleX = scaleY = Math.max(scaleX, scaleY); + break; + + case CENTER_INSIDE: + scaleX = scaleY = Math.min(1, Math.min(scaleX, scaleY)); + + case FIT_CENTER: + scaleX = scaleY = Math.min(scaleX, scaleY); + break; + + case FIT_XY: + break; + + default: + // + // FIT_START and FIT_END not supported + // + throw new UnsupportedOperationException("TouchImageView does not support FIT_START or FIT_END"); + + } + + // + // Center the image + // + float redundantXSpace = viewWidth - (scaleX * drawableWidth); + float redundantYSpace = viewHeight - (scaleY * drawableHeight); + matchViewWidth = viewWidth - redundantXSpace; + matchViewHeight = viewHeight - redundantYSpace; + if (!isZoomed() && !imageRenderedAtLeastOnce) { + // + // Stretch and center image to fit view + // + matrix.setScale(scaleX, scaleY); + matrix.postTranslate(redundantXSpace / 2, redundantYSpace / 2); + normalizedScale = 1; + + } else { + // + // These values should never be 0 or we will set viewWidth and viewHeight + // to NaN in translateMatrixAfterRotate. To avoid this, call savePreviousImageValues + // to set them equal to the current values. + // + if (prevMatchViewWidth == 0 || prevMatchViewHeight == 0) { + savePreviousImageValues(); + } + + prevMatrix.getValues(m); + + // + // Rescale Matrix after rotation + // + m[Matrix.MSCALE_X] = matchViewWidth / drawableWidth * normalizedScale; + m[Matrix.MSCALE_Y] = matchViewHeight / drawableHeight * normalizedScale; + + // + // TransX and TransY from previous matrix + // + float transX = m[Matrix.MTRANS_X]; + float transY = m[Matrix.MTRANS_Y]; + + // + // Width + // + float prevActualWidth = prevMatchViewWidth * normalizedScale; + float actualWidth = getImageWidth(); + translateMatrixAfterRotate(Matrix.MTRANS_X, transX, prevActualWidth, actualWidth, prevViewWidth, viewWidth, drawableWidth); + + // + // Height + // + float prevActualHeight = prevMatchViewHeight * normalizedScale; + float actualHeight = getImageHeight(); + translateMatrixAfterRotate(Matrix.MTRANS_Y, transY, prevActualHeight, actualHeight, prevViewHeight, viewHeight, drawableHeight); + + // + // Set the matrix to the adjusted scale and translate values. + // + matrix.setValues(m); + } + fixTrans(); + setImageMatrix(matrix); + } + + /** + * Set view dimensions based on layout params + * + * @param mode + * @param size + * @param drawableWidth + * @return + */ + private int setViewSize(int mode, int size, int drawableWidth) { + int viewSize; + switch (mode) { + case MeasureSpec.EXACTLY: + viewSize = size; + break; + + case MeasureSpec.AT_MOST: + viewSize = Math.min(drawableWidth, size); + break; + + case MeasureSpec.UNSPECIFIED: + viewSize = drawableWidth; + break; + + default: + viewSize = size; + break; + } + return viewSize; + } + + /** + * After rotating, the matrix needs to be translated. This function finds the area of image + * which was previously centered and adjusts translations so that is again the center, post-rotation. + * + * @param axis Matrix.MTRANS_X or Matrix.MTRANS_Y + * @param trans the value of trans in that axis before the rotation + * @param prevImageSize the width/height of the image before the rotation + * @param imageSize width/height of the image after rotation + * @param prevViewSize width/height of view before rotation + * @param viewSize width/height of view after rotation + * @param drawableSize width/height of drawable + */ + private void translateMatrixAfterRotate(int axis, float trans, float prevImageSize, float imageSize, int prevViewSize, int viewSize, int drawableSize) { + if (imageSize < viewSize) { + // + // The width/height of image is less than the view's width/height. Center it. + // + m[axis] = (viewSize - (drawableSize * m[Matrix.MSCALE_X])) * 0.5f; + + } else if (trans > 0) { + // + // The image is larger than the view, but was not before rotation. Center it. + // + m[axis] = -((imageSize - viewSize) * 0.5f); + + } else { + // + // Find the area of the image which was previously centered in the view. Determine its distance + // from the left/top side of the view as a fraction of the entire image's width/height. Use that percentage + // to calculate the trans in the new view width/height. + // + float percentage = (Math.abs(trans) + (0.5f * prevViewSize)) / prevImageSize; + m[axis] = -((percentage * imageSize) - (viewSize * 0.5f)); + } + } + + private void setState(State state) { + this.state = state; + } + + public boolean canScrollHorizontallyFroyo(int direction) { + return canScrollHorizontally(direction); + } + + @Override + public boolean canScrollHorizontally(int direction) { + matrix.getValues(m); + float x = m[Matrix.MTRANS_X]; + + if (getImageWidth() < viewWidth) { + return false; + + } else if (x >= -1 && direction < 0) { + return false; + + } else if (Math.abs(x) + viewWidth + 1 >= getImageWidth() && direction > 0) { + return false; + } + + return true; + } + + /** + * Gesture Listener detects a single click or long click and passes that on + * to the view's listener. + * @author Ortiz + * + */ + private class GestureListener extends GestureDetector.SimpleOnGestureListener { + + @Override + public boolean onSingleTapConfirmed(MotionEvent e) + { + if(doubleTapListener != null) { + return doubleTapListener.onSingleTapConfirmed(e); + } + return performClick(); + } + + @Override + public void onLongPress(MotionEvent e) + { + performLongClick(); + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) + { + if (fling != null) { + // + // If a previous fling is still active, it should be cancelled so that two flings + // are not run simultaenously. + // + fling.cancelFling(); + } + fling = new Fling((int) velocityX, (int) velocityY); + compatPostOnAnimation(fling); + return super.onFling(e1, e2, velocityX, velocityY); + } + + @Override + public boolean onDoubleTap(MotionEvent e) { + boolean consumed = false; + if(doubleTapListener != null) { + consumed = doubleTapListener.onDoubleTap(e); + } + if (state == State.NONE) { + float targetZoom = (normalizedScale == minScale) ? maxScale : minScale; + DoubleTapZoom doubleTap = new DoubleTapZoom(targetZoom, e.getX(), e.getY(), false); + compatPostOnAnimation(doubleTap); + consumed = true; + } + return consumed; + } + + @Override + public boolean onDoubleTapEvent(MotionEvent e) { + return doubleTapListener != null && doubleTapListener.onDoubleTapEvent(e); + } + } + + public interface OnTouchImageViewListener { + public void onMove(); + } + + /** + * Responsible for all touch events. Handles the heavy lifting of drag and also sends + * touch events to Scale Detector and Gesture Detector. + * @author Ortiz + * + */ + private class PrivateOnTouchListener implements OnTouchListener { + + // + // Remember last point position for dragging + // + private PointF last = new PointF(); + + @Override + public boolean onTouch(View v, MotionEvent event) { + mScaleDetector.onTouchEvent(event); + mGestureDetector.onTouchEvent(event); + PointF curr = new PointF(event.getX(), event.getY()); + + if (state == State.NONE || state == State.DRAG || state == State.FLING) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + last.set(curr); + if (fling != null) + fling.cancelFling(); + setState(State.DRAG); + break; + + case MotionEvent.ACTION_MOVE: + if (state == State.DRAG) { + float deltaX = curr.x - last.x; + float deltaY = curr.y - last.y; + float fixTransX = getFixDragTrans(deltaX, viewWidth, getImageWidth()); + float fixTransY = getFixDragTrans(deltaY, viewHeight, getImageHeight()); + matrix.postTranslate(fixTransX, fixTransY); + fixTrans(); + last.set(curr.x, curr.y); + } + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + setState(State.NONE); + break; + } + } + + setImageMatrix(matrix); + + // + // User-defined OnTouchListener + // + if(userTouchListener != null) { + userTouchListener.onTouch(v, event); + } + + // + // OnTouchImageViewListener is set: TouchImageView dragged by user. + // + if (touchImageViewListener != null) { + touchImageViewListener.onMove(); + } + + // + // indicate event was handled + // + return true; + } + } + + /** + * ScaleListener detects user two finger scaling and scales image. + * @author Ortiz + * + */ + private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { + @Override + public boolean onScaleBegin(ScaleGestureDetector detector) { + setState(State.ZOOM); + return true; + } + + @Override + public boolean onScale(ScaleGestureDetector detector) { + scaleImage(detector.getScaleFactor(), detector.getFocusX(), detector.getFocusY(), true); + + // + // OnTouchImageViewListener is set: TouchImageView pinch zoomed by user. + // + if (touchImageViewListener != null) { + touchImageViewListener.onMove(); + } + return true; + } + + @Override + public void onScaleEnd(ScaleGestureDetector detector) { + super.onScaleEnd(detector); + setState(State.NONE); + boolean animateToZoomBoundary = false; + float targetZoom = normalizedScale; + if (normalizedScale > maxScale) { + targetZoom = maxScale; + animateToZoomBoundary = true; + + } else if (normalizedScale < minScale) { + targetZoom = minScale; + animateToZoomBoundary = true; + } + + if (animateToZoomBoundary) { + DoubleTapZoom doubleTap = new DoubleTapZoom(targetZoom, viewWidth / 2, viewHeight / 2, true); + compatPostOnAnimation(doubleTap); + } + } + } + + private void scaleImage(double deltaScale, float focusX, float focusY, boolean stretchImageToSuper) { + + float lowerScale, upperScale; + if (stretchImageToSuper) { + lowerScale = superMinScale; + upperScale = superMaxScale; + + } else { + lowerScale = minScale; + upperScale = maxScale; + } + + float origScale = normalizedScale; + normalizedScale *= deltaScale; + if (normalizedScale > upperScale) { + normalizedScale = upperScale; + deltaScale = upperScale / origScale; + } else if (normalizedScale < lowerScale) { + normalizedScale = lowerScale; + deltaScale = lowerScale / origScale; + } + + matrix.postScale((float) deltaScale, (float) deltaScale, focusX, focusY); + fixScaleTrans(); + } + + /** + * DoubleTapZoom calls a series of runnables which apply + * an animated zoom in/out graphic to the image. + * @author Ortiz + * + */ + private class DoubleTapZoom implements Runnable { + + private long startTime; + private static final float ZOOM_TIME = 500; + private float startZoom, targetZoom; + private float bitmapX, bitmapY; + private boolean stretchImageToSuper; + private AccelerateDecelerateInterpolator interpolator = new AccelerateDecelerateInterpolator(); + private PointF startTouch; + private PointF endTouch; + + DoubleTapZoom(float targetZoom, float focusX, float focusY, boolean stretchImageToSuper) { + setState(State.ANIMATE_ZOOM); + startTime = System.currentTimeMillis(); + this.startZoom = normalizedScale; + this.targetZoom = targetZoom; + this.stretchImageToSuper = stretchImageToSuper; + PointF bitmapPoint = transformCoordTouchToBitmap(focusX, focusY, false); + this.bitmapX = bitmapPoint.x; + this.bitmapY = bitmapPoint.y; + + // + // Used for translating image during scaling + // + startTouch = transformCoordBitmapToTouch(bitmapX, bitmapY); + endTouch = new PointF(viewWidth / 2, viewHeight / 2); + } + + @Override + public void run() { + float t = interpolate(); + double deltaScale = calculateDeltaScale(t); + scaleImage(deltaScale, bitmapX, bitmapY, stretchImageToSuper); + translateImageToCenterTouchPosition(t); + fixScaleTrans(); + setImageMatrix(matrix); + + // + // OnTouchImageViewListener is set: double tap runnable updates listener + // with every frame. + // + if (touchImageViewListener != null) { + touchImageViewListener.onMove(); + } + + if (t < 1f) { + // + // We haven't finished zooming + // + compatPostOnAnimation(this); + + } else { + // + // Finished zooming + // + setState(State.NONE); + } + } + + /** + * Interpolate between where the image should start and end in order to translate + * the image so that the point that is touched is what ends up centered at the end + * of the zoom. + * @param t + */ + private void translateImageToCenterTouchPosition(float t) { + float targetX = startTouch.x + t * (endTouch.x - startTouch.x); + float targetY = startTouch.y + t * (endTouch.y - startTouch.y); + PointF curr = transformCoordBitmapToTouch(bitmapX, bitmapY); + matrix.postTranslate(targetX - curr.x, targetY - curr.y); + } + + /** + * Use interpolator to get t + * @return + */ + private float interpolate() { + long currTime = System.currentTimeMillis(); + float elapsed = (currTime - startTime) / ZOOM_TIME; + elapsed = Math.min(1f, elapsed); + return interpolator.getInterpolation(elapsed); + } + + /** + * Interpolate the current targeted zoom and get the delta + * from the current zoom. + * @param t + * @return + */ + private double calculateDeltaScale(float t) { + double zoom = startZoom + t * (targetZoom - startZoom); + return zoom / normalizedScale; + } + } + + /** + * This function will transform the coordinates in the touch event to the coordinate + * system of the drawable that the imageview contain + * @param x x-coordinate of touch event + * @param y y-coordinate of touch event + * @param clipToBitmap Touch event may occur within view, but outside image content. True, to clip return value + * to the bounds of the bitmap size. + * @return Coordinates of the point touched, in the coordinate system of the original drawable. + */ + private PointF transformCoordTouchToBitmap(float x, float y, boolean clipToBitmap) { + matrix.getValues(m); + float origW = getDrawable().getIntrinsicWidth(); + float origH = getDrawable().getIntrinsicHeight(); + float transX = m[Matrix.MTRANS_X]; + float transY = m[Matrix.MTRANS_Y]; + float finalX = ((x - transX) * origW) / getImageWidth(); + float finalY = ((y - transY) * origH) / getImageHeight(); + + if (clipToBitmap) { + finalX = Math.min(Math.max(finalX, 0), origW); + finalY = Math.min(Math.max(finalY, 0), origH); + } + + return new PointF(finalX , finalY); + } + + /** + * Inverse of transformCoordTouchToBitmap. This function will transform the coordinates in the + * drawable's coordinate system to the view's coordinate system. + * @param bx x-coordinate in original bitmap coordinate system + * @param by y-coordinate in original bitmap coordinate system + * @return Coordinates of the point in the view's coordinate system. + */ + private PointF transformCoordBitmapToTouch(float bx, float by) { + matrix.getValues(m); + float origW = getDrawable().getIntrinsicWidth(); + float origH = getDrawable().getIntrinsicHeight(); + float px = bx / origW; + float py = by / origH; + float finalX = m[Matrix.MTRANS_X] + getImageWidth() * px; + float finalY = m[Matrix.MTRANS_Y] + getImageHeight() * py; + return new PointF(finalX , finalY); + } + + /** + * Fling launches sequential runnables which apply + * the fling graphic to the image. The values for the translation + * are interpolated by the Scroller. + * @author Ortiz + * + */ + private class Fling implements Runnable { + + CompatScroller scroller; + int currX, currY; + + Fling(int velocityX, int velocityY) { + setState(State.FLING); + scroller = new CompatScroller(context); + matrix.getValues(m); + + int startX = (int) m[Matrix.MTRANS_X]; + int startY = (int) m[Matrix.MTRANS_Y]; + int minX, maxX, minY, maxY; + + if (getImageWidth() > viewWidth) { + minX = viewWidth - (int) getImageWidth(); + maxX = 0; + + } else { + minX = maxX = startX; + } + + if (getImageHeight() > viewHeight) { + minY = viewHeight - (int) getImageHeight(); + maxY = 0; + + } else { + minY = maxY = startY; + } + + scroller.fling(startX, startY, (int) velocityX, (int) velocityY, minX, + maxX, minY, maxY); + currX = startX; + currY = startY; + } + + public void cancelFling() { + if (scroller != null) { + setState(State.NONE); + scroller.forceFinished(true); + } + } + + @Override + public void run() { + + // + // OnTouchImageViewListener is set: TouchImageView listener has been flung by user. + // Listener runnable updated with each frame of fling animation. + // + if (touchImageViewListener != null) { + touchImageViewListener.onMove(); + } + + if (scroller.isFinished()) { + scroller = null; + return; + } + + if (scroller.computeScrollOffset()) { + int newX = scroller.getCurrX(); + int newY = scroller.getCurrY(); + int transX = newX - currX; + int transY = newY - currY; + currX = newX; + currY = newY; + matrix.postTranslate(transX, transY); + fixTrans(); + setImageMatrix(matrix); + compatPostOnAnimation(this); + } + } + } + + @TargetApi(VERSION_CODES.GINGERBREAD) + private class CompatScroller { + Scroller scroller; + OverScroller overScroller; + boolean isPreGingerbread; + + public CompatScroller(Context context) { + if (VERSION.SDK_INT < VERSION_CODES.GINGERBREAD) { + isPreGingerbread = true; + scroller = new Scroller(context); + + } else { + isPreGingerbread = false; + overScroller = new OverScroller(context); + } + } + + public void fling(int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY) { + if (isPreGingerbread) { + scroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY); + } else { + overScroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY); + } + } + + public void forceFinished(boolean finished) { + if (isPreGingerbread) { + scroller.forceFinished(finished); + } else { + overScroller.forceFinished(finished); + } + } + + public boolean isFinished() { + if (isPreGingerbread) { + return scroller.isFinished(); + } else { + return overScroller.isFinished(); + } + } + + public boolean computeScrollOffset() { + if (isPreGingerbread) { + return scroller.computeScrollOffset(); + } else { + overScroller.computeScrollOffset(); + return overScroller.computeScrollOffset(); + } + } + + public int getCurrX() { + if (isPreGingerbread) { + return scroller.getCurrX(); + } else { + return overScroller.getCurrX(); + } + } + + public int getCurrY() { + if (isPreGingerbread) { + return scroller.getCurrY(); + } else { + return overScroller.getCurrY(); + } + } + } + + @TargetApi(VERSION_CODES.JELLY_BEAN) + private void compatPostOnAnimation(Runnable runnable) { + if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) { + postOnAnimation(runnable); + + } else { + postDelayed(runnable, 1000/60); + } + } + + private class ZoomVariables { + public float scale; + public float focusX; + public float focusY; + public ScaleType scaleType; + + public ZoomVariables(float scale, float focusX, float focusY, ScaleType scaleType) { + this.scale = scale; + this.focusX = focusX; + this.focusY = focusY; + this.scaleType = scaleType; + } + } + + private void printMatrixInfo() { + float[] n = new float[9]; + matrix.getValues(n); + Log.d(DEBUG, "Scale: " + n[Matrix.MSCALE_X] + " TransX: " + n[Matrix.MTRANS_X] + " TransY: " + n[Matrix.MTRANS_Y]); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/AboutPoinilaDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/AboutPoinilaDialog.java new file mode 100755 index 0000000..20adee7 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/AboutPoinilaDialog.java @@ -0,0 +1,51 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v7.app.AlertDialog; +import android.text.Html; +import android.text.method.LinkMovementMethod; +import android.widget.TextView; + +import com.shaya.poinila.android.presentation.BuildConfig; +import com.shaya.poinila.android.presentation.R; + +/** + * Created by iran on 2015-11-04. + */ +public class AboutPoinilaDialog extends DialogFragment { + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + + PackageInfo packageInfo = null; + try { + packageInfo = getActivity().getPackageManager().getPackageInfo(getActivity().getPackageName(), 0); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + + builder.setPositiveButton(R.string.ok,null) + .setTitle(R.string.about_poinila) + .setMessage(Html.fromHtml(getString(R.string.about_version_contact, BuildConfig.VERSION_NAME))) + .setCancelable(false); + //.setIcon(R.drawable.logo_full); + + Dialog d = builder.create(); + d.setCancelable(true); + d.setCanceledOnTouchOutside(false); + return d; + } + + @Override + public void onStart() { + super.onStart(); + ((TextView) getDialog().findViewById(android.R.id.message)) + .setMovementMethod(LinkMovementMethod.getInstance()); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/BaseDialogFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/BaseDialogFragment.java new file mode 100755 index 0000000..50a6d6b --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/BaseDialogFragment.java @@ -0,0 +1,261 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.Context; +import android.os.Bundle; +import android.support.annotation.LayoutRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.annotation.StringRes; +import android.support.v4.widget.Space; +import android.support.v7.app.AlertDialog; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.util.ResourceUtils; + +import butterknife.Bind; +import butterknife.ButterKnife; +import butterknife.OnClick; + +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static com.shaya.poinila.android.util.ConstantsUtils.NO_RESOURCE; + +/** + * Created by iran on 11/25/2015. + */ +public abstract class BaseDialogFragment extends android.support.v4.app.DialogFragment{ + @Bind(R.id.dialog_title) + TextView titleView; + @Bind(R.id.dialog_positive_button) + Button positiveBtn; + @Bind(R.id.dialog_negative_button) Button negativeBtn; + @Bind(R.id.dialog_neutral_button) Button neutralBtn; + @Bind(R.id.dialog_message) TextView messageView; + LinearLayout container; + @Bind(R.id.divider) View divider; + @Bind(R.id.buttons_space_between1) + Space spaceView1; + @Bind(R.id.buttons_space_between2) Space spaceView2; + + protected static final int LAYOUT_NONE = -1; + protected static final int RESOURCE_NONE = -1; + protected ViewGroup rootView; + + public abstract @LayoutRes int getLayoutResId(); + + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + //setStyle(DialogFragment.STYLE_NO_TITLE, R.style.AppTheme_Dialog);//android.R.style.Theme_Holo_Light_Dialog_NoActionBar_MinWidth);//R.style.AppTheme_Dialog); + + // restoring state - reading parameters + Bundle state = null; + if (savedInstanceState != null) + state = savedInstanceState; + else if (getArguments() != null) + state = getArguments(); + if (state != null) + loadStateFromBundle(state); + + + /* Preventing loss of data on configuration change(like rotation). + * May be saving data on a fragment object and retrieving by fragmentManager is a + * better idea. + * + FragmentManager fm = getFragmentManager(); + dataFragment = (DataFragment) fm.findFragmentByTag(“data”); + + // create the fragment and data the first time + if (dataFragment == null) { + // add the fragment + dataFragment = new DataFragment(); + fm.beginTransaction().add(dataFragment, “data”).commit(); + // load the data from the web + dataFragment.setData(loadMyData()); + } + + //setRetainInstance(true); */ + } +/* + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + // restoring state - reading parameters + Bundle state = null; + if (savedInstanceState != null) + state = savedInstanceState; + else if (getArguments() != null) + state = getArguments(); + if (state != null) + loadStateFromBundle(state); + }*/ + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + saveStateToBundle(outState); + } + + protected abstract void loadStateFromBundle(Bundle savedInstanceState); + + /** + * Saves the state of object in the passed bundle + * @param outState Bundle in which dialog state would be saved + */ + protected abstract void saveStateToBundle(Bundle outState); + +/* @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + rootView = (ViewGroup) inflater.inflate(R.layout.dialog_general, container, false); + + if (getLayoutResId() != LAYOUT_NONE) { + View dialogView = inflater.inflate(getLayoutResId(), rootView, false); + + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0); + lp.weight = 1; + + rootView.addView(dialogView, 2, lp); + } + + ButterKnife.bind(this, rootView); + + //setDialogProperties(); + + initDialogGeneralAttributes(); + + initUI(getActivity()); + + return rootView; + }*/ + + + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + //Dialog dialog = super.onCreateDialog(savedInstanceState); + LayoutInflater inflater = getActivity().getLayoutInflater(); + rootView = (ViewGroup) inflater.inflate(R.layout.dialog_general, container, false); + if (getLayoutResId() != LAYOUT_NONE) { + View dialogView = inflater.inflate(getLayoutResId(), rootView, false); + + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(MATCH_PARENT, 0); + lp.weight = 1; + + rootView.addView(dialogView, 2, lp); + } + ButterKnife.bind(this, rootView); + initUI(getActivity()); + initDialogGeneralAttributes(); + + Dialog dialog= new AlertDialog.Builder(getActivity()).setView(rootView).create(); + + setDialogProperties(dialog); + + return dialog; + } + + private void setDialogProperties(Dialog dialog) { + dialog.setCanceledOnTouchOutside(false); + dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); + //dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + } + + private void initDialogGeneralAttributes() { + GeneralDialogData data = getDialogGeneralAttributes(); + if (data.title != null) + titleView.setText(data.title); + else { + titleView.setVisibility(View.GONE); + divider.setVisibility(View.GONE); + } + + if (data.message != null) + messageView.setText(data.message); + else + messageView.setVisibility(View.GONE); + + /*---Buttons----*/ + + // pos + if (data.positiveButtonText != null) { + positiveBtn.setText(data.positiveButtonText); + } else{ + positiveBtn.setVisibility(View.GONE); + } + + // neutral + if (data.neutralButtonText != null) { + neutralBtn.setVisibility(View.VISIBLE); + neutralBtn.setText(data.neutralButtonText); + spaceView1.setVisibility(View.VISIBLE); + } + + // neg + if (data.negativeButtonText!= null) + negativeBtn.setText(data.negativeButtonText); + else{ + spaceView2.setVisibility(View.GONE); + negativeBtn.setVisibility(View.GONE); + } + } + + protected abstract GeneralDialogData getDialogGeneralAttributes(); + + protected abstract void initUI(Context context); + + @OnClick(R.id.dialog_positive_button) public void onPositiveButton(){ + dismiss(); + } + + @OnClick(R.id.dialog_neutral_button) public void onNeutralButton(){ + dismiss(); + } + + @OnClick(R.id.dialog_negative_button) public void onNegativeButton(){ + dismiss(); + } + + + @Override + public void onDestroy() { + super.onDestroy(); + ButterKnife.unbind(this); + } + + public static class GeneralDialogData { + public final String title; + public final String message; + public final String positiveButtonText; + public final String negativeButtonText; + public final String neutralButtonText; + + GeneralDialogData(String title, String message, String positiveButtonText, String negativeButtonText, String neutralButtonText){ + this.title = title; + this.message = message; + this.positiveButtonText = positiveButtonText; + this.negativeButtonText = negativeButtonText; + this.neutralButtonText = neutralButtonText; + } + + GeneralDialogData(@StringRes int titleRes, @StringRes int messageRes, @StringRes int positiveButtonTextRes + , @StringRes int negativeButtonTextRes, @StringRes int neutralButtonTextRes){ + this.title = titleRes != NO_RESOURCE ? ResourceUtils.getString(titleRes) : null; + this.message = messageRes != NO_RESOURCE ? ResourceUtils.getString(messageRes) : null; + this.positiveButtonText = positiveButtonTextRes != NO_RESOURCE ? ResourceUtils.getString(positiveButtonTextRes) : null; + this.negativeButtonText = negativeButtonTextRes != NO_RESOURCE ? ResourceUtils.getString(negativeButtonTextRes) : null; + this.neutralButtonText = neutralButtonTextRes != NO_RESOURCE ? ResourceUtils.getString(neutralButtonTextRes) : null; + } + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/BusDialogFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/BusDialogFragment.java new file mode 100755 index 0000000..c49cc53 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/BusDialogFragment.java @@ -0,0 +1,174 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.app.Dialog; +import android.app.ProgressDialog; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.support.annotation.CallSuper; +import android.support.annotation.LayoutRes; +import android.support.annotation.NonNull; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ProgressBar; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.ConstantsUtils; + +import butterknife.ButterKnife; +import data.event.BaseEvent; + +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; + +/** + * Created by iran on 2015-09-26. + */ +public abstract class BusDialogFragment extends BaseDialogFragment{ + protected boolean initDataResponseReceived = false; + protected ViewGroup progressView; + private View loadableView; + private ViewGroup loadableViewParent; + private ViewGroup.LayoutParams loadableViewLayoutParams; + private int loadableViewIndex; + + private ProgressDialog mProgressDialog; + + /*----Same Code as BusFragment---------*/ + + @Override + public void onStart() { + super.onStart(); + BusProvider.getBus().register(this); + if (sendsRequestAutomatically() && !initDataResponseReceived) + initData(); + } + + protected abstract boolean sendsRequestAutomatically(); + + @Override + public void onStop() { + super.onStop(); + BusProvider.getBus().unregister(this); + } + + @CallSuper + protected void onGettingInitDataResponse(BaseEvent event) { + if (isInitDataResponseValid(event)) + onSuccessfulInitData(event); + } + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + + mProgressDialog = new ProgressDialog(getActivity(), 0); + mProgressDialog.setMessage(getString(R.string.progress_dialog_message)); + + return super.onCreateDialog(savedInstanceState); + } + + + + /** + * called only on creating view. (with regard to {@link #initDataResponseReceived} + * if somethings need to be updated, update with events and bus. + */ + public void initData() { + initDataResponseReceived = false; + if (mustShowProgressView()) + showProgress(); + //requestTracker.addInitRequestID(RandomUtils.getRandomInt()); + requestInitialData(); + } + + protected abstract void requestInitialData(); + + public abstract ViewGroup getLoadableView(); + + protected void showProgress() { + progressView = (ViewGroup) LayoutInflater.from(getActivity()).inflate(getProgressViewLayoutID(), rootView, false); + initProgressBar(getProgressBar()); + /*int progressBarSize = getResources().getDimensionPixelSize(R.dimen.icon_big); + FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(progressBarSize, progressBarSize); + lp.gravity = Gravity.CENTER; + progressView.addView(getProgressBar(), lp);*/ + + loadableView = getLoadableView(); + loadableViewLayoutParams = loadableView.getLayoutParams(); + + loadableViewParent = (ViewGroup) loadableView.getParent(); + loadableViewIndex = loadableViewParent.indexOfChild(loadableView); + + //TODO: null pointer sometimes. but why? + if (loadableViewParent != null) { + loadableViewParent.removeView(loadableView); + loadableViewParent.addView(progressView, loadableViewIndex, loadableViewLayoutParams); + } + + if (getProgressBar().isIndeterminate()) { + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { + @Override + public void run() { + if (rootView.findViewById(R.id.progress_view) != null) + dismissProgress(false); + } + }, ConstantsUtils.CONNECT_TIME_OUT_MILLISECONDS); + } + } + + @LayoutRes + protected int getProgressViewLayoutID() { + return R.layout.progress; + } + + protected void initProgressBar(ProgressBar progressBar) { + + } + + public void showProgressDialog(){ + if(mProgressDialog != null) mProgressDialog.show(); + } + + public void dismissProgressDialog(){ + mProgressDialog.dismiss(); + } + + public ProgressBar getProgressBar() { + /*ProgressBar progressBar = ButterKnife.findById(progressView, R.id.progress_bar); + if (progressBar == null){ + progressBar = new ProgressBar(getActivity(), null, android.R.attr.progressBarStyleLarge); + } + return progressBar;*/ + return ButterKnife.findById(progressView, R.id.progress_bar); + } + + // TODO: sometimes getting null loadableViewParent. But what times exactly? + protected void dismissProgress(boolean dataLoadSuccessful) { + if (loadableViewParent == null) return; + + loadableViewParent.removeView(progressView); + if (dataLoadSuccessful) { + //ViewUtils.enableLayoutChildes(getLoadableView(), true); + loadableViewParent.addView(loadableView, loadableViewIndex, loadableViewLayoutParams); + } + } + + @CallSuper + public void onSuccessfulInitData(BaseEvent baseEvent) { + if (mustShowProgressView() && !initDataResponseReceived) + dismissProgress(true); + initDataResponseReceived = true; + } + + public abstract boolean mustShowProgressView(); + + protected boolean isInitDataResponseValid(BaseEvent baseEvent) { + // in many cases of list pages, requestForInitData just calls the requestForLoadMore so we call this + // function on getting response data of list. In order to avoid view hierarchy traversing on every + // list data response, we check the requestOnFirstTime flag to be true. + return true; + } + /*---temporary validity check functions----*/ + //TODO: later, these methods must +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ChangeCircleNameDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ChangeCircleNameDialog.java new file mode 100755 index 0000000..0f155ea --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ChangeCircleNameDialog.java @@ -0,0 +1,77 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.content.Context; +import android.os.Bundle; +import android.widget.EditText; + +import com.mobsandgeeks.saripaar.annotation.Length; +import com.mobsandgeeks.saripaar.annotation.NotEmpty; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.activity.SettingActivity; +import com.shaya.poinila.android.util.ConstantsUtils; + +import butterknife.Bind; + +import static com.shaya.poinila.android.presentation.view.activity.SettingActivity.SettingType.CIRCLE_NAME; +import static com.shaya.poinila.android.util.ConstantsUtils.NO_RESOURCE; + +/** + * Created by iran on 2015-09-23. + */ +public class ChangeCircleNameDialog extends SingleTextFieldDialog { + private static final String KEY_CIRCLE_NAME = "circle name"; + @Length(max = ConstantsUtils.max_length_circle_name, min = ConstantsUtils.min_length_circle_name, messageResId = R.string.error_circle_name_length) + @Bind(R.id.input_field) public EditText inputField; + private String circleName; + + public static ChangeCircleNameDialog newInstance(String oldCircleName) { + Bundle args = new Bundle(); + ChangeCircleNameDialog fragment = new ChangeCircleNameDialog(); + fragment.circleName = oldCircleName; + fragment.saveStateToBundle(args); + fragment.setArguments(args); + return fragment; + } + + @Override + protected void saveStateToBundle(Bundle outState) { + super.saveStateToBundle(outState); + outState.putString(KEY_CIRCLE_NAME, circleName); + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData(R.string.edit_circle_name, NO_RESOURCE, R.string.submit, R.string.cancel, NO_RESOURCE); + } + + @Override + protected void loadStateFromBundle(Bundle savedInstanceState) { + super.loadStateFromBundle(savedInstanceState); + circleName = savedInstanceState.getString(KEY_CIRCLE_NAME, ""); + } + + @Override + protected void initUI(Context context) { + super.initUI(context); + inputField.setText(circleName); + } + + @Override + protected SettingActivity.SettingType getSettingType() { + return CIRCLE_NAME; + } + + @Override + protected void setTextFieldInputMethod() { + inputLayout.setCounterEnabled(true); + inputLayout.setCounterMaxLength(ConstantsUtils.max_length_circle_name); + inputLayout.setHint(getString(R.string.circle_name)); + } + + @Override + protected int getItemPosition() { + return adapterPosition; + } + + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ChangeEmailDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ChangeEmailDialog.java new file mode 100755 index 0000000..3ead5e8 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ChangeEmailDialog.java @@ -0,0 +1,79 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.content.Context; +import android.os.Bundle; +import android.text.InputType; +import android.widget.EditText; + +import com.mobsandgeeks.saripaar.annotation.Email; +import com.mobsandgeeks.saripaar.annotation.Length; +import com.mobsandgeeks.saripaar.annotation.NotEmpty; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.activity.SettingActivity; +import com.shaya.poinila.android.util.ConstantsUtils; + +import butterknife.Bind; + +import static com.shaya.poinila.android.util.ConstantsUtils.NO_RESOURCE; + +/** + * Created by iran on 2015-07-21. + */ +public class ChangeEmailDialog extends SingleTextFieldDialog { + + private static final String KEY_EMAIL = "email"; + @NotEmpty(trim = true, messageResId = R.string.error_required_field) + @Length(max = ConstantsUtils.max_length_email, messageResId = R.string.error_max_50) + @Email(messageResId = R.string.error_mail) + @Bind(R.id.input_field) public EditText inputField; + private String email; + + public static ChangeEmailDialog newInstance(String oldEmail) { + Bundle args = new Bundle(); + ChangeEmailDialog fragment = new ChangeEmailDialog(); + fragment.email = oldEmail; + fragment.saveStateToBundle(args); + fragment.setArguments(args); + return fragment; + } + + @Override + protected void initUI(Context context) { + super.initUI(context); + inputField.setText(email); + } + + @Override + protected void loadStateFromBundle(Bundle savedInstanceState) { + super.loadStateFromBundle(savedInstanceState); + email = savedInstanceState.getString(KEY_EMAIL, ""); + } + + @Override + protected void saveStateToBundle(Bundle outState) { + super.saveStateToBundle(outState); + outState.putString(KEY_EMAIL, email); + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData(R.string.change_email, NO_RESOURCE, R.string.submit, R.string.cancel, NO_RESOURCE); + } + + @Override + protected SettingActivity.SettingType getSettingType() { + return SettingActivity.SettingType.EMAIL; + } + + @Override + protected void setTextFieldInputMethod() { + inputField.setInputType(InputType.TYPE_CLASS_TEXT | + InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS); + inputField.setHint(R.string.hint_email); + } + + @Override + protected int getItemPosition() { + return -1; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ChangeFrameNameDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ChangeFrameNameDialog.java new file mode 100755 index 0000000..a308c58 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ChangeFrameNameDialog.java @@ -0,0 +1,72 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.content.Context; +import android.os.Bundle; +import android.widget.EditText; + +import com.mobsandgeeks.saripaar.annotation.Length; +import com.mobsandgeeks.saripaar.annotation.NotEmpty; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.activity.SettingActivity; +import com.shaya.poinila.android.util.ConstantsUtils; + +import butterknife.Bind; + +import static com.shaya.poinila.android.presentation.view.activity.SettingActivity.SettingType.FRAME_NAME; +import static com.shaya.poinila.android.util.ConstantsUtils.NO_RESOURCE; + +public class ChangeFrameNameDialog extends SingleTextFieldDialog { + private static final java.lang.String KEY_FRAME_NAME = "frame name"; + @Length(max = ConstantsUtils.max_length_frame_name, min = ConstantsUtils.min_length_frame_name, messageResId = R.string.error_frame_name_length) + @Bind(R.id.input_field) public EditText inputField; + String frameName; + + public static ChangeFrameNameDialog newInstance(String oldFrameName) { + Bundle args = new Bundle(); + ChangeFrameNameDialog fragment = new ChangeFrameNameDialog(); + fragment.frameName = oldFrameName; + fragment.saveStateToBundle(args); + fragment.setArguments(args); + return fragment; + } + + @Override + protected void initUI(Context context) { + super.initUI(context); + inputField.setText(frameName); + } + + @Override + protected void loadStateFromBundle(Bundle savedInstanceState) { + super.loadStateFromBundle(savedInstanceState); + frameName = savedInstanceState.getString(KEY_FRAME_NAME, ""); + } + + @Override + protected void saveStateToBundle(Bundle outState) { + super.saveStateToBundle(outState); + outState.putString(KEY_FRAME_NAME, frameName); + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData(R.string.edit_frame_name, NO_RESOURCE, R.string.submit, R.string.cancel, NO_RESOURCE); + } + + @Override + protected SettingActivity.SettingType getSettingType() { + return FRAME_NAME; + } + + @Override + protected void setTextFieldInputMethod() { + inputLayout.setCounterEnabled(true); + inputLayout.setCounterMaxLength(ConstantsUtils.max_length_frame_name); + inputLayout.setHint(getString(R.string.frame_name)); + } + + @Override + protected int getItemPosition() { + return adapterPosition; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ChangeFriendCirclesDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ChangeFriendCirclesDialog.java new file mode 100755 index 0000000..8abc580 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ChangeFriendCirclesDialog.java @@ -0,0 +1,149 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.content.Context; +import android.os.Bundle; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewProvider; +import com.shaya.poinila.android.presentation.uievent.CheckBoxClickUIEvent; +import com.shaya.poinila.android.presentation.uievent.FriendCirclesUpdated; +import com.shaya.poinila.android.presentation.viewholder.CheckedCircleViewHolder; +import com.shaya.poinila.android.util.BusProvider; +import com.squareup.otto.Subscribe; + +import org.parceler.Parcels; + +import java.util.ArrayList; +import java.util.List; + +import data.PoinilaNetService; +import data.model.Circle; +import data.model.Member; +import manager.DBFacade; + +import static android.support.v7.widget.LinearLayoutManager.VERTICAL; + +/** + * Created by iran on 2015-08-19. + */ +public class ChangeFriendCirclesDialog extends ListBusDialogFragment { + private static final String KEY_FRIEND = "friend"; + + private Member friend; + + @Override + public int getLayoutResId() { + return R.layout.recycler_view_full; + } + + public static ChangeFriendCirclesDialog newInstance(Member friend) { + Bundle args = new Bundle(); + ChangeFriendCirclesDialog fragment = new ChangeFriendCirclesDialog(); + fragment.friend = friend; + fragment.saveStateToBundle(args); + fragment.setArguments(args); + return fragment; + } + + + @Override + protected void initUI(final Context context) { + super.initUI(context); + mRecyclerView = new RecyclerViewProvider(mRecyclerView). + setLinearLayoutManager(VERTICAL). + setAdapter(getRecyclerViewAdapter()). + bindViewToAdapter(); + if (friend!= null) + fill(); + } + + private void selectFriendCurrentCircles(List allCircles, List friendCircleIDS) { + if (friendCircleIDS == null) + return; + for (Circle circle : allCircles) { + if (friendCircleIDS.contains(circle.id)){ + circle.selected = true; + } + } + } + + @Subscribe public void onCircleSelected(CheckBoxClickUIEvent event){ + getRecyclerViewAdapter().getItem(event.adapterPosition).selected = event.checked; + } + + @Override + public void onPositiveButton() { + List selectedCirclesIDs = new ArrayList<>(); + for (Object circle : getRecyclerViewAdapter().getItems()) { + if (((Circle)circle).selected) + selectedCirclesIDs.add(((Circle)circle).id); + } + PoinilaNetService.changeFriendCircle(selectedCirclesIDs, friend.id); + BusProvider.getBus().post(new FriendCirclesUpdated(selectedCirclesIDs, friend)); + super.onPositiveButton(); + } + +/* @Subscribe public void onMemberReceived(MemberReceivedEvent event){ + this.friend = event.member; + fill(); + }*/ + + private void fill(){ + List allCircles = DBFacade.getMyCircles(); + selectFriendCurrentCircles(allCircles, friend.circle_ids); + getRecyclerViewAdapter().resetData(allCircles); + } + + @Override + protected void loadStateFromBundle(Bundle savedInstanceState) { + super.loadStateFromBundle(savedInstanceState); + friend = Parcels.unwrap(savedInstanceState.getParcelable(KEY_FRIEND)); + } + + @Override + protected void saveStateToBundle(Bundle outState) { + super.saveStateToBundle(outState); + outState.putParcelable(KEY_FRIEND, Parcels.wrap(friend)); + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData(R.string.select_circle, RESOURCE_NONE, R.string.finish, RESOURCE_NONE, RESOURCE_NONE); + } + + @Override + protected boolean sendsRequestAutomatically() { + return false; + } + + @Override + protected void requestInitialData() { + } + + @Override + public boolean mustShowProgressView() { + return false; + } + + @Override + protected RecyclerView.OnScrollListener getRecyclerViewListener() { + return RecyclerViewProvider.linearListEndDetectorListener(getRecyclerViewAdapter(), this); + } + + @Override + public void requestForMoreData() { + + } + + @Override + public RecyclerViewAdapter createAndReturnRVAdapter() { + return new RecyclerViewAdapter(getActivity(), R.layout.checked_text) { + @Override + protected CheckedCircleViewHolder getProperViewHolder(View v, int viewType) { + return new CheckedCircleViewHolder(v); + } + }; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ChangeNameDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ChangeNameDialog.java new file mode 100755 index 0000000..44e48eb --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ChangeNameDialog.java @@ -0,0 +1,81 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.content.Context; +import android.os.Bundle; +import android.support.design.widget.TextInputEditText; +import android.support.design.widget.TextInputLayout; +import android.widget.EditText; + +import com.mobsandgeeks.saripaar.annotation.Length; +import com.mobsandgeeks.saripaar.annotation.NotEmpty; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.activity.SettingActivity; +import com.shaya.poinila.android.presentation.view.activity.SettingActivity.SettingType; +import com.shaya.poinila.android.util.ConstantsUtils; + +import butterknife.Bind; + +/** + * Created by iran on 2015-07-21. + */ +public class ChangeNameDialog extends SingleTextFieldDialog{ + + private static final String KEY_FULL_NAME = ConstantsUtils.KEY_FULL_NAME; + @Length(max = ConstantsUtils.max_length_full_name, min = ConstantsUtils.min_length_full_name, + messageResId = R.string.error_full_name_length, trim = true) + @Bind(R.id.input_field) + TextInputEditText fullNameField; + @Bind(R.id.field_input_layout) + TextInputLayout inputLayout; + + private String fullName; + + public static ChangeNameDialog newInstance(String fullName) { + Bundle args = new Bundle(); + ChangeNameDialog fragment = new ChangeNameDialog(); + fragment.fullName = fullName; + fragment.saveStateToBundle(args); + fragment.setArguments(args); + return fragment; + } + + @Override + protected void initUI(Context context) { + super.initUI(context); + fullNameField.setText(fullName); + } + + @Override + protected void loadStateFromBundle(Bundle savedInstanceState) { + super.loadStateFromBundle(savedInstanceState); + fullName = savedInstanceState.getString(KEY_FULL_NAME, ""); + } + + @Override + protected void saveStateToBundle(Bundle outState) { + super.saveStateToBundle(outState); + outState.putString(KEY_FULL_NAME, fullName); + } + + @Override + protected SettingType getSettingType() { + return SettingActivity.SettingType.FullName; + } + + @Override + protected void setTextFieldInputMethod() { + inputLayout.setCounterEnabled(true); + inputLayout.setCounterMaxLength(ConstantsUtils.max_length_full_name); + inputLayout.setHint(getString(R.string.hint_full_name)); + } + + @Override + protected int getItemPosition() { + return -1; + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData(RESOURCE_NONE, RESOURCE_NONE, R.string.submit, R.string.cancel, RESOURCE_NONE); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ChangePhoneDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ChangePhoneDialog.java new file mode 100755 index 0000000..a703fd0 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ChangePhoneDialog.java @@ -0,0 +1,67 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.content.Context; +import android.os.Bundle; +import android.text.InputType; +import android.widget.EditText; + +import com.mobsandgeeks.saripaar.annotation.Length; +import com.mobsandgeeks.saripaar.annotation.NotEmpty; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.activity.SettingActivity; + +import butterknife.Bind; + +/** + * Created by iran on 1/9/2016. + */ +public class ChangePhoneDialog extends SingleTextFieldDialog{ + + @NotEmpty(trim = true, messageResId = R.string.error_required_field) + + private static final String KEY_PHONE_NO = "old mobileNumber"; + private String mobileNumber; + + @Override + protected void initUI(Context context) { + super.initUI(context); + + inputField.setText(mobileNumber); + } + + public static ChangePhoneDialog newInstance(String oldCellPhone) { + Bundle args = new Bundle(); + ChangePhoneDialog fragment = new ChangePhoneDialog(); + args.putString(KEY_PHONE_NO, oldCellPhone); + fragment.saveStateToBundle(args); + fragment.setArguments(args); + return fragment; + } + + @Override + protected SettingActivity.SettingType getSettingType() { + return SettingActivity.SettingType.PHONE; + } + + @Override + protected void loadStateFromBundle(Bundle savedInstanceState) { + super.loadStateFromBundle(savedInstanceState); + + mobileNumber = savedInstanceState.getString(KEY_PHONE_NO, ""); + } + + @Override + protected void setTextFieldInputMethod() { + inputField.setInputType(InputType.TYPE_CLASS_PHONE); + } + + @Override + protected int getItemPosition() { + return -1; + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData(R.string.edit_phone_no, RESOURCE_NONE, R.string.submit, R.string.cancel, RESOURCE_NONE); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ChangeWebsiteDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ChangeWebsiteDialog.java new file mode 100755 index 0000000..2ed02fa --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ChangeWebsiteDialog.java @@ -0,0 +1,97 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.content.Context; +import android.os.Bundle; +import android.support.design.widget.TextInputEditText; +import android.text.TextUtils; +import android.widget.EditText; +import android.widget.TextView; + +import com.mobsandgeeks.saripaar.ValidationError; +import com.mobsandgeeks.saripaar.Validator; +import com.mobsandgeeks.saripaar.annotation.NotEmpty; +import com.mobsandgeeks.saripaar.annotation.Url; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.SimpleSettingTextSetEvent; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.presentation.view.activity.SettingActivity; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.ConstantsUtils; + +import java.util.List; + +import butterknife.Bind; + +/** + * Created by iran on 2015-10-07. + */ +public class ChangeWebsiteDialog extends BaseDialogFragment { + private static final String KEY_WEBSITE_NAME = ConstantsUtils.KEY_WEBSITE_NAME; + private static final String KEY_WEBSITE_ADDRESS = ConstantsUtils.KEY_WEBSITE_URL; + @Bind(R.id.website_name) + TextInputEditText websiteNameInput; + + @Bind(R.id.website_url) + TextInputEditText websiteAddressInput; + private String websiteName; + private String websiteAddress; + + @Override + public int getLayoutResId() { + return R.layout.dialog_setting_website; + } + + public static ChangeWebsiteDialog newInstance(String oldWebsiteAddress, String oldWebsiteName) { + Bundle args = new Bundle(); + ChangeWebsiteDialog fragment = new ChangeWebsiteDialog(); + args.putString(KEY_WEBSITE_ADDRESS, oldWebsiteAddress); + args.putString(KEY_WEBSITE_NAME, oldWebsiteName); + fragment.setArguments(args); + return fragment; + } + + @Override + protected void loadStateFromBundle(Bundle savedInstanceState) { + websiteAddress = savedInstanceState.getString(KEY_WEBSITE_ADDRESS, ""); + websiteName = savedInstanceState.getString(KEY_WEBSITE_NAME, ""); + } + + @Override + protected void saveStateToBundle(Bundle outState) { + outState.putString(KEY_WEBSITE_ADDRESS, websiteAddressInput.getText().toString()); + outState.putString(KEY_WEBSITE_NAME, websiteNameInput.getText().toString()); + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData(R.string.edit_website, RESOURCE_NONE, R.string.submit, R.string.cancel, RESOURCE_NONE); + } + + @Override + protected void initUI(Context context) { + websiteNameInput.setText(websiteName); + websiteAddressInput.setText(websiteAddress); + } + + @Override + public void onPositiveButton() { + // TODO: how can we achieve the same using saripaar?; + // it's not valid name is set but url is empty. every other state is valid + if (validateNameIsNotEmptyWhenURLIsNot(websiteAddressInput, websiteNameInput) && ViewUtils.validateUrl(websiteAddressInput)) { + BusProvider.getBus().post(new SimpleSettingTextSetEvent( + SettingActivity.SettingType.WEBSITE, + websiteNameInput.getText().toString() + "&" + websiteAddressInput.getText().toString().toLowerCase(), -1)); + } + dismiss(); + } + + private boolean validateNameIsNotEmptyWhenURLIsNot(EditText websiteAddressInput, EditText websiteNameInput) { + if (!TextUtils.isEmpty(websiteNameInput.getText().toString()) && + TextUtils.isEmpty(websiteAddressInput.getText().toString())) { + + ViewUtils.setInputError(websiteAddressInput, R.string.error_websiteNameSetURLNot); + return false; + } + return true; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/CircleMembersManagementDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/CircleMembersManagementDialog.java new file mode 100755 index 0000000..4049c94 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/CircleMembersManagementDialog.java @@ -0,0 +1,133 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.content.Context; +import android.os.Bundle; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewProvider; +import com.shaya.poinila.android.presentation.uievent.MemberCircleToggledEvent; +import com.shaya.poinila.android.presentation.viewholder.CircleMemberViewHolder; +import com.squareup.otto.Subscribe; + +import java.util.List; + +import data.PoinilaNetService; +import data.event.BaseEvent; +import data.event.MembersReceivedEvent; +import data.model.Member; +import manager.DataRepository; + +/** + * Created by iran on 2015-07-26. + */ +public class CircleMembersManagementDialog extends ListBusDialogFragment{ + + private static final String KEY_CIRCLE_ID = "circle id"; + private String circleID; + + public static CircleMembersManagementDialog newInstance(String circleID) { + Bundle args = new Bundle(); + CircleMembersManagementDialog fragment = new CircleMembersManagementDialog(); + fragment.circleID = circleID; + fragment.saveStateToBundle(args); + fragment.setArguments(args); + return fragment; + } + + @Override + protected void loadStateFromBundle(Bundle savedInstanceState) { + super.loadStateFromBundle(savedInstanceState); + circleID = savedInstanceState.getString(KEY_CIRCLE_ID, null); + } + + @Override + protected void saveStateToBundle(Bundle outState) { + super.saveStateToBundle(outState); + outState.putString(KEY_CIRCLE_ID, circleID); + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData(R.string.circle_members_management, RESOURCE_NONE, R.string.finish, RESOURCE_NONE, RESOURCE_NONE); + } + + @Subscribe + public void onToggle(MemberCircleToggledEvent event){ + Member member = getRecyclerViewAdapter().getItem(event.adapterPosition); + member.selected ^= true; + if (member.selected) { + PoinilaNetService.addFriendToCircle(circleID, member.getId()); + // TODO: request for removing this collectionSpinner from frame + }else { + PoinilaNetService.removeFriendFromCircle(circleID, member.getId()); + } + getRecyclerViewAdapter().notifyItemChanged(event.adapterPosition); + } + + @Override + protected void initUI(Context context) { + super.initUI(context); + mRecyclerView = new RecyclerViewProvider(mRecyclerView). + setLinearLayoutManager(LinearLayoutManager.VERTICAL). + setAdapter(getRecyclerViewAdapter()). + bindViewToAdapter(); + } + + @Override + protected RecyclerView.OnScrollListener getRecyclerViewListener() { + return RecyclerViewProvider.linearListEndDetectorListener(getRecyclerViewAdapter(), this); + } + + @Override + public RecyclerViewAdapter createAndReturnRVAdapter() { + return new RecyclerViewAdapter(getActivity(), R.layout.member_inlist) { + @Override + protected CircleMemberViewHolder getProperViewHolder(View v, int viewType) { + return new CircleMemberViewHolder(v); + } + }; + } + + @Override + protected boolean sendsRequestAutomatically() { + return true; + } + + @Override + protected void requestInitialData() { + requestForMoreData(); + } + + @Override + public boolean mustShowProgressView() { + return true; + } + + @Override + public void requestForMoreData() { + DataRepository.getInstance().getMemberFriends(DataRepository.getInstance().getMyId(), bookmark); + } + + @Subscribe public void OnFriendsReceived(MembersReceivedEvent event){ + onGettingInitDataResponse(event); + onGettingListDataResponse(event, event.bookmark); + } + + @Override + public void onSuccessfulListData(BaseEvent baseEvent, String newBookmark) { + super.onSuccessfulListData(baseEvent, newBookmark); + List members = ((MembersReceivedEvent) baseEvent).members; + for (Member member : members) { + member.selected = member.circle_ids != null && member.circle_ids.contains(Integer.parseInt(circleID)); + } + getRecyclerViewAdapter().addItems(members); + } + + @Override + public int getLayoutResId() { + return R.layout.recycler_view_weighted_full; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ContactUsDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ContactUsDialog.java new file mode 100755 index 0000000..b07342f --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ContactUsDialog.java @@ -0,0 +1,92 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.content.Context; +import android.os.Bundle; +import android.widget.EditText; +import android.widget.RadioGroup; +import android.widget.TextView; + +import com.mobsandgeeks.saripaar.ValidationError; +import com.mobsandgeeks.saripaar.Validator; +import com.mobsandgeeks.saripaar.annotation.Length; +import com.mobsandgeeks.saripaar.annotation.NotEmpty; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.util.ConstantsUtils; + +import java.util.List; + +import butterknife.Bind; +import data.PoinilaNetService; + +/** + * Created by iran on 2015-11-07. + */ +public class ContactUsDialog extends BaseDialogFragment implements Validator.ValidationListener{ + @Length(max = ConstantsUtils.max_length_report_title, messageResId = R.string.error_max_200) + @NotEmpty(trim = true, messageResId = R.string.error_required_field) + @Bind(R.id.title) + EditText titleInput; + + @Length(max = ConstantsUtils.max_length_report_content, messageResId = R.string.error_max_5000) + @NotEmpty(trim = true, messageResId = R.string.error_required_field) + @Bind(R.id.content) + EditText contentInput; + + @Bind(R.id.radio_group) public RadioGroup radioGroup; + @Bind(R.id.left_textview) public TextView leftTextView; + @Bind(R.id.right_textview) public TextView rightTextView; + + private Validator validator; + + @Override + public int getLayoutResId() { + return R.layout.dialog_contact_us; + } + + @Override + protected void loadStateFromBundle(Bundle savedInstanceState) { + + } + + @Override + protected void saveStateToBundle(Bundle outState) { + + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData(R.string.contact_us, RESOURCE_NONE, R.string.send, R.string.cancel, RESOURCE_NONE); + } + + @Override + protected void initUI(Context context) { + ViewUtils.setText(leftTextView, getString(R.string.critics_and_suggestions)); + ViewUtils.setText(rightTextView, getString(R.string.report_bug)); + + validator = new Validator(this); + validator.setValidationListener(this); + + contentInput.setMinLines(3); + contentInput.setMaxLines(7); + } + + @Override + public void onPositiveButton() { + validator.validate(false); + super.onPositiveButton(); + } + + @Override + public void onValidationSucceeded() { + PoinilaNetService.sendReport( + radioGroup.getCheckedRadioButtonId() == R.id.left_radioBtn ? "proposal" : "bug", + titleInput.getText().toString().trim(), + contentInput.getText().toString().trim()); + } + + @Override + public void onValidationFailed(List errors) { + ViewUtils.handleSaripaarErrors(errors, getActivity()); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/CoverFromPostsDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/CoverFromPostsDialog.java new file mode 100755 index 0000000..a832372 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/CoverFromPostsDialog.java @@ -0,0 +1,150 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.content.Context; +import android.os.Bundle; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewProvider; +import com.shaya.poinila.android.presentation.uievent.ImageClickedUIEvent; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.presentation.viewholder.SingleImageViewHolder; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.ResourceUtils; +import com.squareup.otto.Subscribe; + +import java.util.List; + +import data.event.BaseEvent; +import data.event.PostReceivedEvent; +import data.event.PostsReceivedEvent; +import data.model.ImageUrls; +import data.model.Post; +import data.model.PostType; +import manager.DataRepository; + +import static android.support.v7.widget.StaggeredGridLayoutManager.VERTICAL; + +/** + * Created by iran on 2015-09-26. + */ +public class CoverFromPostsDialog extends ListBusDialogFragment{ + + private static final String KEY_COLLECTION_ID = ConstantsUtils.KEY_COLLECTION_ID; + private String collectionID; + + + public static CoverFromPostsDialog newInstance(String collectionID){ + CoverFromPostsDialog dialogFragment = new CoverFromPostsDialog(); + Bundle arguments = new Bundle(); + arguments.putString(KEY_COLLECTION_ID, collectionID); + dialogFragment.setArguments(arguments); + return dialogFragment; + } + + + @Subscribe public void onPostsReceveid(PostsReceivedEvent event){ + onGettingInitDataResponse(event); + onGettingListDataResponse(event, event.bookmark); + } + + @Override + protected boolean isInitDataResponseValid(BaseEvent baseEvent) { + return ((PostsReceivedEvent) baseEvent).receiverName == BaseEvent.ReceiverName.PostsImagesDialog && super.isInitDataResponseValid(baseEvent); + } + + @Override + protected boolean isListDataResponseValid(BaseEvent baseEvent, String responseBookmark) { + return ((PostsReceivedEvent) baseEvent).receiverName == BaseEvent.ReceiverName.PostsImagesDialog && + super.isListDataResponseValid(baseEvent, ((PostsReceivedEvent) baseEvent).bookmark); + } + + @Override + public void onSuccessfulListData(BaseEvent baseEvent, String newBookmark) { + super.onSuccessfulListData(baseEvent, newBookmark); + List posts = ((PostsReceivedEvent) baseEvent).posts; + for (int i = posts.size() - 1; i >= 0; i--){ + if (posts.get(i).type == PostType.TEXT) + posts.remove(i); + } + getRecyclerViewAdapter().addItems(posts); + } + + @Override + protected void initUI(Context context) { + super.initUI(context); + mRecyclerView = new RecyclerViewProvider(mRecyclerView). + setAdapter(getRecyclerViewAdapter()). + setStaggeredLayoutManager(VERTICAL, ResourceUtils.getInteger(R.integer.column_count)). + bindViewToAdapter(); + } + + @Subscribe public void onImageClicked(ImageClickedUIEvent event){ + Post post = getRecyclerViewAdapter().getItem(event.adapterPosition); + BusProvider.getBus().post(new PostReceivedEvent(post)); + dismiss(); + } + + @Override + protected RecyclerView.OnScrollListener getRecyclerViewListener() { + return RecyclerViewProvider.staggeredListEndDetectorListener(getRecyclerViewAdapter(), this); + } + + @Override + public void requestForMoreData() { + DataRepository.getCollectionPostsImages(collectionID, null, bookmark, BaseEvent.ReceiverName.PostsImagesDialog); + } + + @Override + public RecyclerViewAdapter> createAndReturnRVAdapter() { + return new RecyclerViewAdapter>(getActivity(), R.layout.single_image_staggered) { + @Override + protected SingleImageViewHolder getProperViewHolder(View v, int viewType) { + return new SingleImageViewHolder(v) { + @Override + public void fill(Post post) { + ViewUtils.setImage(imageView, post.imagesUrls, ImageUrls.ImageType.POST, ImageUrls.ImageSize.MEDIUM); + } + }; + } + }; + } + + @Override + protected boolean sendsRequestAutomatically() { + return true; + } + + @Override + protected void requestInitialData() { + requestForMoreData(); + } + + @Override + public boolean mustShowProgressView() { + return true; + } + + @Override + public int getLayoutResId() { + return R.layout.recycler_view_full; + } + + @Override + protected void loadStateFromBundle(Bundle savedInstanceState) { + collectionID = savedInstanceState.getString(KEY_COLLECTION_ID); + } + + @Override + protected void saveStateToBundle(Bundle outState) { + outState.putString(KEY_COLLECTION_ID, collectionID); + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData(getString(R.string.hint_coverFromPosts), null, null, null, null); + } + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/DialogLauncher.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/DialogLauncher.java new file mode 100755 index 0000000..cbc1888 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/DialogLauncher.java @@ -0,0 +1,125 @@ + +package com.shaya.poinila.android.presentation.view.dialog; + +import android.app.FragmentManager; +import android.view.View; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.PositiveButtonClickedUIEvent; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.Logger; + +import data.model.Collection; +import data.model.FriendRequestAnswer; +import data.model.Member; +import data.model.Post; +import data.model.SuggestedWebPagePost; + + +/** + * Created by iran on 2015-09-26. + */ + +public class DialogLauncher { + public static void launchChangeFriendCircle(android.support.v4.app.FragmentManager fragmentManager, Member member) { + ChangeFriendCirclesDialog.newInstance(member).show(fragmentManager, null); + } + + public static void launchEditCollectionDialog(android.support.v4.app.FragmentManager fragmentManager, Collection collection) { + EditCollectionDialog.newInstance(collection).show(fragmentManager, null); + } + + public static void launchDeleteCollection(android.support.v4.app.FragmentManager fragmentManager) { + new PoinilaAlertDialog.Builder().setTitle(R.string.remove_collectoin). + setMessage(R.string.confirm_delete_collection). + setNegativeBtnText(R.string.no). + setPositiveBtnText(R.string.yes). + build().show(fragmentManager, null); + } + + public static void launchPickCoverFromPosts(android.support.v4.app.FragmentManager fragmentManager, String collectionID) { + CoverFromPostsDialog.newInstance(collectionID).show(fragmentManager, null); + } + + + public static void launchNewWebsitePost(android.support.v4.app.FragmentManager fragmentManager) { + new NewWebsitePostDialog().show(fragmentManager, null); + } + + public static void launchAboutPoinila(FragmentManager fragmentManager) { + new AboutPoinilaDialog().show(fragmentManager, null); + } + + public static void launchContactUsDialog(android.support.v4.app.FragmentManager supportFragmentManager) { + new ContactUsDialog().show(supportFragmentManager, null); + } + + public static void launchFriendshipDialog(final Member member, android.support.v4.app.FragmentManager fragmentManager) { + switch (member.friendshipStatus) { + case NotFriend: + case WaitingForAction: + new PoinilaAlertDialog.Builder(). + setTitle(R.string.friend_request). + setMessage(R.string.approve_send_friend_request). + setPositiveBtnText(R.string.yes). + setNegativeBtnText(R.string.no). + setPositiveBtnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new PositiveButtonClickedUIEvent().setData(FriendRequestAnswer.ACCEPT)); + } + }). + build().show(fragmentManager, null); + break; + case IsFriend: + EditFriendShipDialog.newInstance(member).show(fragmentManager, null); + break; + case Pending: + Logger.toast(R.string.info_already_requested); + break; + } + } + + public static void launchNewPost(android.support.v4.app.FragmentManager fragmentManager, SuggestedWebPagePost webpagePost) { + NewPostDialog.newInstance(webpagePost).show(fragmentManager, null); + } + + + public static void launchRepostDialog(android.support.v4.app.FragmentManager fragmentManager, Post post) { + RepostDialog.newInstance(post).show(fragmentManager, null); + } + + + public static void launchSelectImage(android.support.v4.app.FragmentManager fragmentManager, Member member, View.OnClickListener onItemClickListener){ + SelectImageDialog.newInstance(member, onItemClickListener).show(fragmentManager, null); + } + + public static void launchReportDialog(android.support.v4.app.FragmentManager fragmentManager, int title, int memberIdOrPostId){ + ReportDialog.newInstance(title, memberIdOrPostId).show(fragmentManager, null); + } + + public static void launchInputVerificationCodeDialog(android.support.v4.app.FragmentManager fragmentManager, String mobileOrEmail, boolean byEmail){ + InputVerificationCodeDialog.newInstance(mobileOrEmail, byEmail).show(fragmentManager, null); + } + + public static void launchInputVerificationCodeDialog(android.support.v4.app.FragmentManager fragmentManager, String mobileOrEmail, boolean byEmail, boolean disableResend){ + InputVerificationCodeDialog.newInstance(mobileOrEmail, byEmail, disableResend).show(fragmentManager, null); + } + + public static void launchRequestVerificationDialog(android.support.v4.app.FragmentManager fragmentManager){ + VerificationRequestCodeDialog.newInstance().show(fragmentManager, null); + } + + public static void launchRequestVerificationDialog(android.support.v4.app.FragmentManager fragmentManager, int titleRes, String inputValue, boolean mVerificationByEmail){ + VerificationRequestCodeDialog.newInstance(titleRes, inputValue, mVerificationByEmail).show(fragmentManager, null); + } + + public static void launchMessageDialog(android.support.v4.app.FragmentManager fragmentManager, int titleRes, int messageRes){ + MessageDialog.newInstance(titleRes, messageRes).show(fragmentManager, null); + } + + public static void launchSetUsernamePasswordDialog(android.support.v4.app.FragmentManager fragmentManager){ + SetUserNamePasswordDialog.newInstance().show(fragmentManager, null); + } + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/EditAboutMeDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/EditAboutMeDialog.java new file mode 100755 index 0000000..5bb843a --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/EditAboutMeDialog.java @@ -0,0 +1,85 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +/** + * Created by AlirezaF on 12/25/2015. + */ + +import android.content.Context; +import android.os.Bundle; +import android.widget.EditText; + +import com.mobsandgeeks.saripaar.annotation.Length; +import com.mobsandgeeks.saripaar.annotation.NotEmpty; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.activity.SettingActivity; +import com.shaya.poinila.android.util.ConstantsUtils; + +import butterknife.Bind; + +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_ABOUT_ME; +import static com.shaya.poinila.android.util.ConstantsUtils.NO_RESOURCE; + +/** + * Created by iran on 2015-10-11. + */ +public class EditAboutMeDialog extends SingleTextFieldDialog { + @Length(max = ConstantsUtils.max_length_about_me, messageResId = R.string.error_max_500) + @Bind(R.id.input_field) + public EditText inputField; + + private String aboutMeString; + + public static EditAboutMeDialog newInstance(String oldAboutMe) { + + Bundle args = new Bundle(); + EditAboutMeDialog fragment = new EditAboutMeDialog(); + fragment.aboutMeString = oldAboutMe; + fragment.saveStateToBundle(args); + fragment.setArguments(args); + return fragment; + } + + @Override + protected void saveStateToBundle(Bundle outState) { + super.saveStateToBundle(outState); + outState.putString(KEY_ABOUT_ME, aboutMeString); + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData(NO_RESOURCE, NO_RESOURCE, R.string.submit, R.string.cancel, NO_RESOURCE); + } + + @Override + protected void loadStateFromBundle(Bundle savedInstanceState) { + super.loadStateFromBundle(savedInstanceState); + aboutMeString = savedInstanceState.getString(KEY_ABOUT_ME, ""); + } + + @Override + protected SettingActivity.SettingType getSettingType() { + return SettingActivity.SettingType.ABOUT_ME; + } + + @Override + protected void initUI(Context context) { + super.initUI(context); + inputField.setText(aboutMeString); + } + + @Override + protected void setTextFieldInputMethod() { + inputLayout.setHint(getString(R.string.hint_about_me)); + inputField.setMinLines(3); + inputField.setMaxLines(5); + inputLayout.setCounterEnabled(true); + inputLayout.setCounterMaxLength(ConstantsUtils.max_length_about_me); + } + + @Override + protected int getItemPosition() { + return -1; + } + +} + diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/EditCollectionDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/EditCollectionDialog.java new file mode 100755 index 0000000..2bb41c1 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/EditCollectionDialog.java @@ -0,0 +1,182 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.content.Context; +import android.os.Bundle; +import android.view.View; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.CirclesSelectedUIEvent; +import com.shaya.poinila.android.presentation.uievent.PermissionEvent; +import com.shaya.poinila.android.presentation.uievent.SelectImageEvent; +import com.shaya.poinila.android.presentation.view.activity.BaseActivity; +import com.shaya.poinila.android.presentation.view.costom_view.EditCollectionImagePickerView; +import com.shaya.poinila.android.util.Logger; +import com.squareup.otto.Subscribe; + +import java.util.List; + +import butterknife.Bind; +import data.PoinilaNetService; +import data.event.CollectionReceivedEvent; +import data.event.CollectionUpdatedEvent; +import data.event.PostReceivedEvent; +import data.event.TopicsReceivedEvent; +import data.model.Circle; +import data.model.Collection; +import data.model.Image; +import data.model.ImageUrls; +import data.model.PrivacyType; +import data.model.Topic; + +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; + +/** + * Created by AlirezaF on 7/19/2015. + */ +public class EditCollectionDialog extends NewCollectionDialog{ + public static final String KEY_COVER_URL = "cover url"; + private static String tempCoverUrl; + private Collection collection; + + @Bind(R.id.pickerView) EditCollectionImagePickerView pickerView; + + public static EditCollectionDialog newInstance(Collection collection) { + // TODO: make collection parcelable + Bundle args = new Bundle(); + EditCollectionDialog fragment = new EditCollectionDialog(); + fragment.collection = collection; + fragment.saveStateToBundle(args); + fragment.setArguments(args); + return fragment; + } + + @Override + protected void saveStateToBundle(Bundle outState) { + super.saveStateToBundle(outState); + // TODO: store createCollectionFromFields in bundle + outState.putString(KEY_COVER_URL, tempCoverUrl); + } + + @Override + protected void loadStateFromBundle(Bundle savedInstanceState) { + super.loadStateFromBundle(savedInstanceState); + // TODO load colletion from pardel + tempCoverUrl = savedInstanceState.getString(KEY_COVER_URL, null); + } + + @Override + protected void initUI(Context context) { + super.initUI(context); + pickerView.setOnPickCoverFromPostsListener(new EditCollectionImagePickerView.OnPickCoverFromPostsListener() { + @Override + public void onPickCoverFromPosts() { + DialogLauncher.launchPickCoverFromPosts(getFragmentManager(), collection.getId()); + } + }); + setDefaultValues(collection); + } + + + private void setDefaultValues(Collection collection) { + if (collectionHasCover(collection) && !pickerView.hasImage() && collection.coverImageUrls != null) { + Image image = collection.coverImageUrls.properCollectionImage(ImageUrls.ImageSize.BIG); + if(image != null) + pickerView.setImage(image.url); + } + + setText(nameField, collection.name); + setText(descriptionField, collection.description); + if (collection.privacy == PrivacyType.PUBLIC) { + privacyContainer.setVisibility(View.GONE); + selectCircleBtn.setVisibility(View.GONE); + } else{ + privateCollectionCheckbox.setChecked(true); + findCheckedCircles(mCheckedCircles, collection.circleIDs, mCircles); + setText(selectCircleBtn, createSelectedCirclesText(mCheckedCircles)); + } + } + + private boolean collectionHasCover(Collection collection) { + return collection.coverImageUrls != null; + } + + private void findCheckedCircles(boolean[] checkedCircles, + List circleIDs, List circles) { + for (Integer circleID : circleIDs) { + for (int i = 0; i < circles.size(); i++){ + if (circleID == circles.get(i).id) + checkedCircles[i] = true; + } + } + } + + @Subscribe public void onTopicReceived(TopicsReceivedEvent event){ + super.onTopicReceived(event); + int index = findTopicIndex(mSpinnerAdapter, collection.topic.id); + topicSpinner.setSelection(index); + } + + @Subscribe public void onCirclesSelectedEvent(CirclesSelectedUIEvent event){ + super.onCirclesSelectedEvent(event); + } + + @Subscribe public void onPostImageReceived(PostReceivedEvent event){ + tempCoverUrl = event.post.imagesUrls.properPostImage(ImageUrls.ImageSize.BIG).url; + pickerView.setImage(tempCoverUrl); + } + + private int findTopicIndex(MySpinnerAdapter mSpinnerAdapter, int topicID) { + for (int i = 0; i < mSpinnerAdapter.getCount(); i++) { + if (((Topic) mSpinnerAdapter.getItem(i)).id == topicID) + return i; + } + return -1; + } + + @Override + public void onPositiveButton() { + if (!validate()) + return; + + showProgressDialog(); + + Logger.log("Image = " + pickerView.getImage(), Logger.LEVEL_INFO); + + PoinilaNetService.updateCollection(collection.getId(), + createCollectionFromFields(), pickerView.getImage()); + + + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData(R.string.edit_collection, RESOURCE_NONE, R.string.edit, R.string.cancel, RESOURCE_NONE); + } + + @Override + public int getLayoutResId() { + return R.layout.dialog_edit_collection; + } + + @Subscribe + public void selectImageEvent(SelectImageEvent event) { + super.selectImageEvent(event); + } + + // I think it's cleaner to ask for permission directly in pickerView + @Subscribe public void askForPermissionEvent(PermissionEvent event){ + super.askForPermissionEvent(event); + } + + @Subscribe + public void onCollectionUpdated(CollectionUpdatedEvent event) { + dismissProgressDialog(); + onNegativeButton(); + } + + @Subscribe + public void onCollectionReceived(CollectionReceivedEvent event){ + dismissProgressDialog(); + onNegativeButton(); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/EditFriendShipDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/EditFriendShipDialog.java new file mode 100755 index 0000000..14f80c3 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/EditFriendShipDialog.java @@ -0,0 +1,118 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.content.Context; +import android.os.Bundle; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.presentation.viewholder.SimpleTextViewHolder; + +import org.parceler.Parcels; + +import java.util.List; + +import butterknife.Bind; +import data.PoinilaNetService; +import data.model.Circle; +import data.model.FriendshipStatus; +import data.model.Member; +import manager.DBFacade; + +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; + +/** + * Created by iran on 12/28/2015. + */ +public class EditFriendShipDialog extends BusDialogFragment { + + @Bind(R.id.llcontainer) + LinearLayout mLinearLayoutContainer; + + private static final String KEY_FRIEND = "friend"; + private Member friend; + + public static EditFriendShipDialog newInstance(Member friend) { + Bundle args = new Bundle(); + EditFriendShipDialog fragment = new EditFriendShipDialog(); + fragment.friend = friend; + fragment.saveStateToBundle(args); + fragment.setArguments(args); + return fragment; + } + + + @Override + protected boolean sendsRequestAutomatically() { + return true; + } + + @Override + protected void requestInitialData() { + List allCircles = DBFacade.getMyCircles(); + onGettingInitDataResponse(null); + if (friend.circle_ids == null) + return; + for (Circle circle : allCircles) { + if (friend.circle_ids.contains(circle.id)) { + TextView textView = (TextView) getActivity().getLayoutInflater().inflate(R.layout.simple_textview, mLinearLayoutContainer, false); + setText(textView, circle.name); + mLinearLayoutContainer.addView(textView); + } + } + } + + @Override + public ViewGroup getLoadableView() { + return null; + } + + + @Override + public boolean mustShowProgressView() { + return false; + } + + @Override + public int getLayoutResId() { + return R.layout.scrollable_linearlayout; + } + + @Override + protected void loadStateFromBundle(Bundle savedInstanceState) { + this.friend = Parcels.unwrap(savedInstanceState.getParcelable(KEY_FRIEND)); + } + + @Override + protected void saveStateToBundle(Bundle outState) { + outState.putParcelable(KEY_FRIEND, Parcels.wrap(friend)); + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData(R.string.edit_friendship, RESOURCE_NONE, + R.string.remove_friend, R.string.cancel, R.string.edit_circle); + } + + @Override + protected void initUI(Context context) { + + } + + @Override + public void onPositiveButton() { + PoinilaNetService.removeFriend(friend.getId()); + friend.friendshipStatus = FriendshipStatus.NotFriend; + super.onPositiveButton(); + } + + @Override + public void onNeutralButton() { + DialogLauncher.launchChangeFriendCircle( + getFragmentManager(), friend); + super.onNeutralButton(); + } +} \ No newline at end of file diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ForgotPasswordFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ForgotPasswordFragment.java new file mode 100755 index 0000000..fcd4613 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ForgotPasswordFragment.java @@ -0,0 +1,203 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.text.InputType; +import android.util.Patterns; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.RadioGroup; +import android.widget.TextView; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.presentation.view.activity.SignUpLoginActivity; +import com.shaya.poinila.android.presentation.view.fragments.BusFragment; +import com.squareup.otto.Subscribe; + +import butterknife.Bind; +import butterknife.ButterKnife; +import butterknife.OnClick; +import data.JsonRequestBodyMaker; +import data.PoinilaNetService; +import data.event.VerificationRequestResponse; + +/** + * Created by AlirezaF on 12/7/2015. + */ +public class ForgotPasswordFragment extends BusFragment implements View.OnClickListener { + private static final String KEY_RECOVERY_TYPE = "recovery type"; +// @Bind(R.id.radio_group) +// RadioGroup optionsRadioGroup; + @Bind(R.id.input_field) + EditText inputField; + @Bind(R.id.input_filed_icon) + ImageView inputFieldIcon; + @Bind(R.id.submit) + Button SubmitButton; + @Bind(R.id.card_title) + TextView title; + + @Bind(R.id.email_option) + TextView emailOption; + + @Bind(R.id.sms_option) + TextView smsOption; + + @Bind(R.id.unique_name_option) + TextView uniqueNameOption; + + @Override + public void onClick(View view) { + switch (view.getId()){ + case R.id.email_option: + onMailOption(); + break; + case R.id.sms_option: + onSmsOption(); + break; + case R.id.unique_name_option: + onUniqueNameOption(); + break; + } + } + + public enum RECOVERY_PASS_TYPE{ + EMAIL, + MOBILE_NUMBER, + UNIQUE_NAME + } + + private RECOVERY_PASS_TYPE recoveryPassType; + + @Override + public int getLayoutID() { + return R.layout.fragment_forgot_password; + } + + @Override + protected void initUI() { + ButterKnife.findById(rootView, R.id.left_arrow).setVisibility(View.GONE); + emailOption.setOnClickListener(this); + smsOption.setOnClickListener(this); + uniqueNameOption.setOnClickListener(this); + + smsOption.setSelected(true); + inputFieldIcon.setImageResource(R.drawable.phone_48dp); + recoveryPassType = RECOVERY_PASS_TYPE.MOBILE_NUMBER; + +// optionsRadioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { +// @Override +// public void onCheckedChanged(RadioGroup group, int checkedId) { +// switch (checkedId) { +// case R.id.email_option: +// onMailOption(); +// break; +// case R.id.sms_option: +// onSmsOption(); +// break; +// case R.id.unique_name_option: +// onUniqueNameOption(); +// break; +// } +// } +// }); + ViewUtils.setText(title, getString(R.string.recover_password)); + } + + private void onMailOption() { + emailOption.setSelected(true); + smsOption.setSelected(false); + uniqueNameOption.setSelected(false); + recoveryPassType = RECOVERY_PASS_TYPE.EMAIL; + inputField.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS); + inputFieldIcon.setImageResource(R.drawable.email_48dp); + } + + private void onSmsOption() { + emailOption.setSelected(false); + smsOption.setSelected(true); + uniqueNameOption.setSelected(false); + + recoveryPassType = RECOVERY_PASS_TYPE.MOBILE_NUMBER; + inputField.setInputType(InputType.TYPE_CLASS_PHONE); + inputFieldIcon.setImageResource(R.drawable.phone_48dp); + } + + private void onUniqueNameOption() { + emailOption.setSelected(false); + smsOption.setSelected(false); + uniqueNameOption.setSelected(true); + recoveryPassType = RECOVERY_PASS_TYPE.UNIQUE_NAME; + inputField.setInputType(InputType.TYPE_CLASS_TEXT); + inputFieldIcon.setImageResource(R.drawable.form_login_user); + } + + @OnClick(R.id.submit) + public void onRequestResetPassCode() { + String userText = inputField.getText().toString().trim(); + if (recoveryPassType.equals(RECOVERY_PASS_TYPE.EMAIL) && !Patterns.EMAIL_ADDRESS.matcher(userText).matches()) { + inputField.setText(""); + ViewUtils.temporaryError(inputField, getString(R.string.error_invalid_email)); + return; + } else if (recoveryPassType.equals(RECOVERY_PASS_TYPE.MOBILE_NUMBER) && !Patterns.PHONE.matcher(userText).matches()) { + inputField.setText(""); + ViewUtils.temporaryError(inputField, getString(R.string.error_invalid_phone)); + return; + }else if(recoveryPassType.equals(RECOVERY_PASS_TYPE.UNIQUE_NAME)){ + + } + PoinilaNetService.recoverPassword(recoveryPassType, userText); + } + + @Subscribe + public void onRequestSent(VerificationRequestResponse event) { + if (event.succeed) { + ((SignUpLoginActivity) getActivity()).goToResetPassword(null); + } else if (event.code == 446){ + switch (recoveryPassType){ + case EMAIL: + ViewUtils.temporaryError(inputField, getString(R.string.error_no_occurance_email)); + break; + case MOBILE_NUMBER: + ViewUtils.temporaryError(inputField, getString(R.string.error_no_occurance_phone)); + break; + case UNIQUE_NAME: + ViewUtils.temporaryError(inputField, getString(R.string.error_no_occurance_unique_name)); + break; + } + } + } + + @Override + protected boolean sendsRequestAutomatically() { + return false; + } + + @Override + protected void requestInitialData() { + + } + + @Override + public ViewGroup getLoadableView() { + return null; + } + + @Override + public boolean mustShowProgressView() { + return false; + } + + /* @Override + public Bundle getBundle() { + Bundle bundle = new Bundle(); + bundle.putBoolean(KEY_RECOVERY_TYPE, mRecoveryByEmail); + return bundle; + } + + @Override + public void setBundle(Bundle bundle) { + mRecoveryByEmail = bundle.getBoolean(KEY_RECOVERY_TYPE); + }*/ +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/FrameCollectionsManagementDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/FrameCollectionsManagementDialog.java new file mode 100755 index 0000000..dfb1541 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/FrameCollectionsManagementDialog.java @@ -0,0 +1,132 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.content.Context; +import android.os.Bundle; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewProvider; +import com.shaya.poinila.android.presentation.uievent.CollectionFrameToggledEvent; +import com.shaya.poinila.android.presentation.viewholder.FrameCollectionViewHolder; +import com.squareup.otto.Subscribe; + +import java.util.List; + +import data.PoinilaNetService; +import data.event.BaseEvent; +import data.event.CollectionsReceivedEvent; +import data.model.Collection; +import manager.DataRepository; + +/** + * Created by iran on 2015-07-28. + */ +public class FrameCollectionsManagementDialog extends ListBusDialogFragment{ + private static final java.lang.String KEY_FRAME_ID = "frame id"; + private String frameID; + + public static FrameCollectionsManagementDialog newInstance(String frameId) { + Bundle args = new Bundle(); + FrameCollectionsManagementDialog fragment = new FrameCollectionsManagementDialog(); + fragment.frameID = frameId; + fragment.saveStateToBundle(args); + fragment.setArguments(args); + return fragment; + } + + @Subscribe + public void onToggle(CollectionFrameToggledEvent event){ + Collection collection = getRecyclerViewAdapter().getItem(event.adapterPosition); + collection.selected ^= true; + if (collection.selected) { + PoinilaNetService.addCollectionToFrame(frameID, collection.getId()); + // TODO: request for removing this collectionSpinner from frame + }else { + PoinilaNetService.removeCollectionFromFrame(frameID, collection.getId()); + } + getRecyclerViewAdapter().notifyItemChanged(event.adapterPosition); + } + + @Override + protected void initUI(Context context) { + super.initUI(context); + mRecyclerView = new RecyclerViewProvider(mRecyclerView). + setLinearLayoutManager(LinearLayoutManager.VERTICAL). + setAdapter(getRecyclerViewAdapter()).bindViewToAdapter(); + } + + @Override + protected RecyclerView.OnScrollListener getRecyclerViewListener() { + return RecyclerViewProvider.linearListEndDetectorListener(getRecyclerViewAdapter(), this); + } + + @Override + protected boolean sendsRequestAutomatically() { + return true; + } + + @Override + protected void requestInitialData() { + requestForMoreData(); + } + + @Override + public boolean mustShowProgressView() { + return true; + } + + @Override + public void requestForMoreData() { + DataRepository.getInstance().getMyFollowedCollections(null, bookmark); + } + + @Override + public RecyclerViewAdapter createAndReturnRVAdapter() { + return new RecyclerViewAdapter( + getActivity(), R.layout.rounded_image_title_subtitle_icon) { + @Override + protected FrameCollectionViewHolder getProperViewHolder(View v, int viewType) { + return new FrameCollectionViewHolder(v); + } + }; + } + + @Subscribe public void OnCollectionsReceived(CollectionsReceivedEvent event){ + onGettingInitDataResponse(event); + onGettingListDataResponse(event, event.bookmark); + } + + @Override + public void onSuccessfulListData(BaseEvent baseEvent, String newBookmark) { + super.onSuccessfulListData(baseEvent, newBookmark); + List collections = ((CollectionsReceivedEvent) baseEvent).collections; + for (Collection collection : collections) { + collection.selected = collection.frameIDs != null && collection.frameIDs.contains(Integer.parseInt(frameID)); + } + getRecyclerViewAdapter().addItems(collections); + } + + @Override + public int getLayoutResId() { + return R.layout.recycler_view_weighted_full; + } + + @Override + protected void loadStateFromBundle(Bundle savedInstanceState) { + super.loadStateFromBundle(savedInstanceState); + frameID = savedInstanceState.getString(KEY_FRAME_ID); + } + + @Override + protected void saveStateToBundle(Bundle outState) { + super.saveStateToBundle(outState); + outState.putString(KEY_FRAME_ID, frameID); + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData(R.string.frame_members_management, RESOURCE_NONE, R.string.finish, RESOURCE_NONE, RESOURCE_NONE); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/InputVerificationCodeDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/InputVerificationCodeDialog.java new file mode 100755 index 0000000..f95b2a6 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/InputVerificationCodeDialog.java @@ -0,0 +1,147 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.content.Context; +import android.os.Bundle; +import android.view.ViewGroup; +import android.widget.EditText; +import android.widget.Toast; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.AfterVerifyResponse; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.Logger; +import com.squareup.otto.Subscribe; + +import butterknife.Bind; +import data.PoinilaNetService; +import data.event.VerificationRequestResponse; +import manager.DBFacade; + +/** + * Created by iran on 7/17/2016. + */ +public class InputVerificationCodeDialog extends BusDialogFragment { + + + @Bind(R.id.input_field) + EditText verificationCode; + + private String mobileOrEmail; + private boolean byEmail; + private boolean disableResend; + + public static InputVerificationCodeDialog newInstance(String mobileOrEmail, boolean byEmail){ + InputVerificationCodeDialog fragment = new InputVerificationCodeDialog(); + + fragment.mobileOrEmail = mobileOrEmail; + fragment.byEmail = byEmail; + + + return fragment; + } + + public static InputVerificationCodeDialog newInstance(String mobileOrEmail, boolean byEmail, boolean disableResend){ + InputVerificationCodeDialog fragment = new InputVerificationCodeDialog(); + + fragment.mobileOrEmail = mobileOrEmail; + fragment.byEmail = byEmail; + fragment.disableResend = disableResend; + + return fragment; + } + + @Override + protected boolean sendsRequestAutomatically() { + return false; + } + + @Override + protected void requestInitialData() { + + } + + @Override + public ViewGroup getLoadableView() { + return null; + } + + @Override + public boolean mustShowProgressView() { + return false; + } + + @Override + public int getLayoutResId() { + return R.layout.input_verification_code; + } + + @Override + protected void loadStateFromBundle(Bundle savedInstanceState) { + + } + + @Override + protected void saveStateToBundle(Bundle outState) { + + } + + @Override + public void onPositiveButton() { +// super.onPositiveButton(); + + if(DBFacade.getCachedMyInfo() != null){ + showProgressDialog(); + PoinilaNetService.verifyPhoneOrMobile( + verificationCode.getText().toString(), + DBFacade.getCachedMyInfo().id, + mobileOrEmail, + byEmail + ); + + } else + Logger.toast(R.string.error_user_not_found); + + + } + + @Override + public void onNeutralButton() { + super.onNeutralButton(); + + DialogLauncher.launchRequestVerificationDialog(getFragmentManager()); + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + if(disableResend) + return new GeneralDialogData(R.string.verification_code, ConstantsUtils.NO_RESOURCE, R.string.send, R.string.cancel, ConstantsUtils.NO_RESOURCE); + else + return new GeneralDialogData(R.string.verification_code, ConstantsUtils.NO_RESOURCE, R.string.send, R.string.cancel, R.string.resend); + } + + @Override + protected void initUI(Context context) { + + } + + @Subscribe + public void onVerifyResponse(VerificationRequestResponse event) { + + dismissProgressDialog(); + + dismiss(); + + switch (event.code){ + case 200: + Toast.makeText(getActivity(), R.string.success_verification, Toast.LENGTH_LONG).show(); + BusProvider.getBus().post(new AfterVerifyResponse()); + break; + case 446: + Toast.makeText(getActivity(), R.string.error_verification_wrong_code, Toast.LENGTH_LONG).show(); + break; + } + + + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ListBusDialogFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ListBusDialogFragment.java new file mode 100755 index 0000000..fa712d6 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ListBusDialogFragment.java @@ -0,0 +1,97 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.content.Context; +import android.os.Bundle; +import android.support.annotation.CallSuper; +import android.support.annotation.Nullable; +import android.support.v7.widget.RecyclerView; +import android.view.ViewGroup; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.view.LoaderList; + +import butterknife.Bind; +import data.event.BaseEvent; + +/** + * Created by iran on 11/25/2015. + */ +public abstract class ListBusDialogFragment extends BusDialogFragment implements LoaderList{ + private static final String CLICKED_ITEM_POSITION = "clicked item position"; + protected boolean requestingIsLocked = false; + public String bookmark; + protected int clickedItemPosition = -1; + @Bind(R.id.recycler_view) protected RecyclerView mRecyclerView; + private RecyclerViewAdapter mAdapter; + + + @Override @CallSuper + protected void loadStateFromBundle(Bundle savedInstanceState) { + clickedItemPosition = savedInstanceState.getInt(CLICKED_ITEM_POSITION); + } + + @Override @CallSuper + protected void saveStateToBundle(Bundle outState) { + outState.putInt(CLICKED_ITEM_POSITION, clickedItemPosition); + } + + @Override + @CallSuper + protected void initUI(Context context) { + if(getRecyclerViewListener() != null){ + mRecyclerView.addOnScrollListener(getRecyclerViewListener()); + } + } + + /** + * Called in {@link #onStart()} after {@link #initUI(Context)} so it's safe to assume class variables + * are initialized. + * @return + */ + protected abstract RecyclerView.OnScrollListener getRecyclerViewListener(); + + public void onLoadMore(){ + if (!requestingIsLocked) { + requestingIsLocked = true; + requestForMoreData(); + } + } + + @CallSuper + public void onSuccessfulListData(BaseEvent baseEvent, String newBookmark){ + requestingIsLocked = false; + bookmark = newBookmark; + } + + public abstract void requestForMoreData(); + + @Override + public ViewGroup getLoadableView() { + return mRecyclerView; + } + + public RecyclerViewAdapter getRecyclerViewAdapter(){ + if (mAdapter == null) + mAdapter = createAndReturnRVAdapter(); + return mAdapter; + } + + @CallSuper + protected void onGettingListDataResponse(BaseEvent event, String responseBookmark) { + if (isListDataResponseValid(event, responseBookmark)) + onSuccessfulListData(event, responseBookmark); + } + + @CallSuper + protected boolean isListDataResponseValid(BaseEvent baseEvent, String responseBookmark){ + // bookmark != null && + return checkBookMark(this.bookmark, responseBookmark); // this.bookmark may be null + } + + + public boolean checkBookMark(String pageBookmark, String serverBookmark) { + return serverBookmark == null || !serverBookmark.equals(pageBookmark); + } + + public abstract RecyclerViewAdapter createAndReturnRVAdapter(); +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/MessageDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/MessageDialog.java new file mode 100755 index 0000000..574ae98 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/MessageDialog.java @@ -0,0 +1,80 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.content.Context; +import android.os.Bundle; +import android.view.ViewGroup; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.util.ConstantsUtils; + +/** + * Created by iran on 8/3/2016. + */ +public class MessageDialog extends BusDialogFragment { + + + + public int titleRes; + public int messageRes; + + + public static MessageDialog newInstance(int titleRes, int messageRes){ + MessageDialog fragment = new MessageDialog(); + + Bundle data = new Bundle(); + + data.putInt("titleRes", titleRes); + data.putInt("messageRes", messageRes); + + fragment.setArguments(data); + fragment.saveStateToBundle(data); + return fragment; + } + + + @Override + protected boolean sendsRequestAutomatically() { + return false; + } + + @Override + protected void requestInitialData() { + + } + + @Override + public ViewGroup getLoadableView() { + return null; + } + + @Override + public boolean mustShowProgressView() { + return false; + } + + @Override + public int getLayoutResId() { + return LAYOUT_NONE; + } + + @Override + protected void loadStateFromBundle(Bundle savedInstanceState) { + titleRes = getArguments().getInt("titleRes"); + messageRes = getArguments().getInt("messageRes"); + } + + @Override + protected void saveStateToBundle(Bundle outState) { + + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData(titleRes, messageRes, R.string.ok, ConstantsUtils.NO_RESOURCE, ConstantsUtils.NO_RESOURCE); + } + + @Override + protected void initUI(Context context) { + + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/NewCircleDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/NewCircleDialog.java new file mode 100755 index 0000000..a220231 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/NewCircleDialog.java @@ -0,0 +1,44 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.widget.EditText; + +import com.mobsandgeeks.saripaar.annotation.Length; +import com.mobsandgeeks.saripaar.annotation.NotEmpty; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.activity.SettingActivity; +import com.shaya.poinila.android.util.ConstantsUtils; + +import butterknife.Bind; + +import static com.shaya.poinila.android.presentation.view.activity.SettingActivity.SettingType.NEW_CIRCLE; + +/** + * Created by iran on 2015-09-23. + */ +public class NewCircleDialog extends SingleTextFieldDialog { + @Length(max = ConstantsUtils.max_length_circle_name, min = ConstantsUtils.min_length_circle_name, messageResId = R.string.error_circle_name_length) + @Bind(R.id.input_field) public EditText inputField; + + @Override + protected SettingActivity.SettingType getSettingType() { + return NEW_CIRCLE; + } + + @Override + protected void setTextFieldInputMethod() { + inputLayout.setCounterEnabled(true); + inputLayout.setCounterMaxLength(ConstantsUtils.max_length_circle_name); + inputLayout.setHint(getString(R.string.circle_name)); + } + + @Override + protected int getItemPosition() { + return -1; + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData(R.string.new_circle, RESOURCE_NONE, R.string.submit, R.string.cancel, RESOURCE_NONE); + } + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/NewCollectionDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/NewCollectionDialog.java new file mode 100755 index 0000000..25aa6d4 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/NewCollectionDialog.java @@ -0,0 +1,429 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.Bitmap; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.design.widget.TextInputEditText; +import android.support.design.widget.TextInputLayout; +import android.support.v13.app.FragmentCompat; +import android.support.v7.app.AlertDialog; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.EditText; +import android.widget.ImageButton; +import android.widget.Spinner; +import android.widget.TextView; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.CirclesSelectedUIEvent; +import com.shaya.poinila.android.presentation.uievent.PermissionEvent; +import com.shaya.poinila.android.presentation.uievent.SelectImageEvent; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.presentation.view.activity.BaseActivity; +import com.shaya.poinila.android.presentation.view.costom_view.ActivityResultPermissionDelegate.ImagePickerResultPermissionDelegate; +import com.shaya.poinila.android.presentation.view.costom_view.GalleryCameraImagePickerView; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.util.PoinilaPreferences; +import com.shaya.poinila.android.util.ResourceUtils; +import com.shaya.poinila.android.util.StorageUtils; +import com.shaya.poinila.android.util.StringUtils; +import com.squareup.otto.Subscribe; + +import java.util.ArrayList; +import java.util.List; + +import butterknife.Bind; +import butterknife.OnClick; +import data.PoinilaNetService; +import data.event.BaseEvent; +import data.event.TopicsReceivedEvent; +import data.model.Circle; +import data.model.Collection; +import data.model.PrivacyType; +import data.model.Topic; +import manager.DBFacade; +import manager.DataRepository; + +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; +import static com.shaya.poinila.android.util.ConstantsUtils.max_length_collection_description; +import static com.shaya.poinila.android.util.ConstantsUtils.max_length_collection_name; +import static com.shaya.poinila.android.util.ConstantsUtils.min_length_collection_name; + +/** + * Created by iran on 2015-07-15. + */ +public class NewCollectionDialog extends BusDialogFragment implements FragmentCompat.OnRequestPermissionsResultCallback{ + + protected static final String ABSOLUTE_PATH = "absolute path"; + protected static final String MEDIA_PATH = "media path"; + + private static final String TAG_CIRCLE_DIALOG = "circle dialog"; + private static final java.lang.String KEY_EXPANDED = "is expanded"; + @Bind(R.id.caption_field) + TextInputEditText nameField; + @Bind(R.id.checkbox) + CheckBox privateCollectionCheckbox; + @Bind(R.id.privacy_container) + ViewGroup privacyContainer; + @Bind(R.id.select_circle) + Button selectCircleBtn; + @Bind(R.id.select_topic) + Spinner topicSpinner; + + @Bind(R.id.expand) + ImageButton expandButton; + @Bind(R.id.description_field) + TextInputEditText descriptionField; + @Bind(R.id.description_input_layout) + TextInputLayout descriptionInputlayout; + @Bind(R.id.pickerView) + GalleryCameraImagePickerView pickerView; + + private ImagePickerResultPermissionDelegate resultPermissionDelegateIMPL; + + protected boolean topicsReceived; + List mCircles; + CharSequence[] mCircleNames; + boolean[] mCheckedCircles; + protected MySpinnerAdapter mSpinnerAdapter; + private boolean expanded = false; + + @Override + public int getLayoutResId() { + return R.layout.dialog_new_collection; + } + + @Override + protected void loadStateFromBundle(Bundle savedInstanceState) { + expanded = savedInstanceState.getBoolean(KEY_EXPANDED, false); + } + + @Override + protected void saveStateToBundle(Bundle outState) { + + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData(R.string.new_collection, RESOURCE_NONE, R.string.create, R.string.cancel, RESOURCE_NONE); + } + + @Override + protected void initUI(Context context) { + resultPermissionDelegateIMPL = new ImagePickerResultPermissionDelegate() { + @Override + public void handleValidResults(int requestCode, Intent data) { + super.handleValidResults(requestCode, data); + pickerView.setImage(resultPermissionDelegateIMPL.getImageAddress()); + } + + @Override + public void handlePermissionGranted() { + startForResult(NewCollectionDialog.this, + StorageUtils.dispatchCapturePhotoIntent(), + ConstantsUtils.REQUEST_CODE_TAKE_PHOTO); + } + }; + pickerView.policy = GalleryCameraImagePickerView.Policy.FullFeatures; + + mCircles = DBFacade.getMyCircles(); + mCircleNames = new CharSequence[mCircles.size()]; + mCheckedCircles = new boolean[mCircles.size()]; + for (int i = 0; i < mCircleNames.length; i++) { + mCircleNames[i] = mCircles.get(i).name; + } + + privateCollectionCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + selectCircleBtn.setEnabled(isChecked); + setText(selectCircleBtn, + isChecked ? createSelectedCirclesText(mCheckedCircles) : + createSelectedCirclesText(null)); + } + }); + setText(selectCircleBtn, createSelectedCirclesText(null)); + + expandOrCollapseArbitraryFields(expanded); + } + + @Override + public void onPositiveButton() { + // TODO: request to server for creating new collectionSpinner + if (!validate()) + return; + + Bitmap bitmap = null; + if (pickerView.showMode == GalleryCameraImagePickerView.ShowMode.Cropping){ + Logger.toast(R.string.warning_complete_crop); + return; + } + + bitmap = pickerView.getImage(); + + PoinilaNetService.createCollection(PoinilaPreferences.getMyId(), + createCollectionFromFields(), bitmap); + super.onPositiveButton(); + } + + @OnClick(R.id.expand) + public void onExpand() { + expanded ^= true; + expandOrCollapseArbitraryFields(expanded); + } + + private void expandOrCollapseArbitraryFields(boolean expanded) { + View[] toChangeViews = new View[]{descriptionInputlayout, pickerView}; + if (expanded) { + ViewUtils.setViewsVisibilityToVisible(toChangeViews); + } else { + ViewUtils.setViewsVisibilityToGone(toChangeViews); + } + expandButton.setImageResource(expanded ? R.drawable.arrow_up_white_24dp : R.drawable.arrow_down_white_24dp); + } + + + @Override + protected boolean sendsRequestAutomatically() { + return true; + } + + @Override + protected void requestInitialData() { + DataRepository.getInstance().getTopics(); + } + + @Override + public ViewGroup getLoadableView() { + return null; + } + + @Override + public boolean mustShowProgressView() { + return false; + } + + @Subscribe + public void selectImageEvent(SelectImageEvent event) { + resultPermissionDelegateIMPL.startForResult(this, event.intent, event.requestCode); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + resultPermissionDelegateIMPL.onActivityResult(requestCode, resultCode, data); + } + + // I think it's cleaner to ask for permission directly in pickerView + @Subscribe public void askForPermissionEvent(PermissionEvent event){ + resultPermissionDelegateIMPL.askForPermission(this, event.permissionString, BaseActivity.MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + resultPermissionDelegateIMPL.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + //--------------------------- + + protected boolean validate() { + if (!topicsReceived) { + Logger.toastError(R.string.error_select_topic); + return false; + } + + if (pickerView.hasImage()) { + Bitmap bitmap = pickerView.getImageBitmap(); + if (bitmap.getWidth() < ConstantsUtils.MIN_LENGTH_COLLECTION_COVER_DIMENSION || + bitmap.getHeight() < ConstantsUtils.MIN_LENGTH_COLLECTION_COVER_DIMENSION) { + Logger.toastError(R.string.error_small_image); + return false; + } + } + + /*return ViewUtils.validateEntityName(nameField) && + ViewUtils.validateEdittexts( + new EditText[]{nameField}, new int[]{min_length_collection_name}, + new EditText[]{nameField, descriptionField}, new int[]{max_length_collection_name, max_length_collection_description});*/ + + return ViewUtils.validateInputs( + new EditText[]{nameField}, new int[]{min_length_collection_name}, + new EditText[]{nameField, descriptionField}, new int[]{max_length_collection_name, max_length_collection_description}) + && ViewUtils.validateEntityName(nameField); + } + + @Subscribe + public void onTopicReceived(TopicsReceivedEvent event) { + onGettingInitDataResponse(event); + } + + @Override + public void onSuccessfulInitData(BaseEvent baseEvent) { + super.onSuccessfulInitData(baseEvent); + mSpinnerAdapter = new MySpinnerAdapter(getActivity(), ((TopicsReceivedEvent) baseEvent).data); + topicSpinner.setAdapter(mSpinnerAdapter); + topicSpinner.setEnabled(true); + topicsReceived = true; + } + + protected Collection createCollectionFromFields() { + Collection collection = new Collection(); + collection.name = nameField.getText().toString(); + collection.description = descriptionField.getText().toString(); + collection.topic = ((Topic) topicSpinner.getSelectedItem()); + collection.privacy = (privateCollectionCheckbox.isChecked()) ? PrivacyType.PRIVATE : PrivacyType.PUBLIC; + List circleIDs = new ArrayList<>(mCircles.size()); + for (int i = 0; i < mCheckedCircles.length; i++) + if (mCheckedCircles[i]) + circleIDs.add(mCircles.get(i).id); + collection.circleIDs = circleIDs; + return collection; + } + + @OnClick(R.id.select_circle) + public void showCirclesDialog() { + CirclesDialog.newInstance(mCircleNames, mCheckedCircles). + show(getFragmentManager(), TAG_CIRCLE_DIALOG); + } + + @Subscribe + public void onCirclesSelectedEvent(CirclesSelectedUIEvent event) { + mCheckedCircles = event.selectedCircles; + setText(selectCircleBtn, createSelectedCirclesText(mCheckedCircles)); + } + + protected String createSelectedCirclesText(boolean[] checkedCircles) { + if (checkedCircles == null) return ResourceUtils.getString(R.string.visible_to_everyone); + List strings = new ArrayList<>(); + for (int i = 0; i < checkedCircles.length; i++) { + if (checkedCircles[i]) + strings.add(mCircleNames[i].toString()); + } + if (strings.isEmpty()) + return ResourceUtils.getString(R.string.private_for_all); + return StringUtils.join(strings, ", "); + } + + class MySpinnerAdapter extends BaseAdapter { + + List items; + private Context context; + + public MySpinnerAdapter(Context context, List topics) { + this.context = context; + items = topics; + } + + public void addItems(List topics) { + items.addAll(topics); + } + + @Override + public int getCount() { + return items.size(); + } + + @Override + public Object getItem(int position) { + return items.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View spinView; + if (convertView == null) { + LayoutInflater inflater = LayoutInflater.from(context); + spinView = inflater.inflate(R.layout.spinner_item, parent, false); + } else { + spinView = convertView; + } + ((TextView) spinView.findViewById(R.id.spinner_row_title)).setText(((Topic) getItem(position)).name); + return spinView; + } + + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + View spinView; + if (convertView == null) { + LayoutInflater inflater = LayoutInflater.from(context); + spinView = inflater.inflate(R.layout.spinner_item, parent, false); + } else { + spinView = convertView; + } + ((TextView) spinView.findViewById(R.id.spinner_row_title)).setText(((Topic) getItem(position)).name); + return spinView; + } + } + + + public static class CirclesDialog extends android.support.v4.app.DialogFragment { + private static final String KEY_CIRCLE_NAMES = "circles"; + private static final String KEY_CHECKED_ITEMS = "checked"; + + public static CirclesDialog newInstance(CharSequence[] circleNames, boolean[] checkedItems + ) { + CirclesDialog df = new CirclesDialog(); + Bundle b = new Bundle(); + b.putCharSequenceArray(KEY_CIRCLE_NAMES, circleNames); + b.putBooleanArray(KEY_CHECKED_ITEMS, checkedItems); + df.setArguments(b); + return df; + } + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + Bundle b = getArguments(); + CharSequence[] circleNames = b.getCharSequenceArray(KEY_CIRCLE_NAMES); + final boolean[] checkedItems = b.getBooleanArray(KEY_CHECKED_ITEMS); + + + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + // Set the dialog title + builder.setTitle(R.string.select_circle) + // Specify the list array, the items to be selected by default (null for none), + // and the listener through which to receive callbacks when items are selected + .setMultiChoiceItems(circleNames, checkedItems, + new DialogInterface.OnMultiChoiceClickListener() { + @Override + public void onClick(DialogInterface dialog, int which, + boolean isChecked) { + if (isChecked) { + // If the user checked the item, add it to the selected items + checkedItems[which] = true; + } else if (checkedItems[which]) { + // Else, if the item is already in the array, remove it + checkedItems[which] = false; + } + } + }) + // Set the action buttons + .setPositiveButton(R.string.submit, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + BusProvider.getBus().post(new CirclesSelectedUIEvent(checkedItems, -1)); + } + }) + .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + CirclesDialog.this.dismiss(); + } + }); + return builder.create(); + } + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/NewFrameDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/NewFrameDialog.java new file mode 100755 index 0000000..bb7b078 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/NewFrameDialog.java @@ -0,0 +1,44 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.widget.EditText; + +import com.mobsandgeeks.saripaar.annotation.Length; +import com.mobsandgeeks.saripaar.annotation.NotEmpty; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.activity.SettingActivity; +import com.shaya.poinila.android.util.ConstantsUtils; + +import butterknife.Bind; + +import static com.shaya.poinila.android.presentation.view.activity.SettingActivity.SettingType.NEW_FRAME; + +/** + * Created by iran on 2015-09-23. + */ +public class NewFrameDialog extends SingleTextFieldDialog { + @Length(max = ConstantsUtils.max_length_frame_name, min = ConstantsUtils.min_length_frame_name, messageResId = R.string.error_frame_name_length) + @Bind(R.id.input_field) + public EditText inputField; + + @Override + protected SettingActivity.SettingType getSettingType() { + return NEW_FRAME; + } + + @Override + protected void setTextFieldInputMethod() { + inputLayout.setCounterEnabled(true); + inputLayout.setCounterMaxLength(ConstantsUtils.max_length_frame_name); + inputLayout.setHint(getString(R.string.frame_name)); + } + + @Override + protected int getItemPosition() { + return -1; + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData(R.string.new_frame, RESOURCE_NONE, R.string.submit, R.string.cancel, RESOURCE_NONE); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/NewPostDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/NewPostDialog.java new file mode 100755 index 0000000..b8a672d --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/NewPostDialog.java @@ -0,0 +1,595 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.support.annotation.NonNull; +import android.support.design.widget.TextInputEditText; +import android.support.design.widget.TextInputLayout; +import android.support.v13.app.FragmentCompat; +import android.support.v7.widget.AppCompatSpinner; +import android.text.TextUtils; +import android.util.Log; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.EditorInfo; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.EditText; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.RadioButton; +import android.widget.RadioGroup; +import android.widget.TextView; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.PermissionEvent; +import com.shaya.poinila.android.presentation.uievent.RemoveTagEvent; +import com.shaya.poinila.android.presentation.uievent.SelectImageEvent; +import com.shaya.poinila.android.presentation.uievent.UpdateNewPostDialogEvent; +import com.shaya.poinila.android.presentation.view.ViewInflater; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.presentation.view.activity.BaseActivity; +import com.shaya.poinila.android.presentation.view.costom_view.ActivityResultPermissionDelegate; +import com.shaya.poinila.android.presentation.view.costom_view.ActivityResultPermissionDelegate.ImagePickerResultPermissionDelegate; +import com.shaya.poinila.android.presentation.view.costom_view.GalleryCameraImagePickerView; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.util.StorageUtils; +import com.shaya.poinila.android.util.StringUtils; +import com.squareup.otto.Subscribe; + +import org.apmem.tools.layouts.FlowLayout; +import org.parceler.Parcels; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import butterknife.Bind; +import butterknife.ButterKnife; +import butterknife.OnClick; +import data.PoinilaNetService; +import data.model.Collection; +import data.model.ImageUrls; +import data.model.Post; +import data.model.PostType; +import data.model.SuggestedWebPagePost; +import data.model.Tag; +import manager.DBFacade; + +import static com.shaya.poinila.android.presentation.view.ViewUtils.setValueAndDisableInputLayout; +import static com.shaya.poinila.android.presentation.view.ViewUtils.validateEntityName; +import static com.shaya.poinila.android.presentation.view.ViewUtils.validateInputs; +import static com.shaya.poinila.android.presentation.view.costom_view.GalleryCameraImagePickerView.Policy.NoFeature; +import static com.shaya.poinila.android.util.ConstantsUtils.NO_RESOURCE; +import static com.shaya.poinila.android.util.ConstantsUtils.max_length_image_post_summary; +import static com.shaya.poinila.android.util.ConstantsUtils.max_length_image_post_title; +import static com.shaya.poinila.android.util.ConstantsUtils.max_length_tag; +import static com.shaya.poinila.android.util.ConstantsUtils.max_length_text_post_content; +import static com.shaya.poinila.android.util.ConstantsUtils.max_length_text_post_summary; +import static com.shaya.poinila.android.util.ConstantsUtils.max_length_text_post_title; +import static com.shaya.poinila.android.util.ConstantsUtils.min_length_tag; +import static com.shaya.poinila.android.util.ConstantsUtils.min_length_text_post_content; +import static com.shaya.poinila.android.util.ConstantsUtils.min_length_text_post_title; + +/** + * Created by iran on 2015-08-26. + */ +public class NewPostDialog extends BusDialogFragment implements FragmentCompat.OnRequestPermissionsResultCallback{ + private static final String KEY_WEBSITE_POST = "website post"; + private static final java.lang.String KEY_IS_IMAGED_POST = "is imaged post"; + private static final String KEY_IS_FROM_WEB = "is from web"; + protected static final String KEY_IS_EXPANDED = "is expanded"; + //AbstractCreateDialogPost extends AbstractDialogContent { + @Bind(R.id.post_type_container) + RadioGroup postTypeContainer; + @Bind(R.id.image_radio_btn) + protected RadioButton image; + @Bind(R.id.text_radio_btn) + protected RadioButton text; + + @Bind(R.id.tag_field) + protected EditText tagField; + @Bind(R.id.tags_flowlayout) + protected FlowLayout tagsFlowLayout; + @Bind(R.id.tags_container) + protected ViewGroup tagsContainer; + + @Bind(R.id.select_collection) + protected AppCompatSpinner collectionSpinner; + + @Bind(R.id.expand) + ImageButton expandButton; + + @Bind(R.id.caption_field) + TextInputEditText name; + @Bind(R.id.summary_field) + TextInputEditText summary; + @Bind(R.id.caption_input_layout) + TextInputLayout titleInputLayout; + @Bind(R.id.summary_input_layout) + TextInputLayout summaryInputLayout; + + protected List tags; + + /*-------image----*/ + @Bind(R.id.pickerView) + GalleryCameraImagePickerView pickerView; + + /*-----text------*/ + @Bind(R.id.content_field) + TextInputEditText contentInput; + @Bind(R.id.content_input_layout) + TextInputLayout contentInputLayout; + + // state variables + private SuggestedWebPagePost suggestedPost; + protected boolean isImaged = true; + private boolean isFromWeb = false; + protected boolean expanded; + private ActivityResultPermissionDelegate.ImagePickerResultPermissionDelegate resultPermissionDelegateIMPL; + + MySpinnerAdapter mSpinnerAdapter; + + + public static NewPostDialog newInstance(SuggestedWebPagePost suggestedPost) { + // TODO: use Parceler library later but read https://guides.codepath.com/android/Must-Have-Libraries first + NewPostDialog fragment = new NewPostDialog(); + Bundle args = new Bundle(); + + // kasif o lus! + fragment.suggestedPost = suggestedPost; + + fragment.saveStateToBundle(args); + fragment.setArguments(args); + return fragment; + } + + @Override + public void saveStateToBundle(Bundle outState) { + outState.putParcelable(KEY_WEBSITE_POST, Parcels.wrap(suggestedPost)); + outState.putBoolean(KEY_IS_FROM_WEB, suggestedPost != null); + + outState.putBoolean(KEY_IS_IMAGED_POST, isImaged); + outState.putBoolean(KEY_IS_EXPANDED, expanded); + } + + @Override + protected void loadStateFromBundle(Bundle savedStateBundle) { + suggestedPost = Parcels.unwrap(savedStateBundle.getParcelable(KEY_WEBSITE_POST)); + isFromWeb = suggestedPost != null; + + isImaged = savedStateBundle.getBoolean(KEY_IS_IMAGED_POST); + expanded = savedStateBundle.getBoolean(KEY_IS_EXPANDED); + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData(R.string.new_post, NO_RESOURCE, R.string.create, R.string.cancel, NO_RESOURCE); + } + + @Override + protected void initUI(Context context) { + resultPermissionDelegateIMPL = new ImagePickerResultPermissionDelegate() { + @Override + public void handleValidResults(int requestCode, Intent data) { + super.handleValidResults(requestCode, data); + pickerView.setImage(resultPermissionDelegateIMPL.getImageAddress()); + } + + @Override + public void handlePermissionGranted() { + startForResult(NewPostDialog.this, + StorageUtils.dispatchCapturePhotoIntent(), + ConstantsUtils.REQUEST_CODE_TAKE_PHOTO); + } + }; + + sharedInitUIBetweenRepostAndNewPost(); + + postTypeContainer.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(RadioGroup group, int checkedId) { + switch (checkedId) { + case R.id.image_radio_btn: + onImageType(); + break; + case R.id.text_radio_btn: + onTextType(); + break; + } + } + }); + + if (isFromWeb) { + postTypeContainer.setVisibility(View.GONE); + if (TextUtils.isEmpty(suggestedPost.imageAddress)) + onTextType(); + else + onImageType(); + fillViewsWithValues(suggestedPost); + } + + expandOrCollapseArbitraryFields(expanded); + } + + protected void sharedInitUIBetweenRepostAndNewPost() { + tags = new ArrayList<>(); + mSpinnerAdapter = new MySpinnerAdapter(getActivity(), DBFacade.getMyCollections()); + collectionSpinner.setAdapter(mSpinnerAdapter); + collectionSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView adapterView, View view, int position, long l) { + switch (position){ + case 1: + new NewCollectionDialog().show(getFragmentManager(), null); + break; + } + } + + @Override + public void onNothingSelected(AdapterView adapterView) { + + } + }); + pickerView.policy = NoFeature; + if (tagField != null) { + tagField.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (actionId == EditorInfo.IME_ACTION_DONE) { + addTagIfValid(tagField, true); + return true; + } + return false; // ?? + } + }); + } + } + + protected void fillViewsWithValues(Post post, boolean disableNonEditableFields) { + if (post.type == PostType.IMAGE) { + if (TextUtils.isEmpty(post.name) && !isFromWeb) { + titleInputLayout.setVisibility(View.GONE); + } else { + setValueAndDisableInputLayout(titleInputLayout, post.name, disableNonEditableFields); + } + + pickerView.setVisibility(View.VISIBLE); + if (!pickerView.hasImage() && !isFromWeb) // will be set for web post in different way + pickerView.setImage(post.imagesUrls.properPostImage(ImageUrls.ImageSize.BIG).url); + + contentInputLayout.setVisibility(View.GONE); + } else { // Text Post + setValueAndDisableInputLayout(titleInputLayout, post.name, disableNonEditableFields); + setValueAndDisableInputLayout(contentInputLayout, post.content, disableNonEditableFields); + // baraye etiminan! + pickerView.setVisibility(View.GONE); + } + + summary.setText(post.summary); + + for (Tag tag : post.tags) { + ViewInflater.addRemovableTagToContainer(tagsFlowLayout, tag.name); + tags.add(tag); + } + } + + private void fillViewsWithValues(SuggestedWebPagePost suggestedPost) { + fillViewsWithValues(new Post(suggestedPost.name, suggestedPost.summary, suggestedPost.content, suggestedPost.tags), false); + if (!TextUtils.isEmpty(suggestedPost.imageAddress)) + pickerView.setImage(suggestedPost.imageAddress); + } + + private void addTagIfValid(EditText tagField, boolean makeToast) { + EditText[] editTexts = new EditText[]{tagField}; + if (validateEntityName(tagField) && + validateInputs(editTexts, new int[]{min_length_tag}, editTexts, new int[]{max_length_tag})) { + addTag(tagField.getText().toString()); + } + } + + private void addTag(String tagString) { + Tag newTag = new Tag(-1, tagString); + addTagViewToContainer(newTag.name); + tags.add(newTag); + tagField.setText(""); + } + + public void addTagViewToContainer(String tagText) { + ViewInflater.addRemovableTagToContainer(tagsFlowLayout, tagText); + } + + @Subscribe + public void removeTag(final RemoveTagEvent event) { + tags.remove(event.adapterPosition); + new Handler().postAtFrontOfQueue(new Runnable() { + @Override + public void run() { + tagsFlowLayout.removeViewAt(event.adapterPosition); //ButterKnife.findById(container, R.actorID.dialog_content) + } + }); + } + + @Subscribe + public void onUpdateCollectionSpinner(UpdateNewPostDialogEvent event){ + mSpinnerAdapter.addItem((Collection)event.model); + collectionSpinner.setSelection(mSpinnerAdapter.getCount() - 1); + mSpinnerAdapter.notifyDataSetChanged(); + } + + + class MySpinnerAdapter extends BaseAdapter { + + private final int SPINNER_ITEM_TYPE = 0; + private final int SPINNER_PROMPT_TYPE = 1; + + List items; + private Context context; + + public MySpinnerAdapter(Context context, List collections) { + this.context = context; + items = new ArrayList<>(); + Collection defaultItem = new Collection(); + defaultItem.name = "---"; + Collection createNew = new Collection(); + createNew.name = getString(R.string.create_new_collection); + items.add(defaultItem); + items.add(createNew); + items.addAll(collections); + } + + public void addItems(List Collections) { + /*for (Collection Collection : mCircles) + items.add(Collection);*/ + items.addAll(Collections); + } + + public void addItem(Collection collection){ + items.add(collection); + } + + @Override + public int getCount() { + return items.size(); + } + + @Override + public Object getItem(int position) { + return items.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + return getSpinnerView(position, convertView, parent); + } + + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + + return getSpinnerView(position, convertView, parent); + } + + private View getSpinnerView(int position, View convertView, ViewGroup parent){ + View spinView; + spinView = LayoutInflater.from(context).inflate(R.layout.spinner_item, parent, false); + ((TextView) spinView.findViewById(R.id.spinner_row_title)).setText(((Collection) getItem(position)).name); + + return spinView; + } + } + + /*--------------New form----------------*/ + + protected Post createPostFromFields() { + Post post = new Post(); + post.name = name.getText().toString(); + post.summary = summary.getText().toString().replaceAll("\\n", "\n"); + post.tags = tags; + if (!isImaged) { + // Edit text removes nextlines, tabs etc. we insert them explicitly. + // Html.toHtml() creates a

tag with direction attribute which textView doesn't support! + post.content = StringUtils.removeHtmlDirAttribute( + //Html.toHtml(Html.fromHtml( + contentInput.getText().toString().replaceAll("\\n", "\n")); + //)); + } + return post; + } + + @Override + public void onPositiveButton() { + + if (!validate()) + return; + + if(collectionSpinner == null + || collectionSpinner.getCount() == 0 + || collectionSpinner.getSelectedItemPosition() <= 1){ + Logger.toastError(R.string.error_select_collection); + return; + } + + String collectionID = ((Collection) collectionSpinner.getSelectedItem()).getId(); + Post newPost = createPostFromFields(); + /*image*/ + if (isFromWeb) { + PoinilaNetService.createReferencedPost(collectionID, newPost, suggestedPost.siteAddress, suggestedPost.imageAddress, suggestedPost.videoAddress); + getActivity().finish(); + } else if (isImaged) { + //TODO: + PoinilaNetService.createImagePost(collectionID, pickerView.getImage(), newPost); + } else { // normal text post + PoinilaNetService.createTextPost(collectionID, newPost); + } +// onNegativeButton(); + dismiss(); + } + + @Override + public void onNegativeButton() { + super.onNegativeButton(); + if(collectionSpinner.getSelectedItemPosition() == 1) + collectionSpinner.setSelection(0); + } + + // image post views order: MANDATORY: image, summary, collection | ARBITRARY: tags, name | HIDE: content + // text post views order: MANDATORY: name, content, collection | ARBITRARY: tags, summary | HIDE: image + // default order for union of text and image post: + // title, content, image, collection, tags, summary + public void onTextType() { + isImaged = false; + updateViews(PostType.TEXT); + titleInputLayout.setCounterMaxLength(getResources().getInteger(R.integer.max_length_text_post_title)); + summaryInputLayout.setCounterMaxLength(getResources().getInteger(R.integer.max_length_text_post_summary)); + } + + public void onImageType() { + isImaged = true; + updateViews(PostType.IMAGE); + titleInputLayout.setCounterMaxLength(getResources().getInteger(R.integer.max_length_image_post_title)); + titleInputLayout.setCounterMaxLength(getResources().getInteger(R.integer.max_length_image_post_summary)); + } + + + private void updateViews(PostType postType) { + expanded = false; + expandOrCollapseArbitraryFields(expanded); + final LinearLayout ll = getLLContainer(); + + final List toHideViews = (postType == PostType.TEXT) ? + Arrays.asList(tagsContainer, summaryInputLayout) : + Arrays.asList(tagsContainer, titleInputLayout); + final List toVisibleViews = (postType == PostType.TEXT) ? + Arrays.asList(titleInputLayout, contentInputLayout) : + Arrays.asList(pickerView, summaryInputLayout); + + // we add all views that must be shown every time from scratch. + final List toShowViews = (postType == PostType.TEXT) ? + Arrays.asList(postTypeContainer, titleInputLayout, contentInputLayout, collectionSpinner, expandButton, tagsContainer, summaryInputLayout) : + Arrays.asList(postTypeContainer, pickerView, summaryInputLayout, collectionSpinner, expandButton, tagsContainer, titleInputLayout); + + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + ll.removeAllViews(); + ViewUtils.setViewsVisibilityToGone(toHideViews); + ViewUtils.setViewsVisibilityToVisible(toVisibleViews); + for (View child : toShowViews) { + ll.addView(child); + } + } + }); + } + + private LinearLayout getLLContainer() { + return ButterKnife.findById(rootView, R.id.dialog_content); + } + + @OnClick(R.id.expand) + public void onExpand() { + expanded ^= true; + expandOrCollapseArbitraryFields(expanded); + } + + private void expandOrCollapseArbitraryFields(boolean expanded) { + View[] toChangeViews = isImaged ? new View[]{tagsContainer, titleInputLayout} : /*text post */ new View[]{tagsContainer, summaryInputLayout}; + if (expanded) { + ViewUtils.setViewsVisibilityToVisible(toChangeViews); + } else { + ViewUtils.setViewsVisibilityToGone(toChangeViews); + } + expandButton.setImageResource(expanded ? R.drawable.arrow_up_white_24dp : R.drawable.arrow_down_white_24dp); + } + + /*---------image----------*/ + + protected boolean validate() { + EditText[] requiredItems; + EditText[] limitedItems; + int[] maxLengths, minLengths; + if (isImaged) { + requiredItems = new EditText[]{}; + limitedItems = new EditText[]{name, summary}; + minLengths = new int[]{};//min_length_image_post_summary}; + maxLengths = new int[]{max_length_image_post_title, max_length_image_post_summary}; + } else { + requiredItems = new EditText[]{name, contentInput}; + limitedItems = new EditText[]{name, summary, contentInput}; + minLengths = new int[]{min_length_text_post_title, min_length_text_post_content}; + maxLengths = new int[]{max_length_text_post_title, max_length_text_post_summary, max_length_text_post_content}; + } + + if (isImaged) { + if (!pickerView.hasImage()) { + Logger.toast(R.string.error_imagepost_without_image); + return false; + } else { + Bitmap bitmap = pickerView.getImageBitmap(); + if (bitmap.getWidth() < ConstantsUtils.MINIMUM_POST_IMAGE_WIDTH || + bitmap.getHeight() < ConstantsUtils.MINIMUM_POST_IMAGE_HEIGHT) { + Logger.toastError(R.string.error_small_image); + return false; + } + } + } + return validateInputs(requiredItems, minLengths, limitedItems, maxLengths); + } + + + @Subscribe + public void selectImageEvent(SelectImageEvent event) { + resultPermissionDelegateIMPL.startForResult(this, event.intent, event.requestCode); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + resultPermissionDelegateIMPL.onActivityResult(requestCode, resultCode, data); + } + + // I think it's cleaner to ask for permission directly in pickerView + @Subscribe public void askForPermissionEvent(PermissionEvent event){ + resultPermissionDelegateIMPL.askForPermission(this, event.permissionString, BaseActivity.MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + resultPermissionDelegateIMPL.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + + /*not important implemented methods*/ + @Override + protected boolean sendsRequestAutomatically() { + return false; + } + + @Override + protected void requestInitialData() { + } + + @Override + public ViewGroup getLoadableView() { + return null; + } + + @Override + public boolean mustShowProgressView() { + return false; + } + + @Override + public int getLayoutResId() { + return R.layout.dialog_new_post; + } + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/NewWebsitePostDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/NewWebsitePostDialog.java new file mode 100755 index 0000000..de40309 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/NewWebsitePostDialog.java @@ -0,0 +1,356 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.content.Context; +import android.content.DialogInterface; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.support.annotation.Nullable; +import android.support.design.widget.TextInputEditText; +import android.support.design.widget.TextInputLayout; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.StaggeredGridLayoutManager; +import android.util.Log; +import android.util.Patterns; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.EditorInfo; +import android.widget.ImageButton; +import android.widget.ProgressBar; +import android.widget.RadioButton; +import android.widget.RadioGroup; +import android.widget.TextView; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewProvider; +import com.shaya.poinila.android.presentation.uievent.ImageClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.NewWebsitePostEvent; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.presentation.view.costom_view.AspectRatioImageView; +import com.shaya.poinila.android.presentation.view.help.Help; +import com.shaya.poinila.android.presentation.viewholder.SingleImageViewHolder; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.util.PoinilaPreferences; +import com.shaya.poinila.android.util.ResourceUtils; +import com.squareup.otto.Subscribe; +import com.squareup.picasso.Picasso; +import com.squareup.picasso.Target; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import butterknife.Bind; +import butterknife.OnClick; +import data.PoinilaNetService; +import data.event.BaseEvent; +import data.event.LoadingImagedFailedEvent; +import data.event.SuggestedWebpagePostReceived; +import data.model.Image; +import data.model.PostType; +import data.model.SuggestedWebPagePost; + +/** + * Created by iran on 2015-07-15. + */ +public class NewWebsitePostDialog extends BusDialogFragment { + private static final String KEY_SITE_ADDRESS = "site address"; + @Bind(R.id.image_radio_btn) + protected RadioButton image; + @Bind(R.id.text_radio_btn) + protected RadioButton text; + @Bind(R.id.post_type_container) + RadioGroup postTypeContainer; + + @Bind(R.id.url_field) + TextInputEditText urlField; + @Bind(R.id.go_btn) + ImageButton goBtn; + @Bind(R.id.recycler_view) + RecyclerView mRecyclerView; + @Bind(R.id.progress_bar) + ProgressBar progressWheel; + @Bind(R.id.url_textinputlayout) + TextInputLayout urlInputLayout; + RecyclerViewAdapter mRecyclerViewAdapter; + String[] protocols = {"http", "https"}; + List validProtocols = Arrays.asList(protocols); + private String siteAddress; + protected boolean isImaged = true; + + // picasso doesn't hold a reference to `Target`s. So during download, they are being garbage collected. + // We hold a strong reference to array of targets just to avoid that + List targets; + private SuggestedWebPagePost suggestedPost; + + @Override + public int getLayoutResId() { + return R.layout.dialog_new_website_post; + } + + @Override + protected void initUI(Context context) { + targets = new ArrayList<>(); + + // TODO: parse url and extract imagesUrls greater than a specific size in each dimension + mRecyclerView = new RecyclerViewProvider(mRecyclerView). + setStaggeredLayoutManager(StaggeredGridLayoutManager.VERTICAL, + ResourceUtils.getInteger(R.integer.column_count)). + setAdapter(new RecyclerViewAdapter>(context, R.layout.single_image_staggered) { + @Override + protected SingleImageViewHolder getProperViewHolder(View v, int viewType) { + return new SingleImageViewHolder(v) { + @Override + public void fill(Image image) { + ((AspectRatioImageView) imageView).setAspectRatio(image.height * 1f / image.width); + imageView.requestLayout(); + Picasso.with(imageView.getContext()).load(image.url).into(imageView); + } + }; + } + }).bindViewToAdapter(); + mRecyclerViewAdapter = (RecyclerViewAdapter) mRecyclerView.getAdapter(); + + urlInputLayout.setHint(getString(R.string.hint_website_url)); + urlField.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (actionId == EditorInfo.IME_ACTION_SEARCH) { + fetchSiteInfo(); + return true; + } + return false; // ?? + } + }); + + postTypeContainer.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(RadioGroup group, int checkedId) { + switch (checkedId) { + case R.id.image_radio_btn: + isImaged = true; + mRecyclerView.setVisibility(View.VISIBLE); + + break; + case R.id.text_radio_btn: + isImaged = false; + mRecyclerView.setVisibility(View.GONE); + break; + } + } + }); + + + + + } + + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + + if(!PoinilaPreferences.getHelpStatus(this.getClass().getName())){ + getDialog().setOnShowListener(new DialogInterface.OnShowListener() { + @Override + public void onShow(DialogInterface dialog) { + Help.getInstance().showNewPostHelp(getActivity(), urlField); + PoinilaPreferences.putHelpStatus(NewWebsitePostDialog.this.getClass().getName(), true); + } + }); + + } + + + return super.onCreateView(inflater, container, savedInstanceState); + } + + @Override + public void setUserVisibleHint(boolean isVisibleToUser) { + super.setUserVisibleHint(isVisibleToUser); + } + + @Subscribe + public void urlsReceivedEvent(SuggestedWebpagePostReceived event) { + onGettingInitDataResponse(event); + } + + @Override + public void onSuccessfulInitData(BaseEvent baseEvent) { + super.onSuccessfulInitData(baseEvent); + showGoBtn(false); + mRecyclerViewAdapter.clear(); + suggestedPost = ((SuggestedWebpagePostReceived) baseEvent).webpagePost; + suggestedPost.siteAddress = this.siteAddress; + feedback(); + if (!isImaged) + return; + for (final Image image : suggestedPost.images) { + MeasureTarget target = new MeasureTarget(image.url); + targets.add(target); + Picasso.with(getActivity()).load(image.url).into(target); + } + } + + private void feedback() { + // TODO: + if (isImaged && suggestedPost.images.isEmpty()) { + Logger.toast(R.string.error_no_image_found); + } + if (!isImaged && (suggestedPost.name != null || suggestedPost.summary != null)) { + Logger.toast(R.string.successfully_loaded); + } + } + + @OnClick(R.id.go_btn) + void fetchSiteInfo() { + targets.clear(); + siteAddress = urlField.getText().toString().trim(); + suggestedPost = null; + if (isAddressValid(siteAddress)) { + showProgressBar(); + PoinilaNetService.getWebsiteInfo(siteAddress, isImaged ? PostType.IMAGE : PostType.TEXT); + } + } + + @Subscribe + public void loadingFailed(LoadingImagedFailedEvent event) { + showGoBtn(true); + } + + private void showProgressBar() { + progressWheel.setVisibility(View.VISIBLE); + goBtn.setVisibility(View.INVISIBLE); + //progressWheel.; + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + if (progressWheel != null && progressWheel.getVisibility() == View.VISIBLE) { + showGoBtn(true); + } + } + }, ConstantsUtils.READ_TIME_OUT_MILLISECONDS); + } + + private void showGoBtn(boolean failed) { + //progressWheel.stopSpinning(); + progressWheel.setVisibility(View.INVISIBLE); + goBtn.setVisibility(View.VISIBLE); + if (failed) Logger.toast(R.string.error_loading_images); + } + + private boolean isAddressValid(String address) { + String error; + Uri uri = Uri.parse(address); + + if (Patterns.WEB_URL.matcher(uri.toString()).matches()) { // trim is essential. + String protocol = uri.getScheme(); + if (protocol == null) { + protocol = "http"; + setSiteAddress("http://" + address); + } + if (!validProtocols.contains(protocol)) { + error = getString(R.string.wrong_protocol); + } else { + setSiteAddress(new Uri.Builder().scheme(uri.getScheme()). + authority(uri.getAuthority()). + path(uri.getPath()). + query(uri.getQuery()).build().toString()); + urlInputLayout.setErrorEnabled(false); + return true; + } + } else { + error = getString(R.string.error_invalid_url); + } + ViewUtils.setInputError(urlField, error); + return false; + } + + @Subscribe + public void onSiteImageClickEvent(ImageClickedUIEvent event) { + suggestedPost.imageAddress = mRecyclerViewAdapter.getItem(event.adapterPosition).url; + BusProvider.getBus().post(new NewWebsitePostEvent(suggestedPost)); + onNegativeButton(); + } + + @Override + public void onPositiveButton() { + if (suggestedPost == null) { + Logger.toast(R.string.error_page_info_not_loaded); + return; + } + BusProvider.getBus().post(new NewWebsitePostEvent(suggestedPost)); + super.onNeutralButton(); + } + + @Override + protected void loadStateFromBundle(Bundle savedInstanceState) { + siteAddress = savedInstanceState.getString(ConstantsUtils.KEY_WEBSITE_URL, null); + } + + @Override + protected void saveStateToBundle(Bundle outState) { + outState.putString(KEY_SITE_ADDRESS, siteAddress); + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData(R.string.website_post, RESOURCE_NONE, R.string.create_text_post, R.string.cancel, RESOURCE_NONE); + } + + public void setSiteAddress(String siteAddress) { + this.siteAddress = siteAddress; + } + + @Override + protected boolean sendsRequestAutomatically() { + return false; + } + + @Override + protected void requestInitialData() { + + } + + @Override + public ViewGroup getLoadableView() { + return null; + } + + @Override + public boolean mustShowProgressView() { + return false; + } + + + class MeasureTarget implements Target { + private final String address; + + public MeasureTarget(String address) { + this.address = address; + } + + @Override + public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { + if (bitmap.getWidth() > ConstantsUtils.MINIMUM_POST_IMAGE_WIDTH && + bitmap.getHeight() > ConstantsUtils.MINIMUM_POST_IMAGE_HEIGHT) { + mRecyclerViewAdapter.addItem(new Image(address, bitmap.getWidth(), bitmap.getHeight())); + Log.w("poinila_image", String.format("width: %d, height: %d", bitmap.getWidth(), bitmap.getHeight())); + } + } + + @Override + public void onBitmapFailed(Drawable errorDrawable) { + } + + @Override + public void onPrepareLoad(Drawable placeHolderDrawable) { + } + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/PoinilaAlertDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/PoinilaAlertDialog.java new file mode 100755 index 0000000..619578e --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/PoinilaAlertDialog.java @@ -0,0 +1,162 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.content.Context; +import android.inputmethodservice.KeyboardView; +import android.os.Bundle; +import android.support.annotation.StringRes; +import android.view.View; + +import com.shaya.poinila.android.presentation.uievent.NeutralDialogButtonClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.PositiveButtonClickedUIEvent; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.ResourceUtils; + +/** + * Created by iran on 12/22/2015. + */ +public abstract class PoinilaAlertDialog extends BaseDialogFragment{ + + + private View.OnClickListener positiveBtnClickListener; + + @Override + public int getLayoutResId() { + return LAYOUT_NONE; + } + + @Override + protected void loadStateFromBundle(Bundle savedInstanceState) { + + } + + @Override + protected void saveStateToBundle(Bundle outState) { + + } + + @Override + public void onPositiveButton() { + if (positiveBtnClickListener == null) + BusProvider.getBus().post(new PositiveButtonClickedUIEvent()); + else + positiveBtnClickListener.onClick(null); + super.onPositiveButton(); + } + + @Override + public void onNeutralButton() { + BusProvider.getBus().post(new NeutralDialogButtonClickedUIEvent()); + super.onNeutralButton(); + } + + @Override + protected void initUI(Context context) { + + } + + public void setOnPositiveClickListener(View.OnClickListener onPositiveClickListener) { + this.positiveBtnClickListener = onPositiveClickListener; + } + + public static class Builder{ + String title; + String message; + String positiveBtnText; + String negativeBtnText; + String neutralBtnText; + View.OnClickListener positiveBtnClickListener; + + public Builder setTitle(String title) { + this.title = title; + return this; + } + + public Builder setMessage(String message) { + this.message = message; + return this; + } + + public Builder setPositiveBtnText(String positiveBtnText) { + this.positiveBtnText = positiveBtnText; + return this; + } + + public Builder setNegativeBtnText(String negativeBtnText) { + this.negativeBtnText = negativeBtnText; + return this; + } + + public Builder setNeutralBtnText(String neutralBtnText) { + this.neutralBtnText = neutralBtnText; + return this; + } + + public Builder setTitle(@StringRes int titleRes) { + return setTitle(ResourceUtils.getString(titleRes)); + } + + public Builder setMessage(@StringRes int messageRes) { + return setMessage(ResourceUtils.getString(messageRes)); + } + + public Builder setPositiveBtnText(@StringRes int positiveBtnText) { + return setPositiveBtnText(ResourceUtils.getString(positiveBtnText)); + } + + public Builder setNegativeBtnText(@StringRes int negativeBtnText) { + return setNegativeBtnText(ResourceUtils.getString(negativeBtnText)); + } + + public Builder setNeutralBtnText(@StringRes int neutralBtnText) { + return setNeutralBtnText(ResourceUtils.getString(neutralBtnText)); + } + + public Builder setPositiveBtnClickListener(View.OnClickListener listener){ + this.positiveBtnClickListener = listener; + return this; + } + + public PoinilaAlertDialog build() { + PoinilaAlertDialog pad = CustomPonilaAlertDialog.newInstance(title, message, positiveBtnText, negativeBtnText, neutralBtnText, positiveBtnClickListener); + pad.setOnPositiveClickListener(positiveBtnClickListener); + return pad; + } + } + + + public static class CustomPonilaAlertDialog extends PoinilaAlertDialog{ + + String title; + String message; + String positiveBtnText; + String negativeBtnText; + String neutralBtnText; + View.OnClickListener positiveBtnClickListener; + + public static CustomPonilaAlertDialog newInstance(String title, + String message, + String positiveBtnText, + String negativeBtnText, + String neutralBtnText, + View.OnClickListener positiveBtnClickListener){ + + CustomPonilaAlertDialog fragment = new CustomPonilaAlertDialog(); + + fragment.title = title; + fragment.message = message; + fragment.positiveBtnText = positiveBtnText; + fragment.negativeBtnText = negativeBtnText; + fragment.neutralBtnText = neutralBtnText; + fragment.positiveBtnClickListener = positiveBtnClickListener; + + + return fragment; + + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData(title, message, positiveBtnText, negativeBtnText, neutralBtnText); + } + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/PoinilaInviteDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/PoinilaInviteDialog.java new file mode 100755 index 0000000..6b6aafa --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/PoinilaInviteDialog.java @@ -0,0 +1,49 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.content.Context; +import android.os.Bundle; +import android.widget.EditText; + +import com.shaya.poinila.android.presentation.R; + +import butterknife.Bind; +import data.PoinilaNetService; + +/** + * Created by iran on 2015-10-01. + */ +public class PoinilaInviteDialog extends BaseDialogFragment{ + @Bind(R.id.email_input) EditText emailInput; + + @Bind(R.id.message) EditText messageInput; + + @Override + public int getLayoutResId() { + return R.layout.dialog_invite_to_poinila; + } + + @Override + protected void loadStateFromBundle(Bundle savedInstanceState) { + } + + @Override + protected void saveStateToBundle(Bundle outState) { + + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData(R.string.invite_to_poinila, RESOURCE_NONE, R.string.send, R.string.cancel, RESOURCE_NONE); + } + + @Override + protected void initUI(Context context) { + + } + + @Override + public void onPositiveButton() { + PoinilaNetService.inviteToPoinila(emailInput.getText().toString(), messageInput.getText().toString()); + super.onPositiveButton(); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ReportDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ReportDialog.java new file mode 100755 index 0000000..ffc6dee --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/ReportDialog.java @@ -0,0 +1,98 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.content.Context; +import android.os.Bundle; +import android.view.ViewGroup; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.util.Logger; + +import data.PoinilaNetService; +import data.model.Collection; + +import static com.shaya.poinila.android.util.ConstantsUtils.NO_RESOURCE; + +/** + * Created by iran on 7/5/2016. + */ +public class ReportDialog extends BusDialogFragment { + + private int title; + private int memberIdOrPostId; + + + public ReportDialog(){ + + } + + public static ReportDialog newInstance(int title, int memberIdOrPostId){ + ReportDialog fragment = new ReportDialog(); + Bundle args = new Bundle(); + args.putInt("title", title); + args.putInt("memberIdOrPostId", memberIdOrPostId); + fragment.setArguments(args); + return fragment; + + } + + + @Override + protected boolean sendsRequestAutomatically() { + return false; + } + + @Override + protected void requestInitialData() { + + } + + @Override + public ViewGroup getLoadableView() { + return null; + } + + @Override + public boolean mustShowProgressView() { + return false; + } + + @Override + public int getLayoutResId() { + return R.layout.dialog_report; + } + + @Override + protected void loadStateFromBundle(Bundle savedInstanceState) { + + } + + @Override + protected void saveStateToBundle(Bundle outState) { + + } + + @Override + public void onPositiveButton() { + PoinilaNetService.reportMemberOrPost(memberIdOrPostId); + onNegativeButton(); + + Logger.toast(R.string.report_successful_message); + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData( + getArguments().getInt("title"), + NO_RESOURCE, + R.string.report, + R.string.cancel, + NO_RESOURCE + ); + } + + @Override + protected void initUI(Context context) { + memberIdOrPostId = getArguments().getInt("memberIdOrPostId"); + } + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/RepostDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/RepostDialog.java new file mode 100755 index 0000000..e82c5f0 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/RepostDialog.java @@ -0,0 +1,153 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.content.Context; +import android.os.Bundle; +import android.view.View; +import android.widget.EditText; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.RemoveTagEvent; +import com.shaya.poinila.android.presentation.uievent.UpdateUiRepostEvent; +import com.shaya.poinila.android.util.BusProvider; +import com.squareup.otto.Subscribe; + +import org.parceler.Parcels; + +import data.PoinilaNetService; +import data.model.Collection; +import data.model.Post; +import data.model.PostType; + +import static com.shaya.poinila.android.presentation.view.ViewUtils.validateInputs; +import static com.shaya.poinila.android.util.ConstantsUtils.NO_RESOURCE; +import static com.shaya.poinila.android.util.ConstantsUtils.max_length_image_post_summary; +import static com.shaya.poinila.android.util.ConstantsUtils.max_length_text_post_summary; + +/** + * Created by iran on 2015-10-07. + */ +public class RepostDialog extends NewPostDialog{ + + private static final String KEY_POST = "initial post"; + private Post post; + + public static RepostDialog newInstance(Post post) { + Bundle args = new Bundle(); + RepostDialog fragment = new RepostDialog(); + args.putParcelable(KEY_POST, Parcels.wrap(post)); + fragment.setArguments(args); + return fragment; + } + + @Override + protected void loadStateFromBundle(Bundle savedStateBundle) { + super.loadStateFromBundle(savedStateBundle); + post = Parcels.unwrap(savedStateBundle.getParcelable(KEY_POST)); + expanded = savedStateBundle.getBoolean(KEY_IS_EXPANDED); + } + + @Override + public void saveStateToBundle(Bundle outState) { + outState.putParcelable(KEY_POST, Parcels.wrap(createPostFromFields())); + outState.putBoolean(KEY_IS_EXPANDED, expanded); + } + + + + /*private void setDefaultValues(Post post) { + if (post.type == PostType.IMAGE){ + isImaged = true; + if (TextUtils.isEmpty(post.name)){ + name.setVisibility(View.GONE); + }else { + setAndDisableName(); + } + pickerView.setVisibility(View.VISIBLE); + if (!pickerView.hasImage()) pickerView.setImage(post.imagesUrls.properPostImage(BIG).url); + + content.setVisibility(View.GONE); + }else { // Text Post + isImaged = false; + setAndDisableName(); + setAndDisableContent(); + // baraye etiminan! + pickerView.setVisibility(View.GONE); + } + + summary.setText(post.summary); + + for (Tag tag : post.tags) { + ViewInflater.addRemovableTagToContainer(tagsFlowLayout, tag.name); + tags.add(tag); + } + }*/ + + /*private void setAndDisableContent() { + content.setVisibility(View.VISIBLE); + content.setEnabled(false); + content.setText(post.content); + } + + private void setAndDisableName() { + name.setVisibility(View.VISIBLE); + name.setEnabled(false); + name.setText(post.name); + }*/ + + @Override + protected void initUI(Context context) { + //super.initUI(context); + /*new Handler().postAtFrontOfQueue(new Runnable() { + @Override + public void run() { + if (rootView != null) + rootView.removeView(postTypeContainer); + } + });*/ + + sharedInitUIBetweenRepostAndNewPost(); + if (post.type == PostType.TEXT) + onTextType(); + else + onImageType(); + postTypeContainer.setVisibility(View.GONE); + fillViewsWithValues(post, true); + } + + + + @Override + public void onPositiveButton() { + if (!validate()) return; + PoinilaNetService.repost(((Collection) collectionSpinner.getSelectedItem()).getId(), + post.id, summary.getText().toString(), tags); + BusProvider.getSyncUIBus().post(new UpdateUiRepostEvent(post.id, true)); + onNegativeButton(); + } + + @Override + protected boolean validate() { + if (collectionSpinner.getSelectedItem() == null) + return false; + + EditText[] requiredItems = new EditText[]{}; + EditText[] limitedItems = new EditText[]{summary}; + int[] minLengths = new int[]{}; + int[] maxLengths; + if (isImaged){ + maxLengths = new int[]{max_length_image_post_summary}; + }else{ + maxLengths = new int[]{max_length_text_post_summary}; + } + return validateInputs(requiredItems, minLengths, limitedItems, maxLengths); + } + + @Subscribe + public void onRemoveTag(final RemoveTagEvent event){ + super.removeTag(event); + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData(R.string.repost_post, NO_RESOURCE, R.string.create, R.string.cancel, NO_RESOURCE); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/SelectImageDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/SelectImageDialog.java new file mode 100755 index 0000000..0b23415 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/SelectImageDialog.java @@ -0,0 +1,108 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.Manifest; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v13.app.FragmentCompat; +import android.support.v4.app.Fragment; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.StaggeredGridLayoutManager; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; +import android.widget.Toast; + +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewProvider; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.presentation.view.activity.BaseActivity; +import com.shaya.poinila.android.presentation.view.costom_view.ActivityResultPermissionDelegate; +import com.shaya.poinila.android.presentation.viewholder.BaseViewHolder; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.StorageUtils; + +import java.util.ArrayList; +import java.util.List; + +import butterknife.Bind; +import data.model.ImageUrls; +import data.model.Member; + +/** + * Created by iran on 6/6/2016. + */ +public class SelectImageDialog extends BaseDialogFragment{ + + private Member member; + + private View.OnClickListener onItemClickListener; + + @Bind(R.id.show_profile_image) + Button showProfileImage; + + @Bind(R.id.select_image_camera) + Button selectImageCamera; + + @Bind(R.id.select_image_gallery) + Button selectImageGallery; + + public static SelectImageDialog newInstance(Member member, View.OnClickListener onItemClickListener){ + Bundle args = new Bundle(); + SelectImageDialog fragment = new SelectImageDialog(); + fragment.member = member; + fragment.onItemClickListener = onItemClickListener; + fragment.saveStateToBundle(args); + fragment.setArguments(args); + return fragment; + } + + + + @Override + public int getLayoutResId() { + return R.layout.select_image_dialog; + } + + @Override + protected void loadStateFromBundle(Bundle savedInstanceState) { + + } + + @Override + protected void saveStateToBundle(Bundle outState) { + + } + + @Override + protected void initUI(Context context) { + + showProfileImage.setOnClickListener(onItemClickListener); + selectImageCamera.setOnClickListener(onItemClickListener); + selectImageGallery.setOnClickListener(onItemClickListener); + + ViewUtils.setFont(showProfileImage, getString(R.string.default_bold_font_path)); + ViewUtils.setFont(selectImageCamera, getString(R.string.default_bold_font_path)); + ViewUtils.setFont(selectImageGallery, getString(R.string.default_bold_font_path)); + + + if(member != null && (member.imageUrls == null || !member.imageUrls.isNotEmpty())){ + showProfileImage.setVisibility(View.GONE); + } + + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData(null, null, null, null, null); + } + + + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/SetUserNamePasswordDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/SetUserNamePasswordDialog.java new file mode 100755 index 0000000..e858e6a --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/SetUserNamePasswordDialog.java @@ -0,0 +1,125 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.content.Context; +import android.os.Bundle; +import android.text.InputType; +import android.view.ViewGroup; +import android.widget.EditText; +import android.widget.ImageView; + +import com.mobsandgeeks.saripaar.annotation.Email; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.util.ConstantsUtils; + +import butterknife.Bind; +import butterknife.OnClick; +import data.PoinilaNetService; +import uk.co.chrisjenx.calligraphy.CalligraphyConfig; +import uk.co.chrisjenx.calligraphy.CalligraphyUtils; + +/** + * Created by iran on 8/7/2016. + */ +public class SetUserNamePasswordDialog extends BusDialogFragment { + + + @Bind(R.id.username_input) + EditText username; + + // TODO: custom rule to send request + @Bind(R.id.password_input) + EditText password; + + + @Bind(R.id.toggle_visibility) + ImageView toggleVisibilityBtn; + + + private boolean passwordVisible; + + + + public static SetUserNamePasswordDialog newInstance(){ + SetUserNamePasswordDialog fragment = new SetUserNamePasswordDialog(); + + return fragment; + } + + @Override + protected boolean sendsRequestAutomatically() { + return false; + } + + @Override + protected void requestInitialData() { + + } + + @Override + public ViewGroup getLoadableView() { + return null; + } + + @Override + public boolean mustShowProgressView() { + return false; + } + + @Override + public int getLayoutResId() { + return R.layout.dialog_set_user_pass; + } + + @Override + protected void loadStateFromBundle(Bundle savedInstanceState) { + + } + + @Override + protected void saveStateToBundle(Bundle outState) { + + } + + @Override + public void onPositiveButton() { + super.onPositiveButton(); + + PoinilaNetService.setUsernamePassword(username.getText().toString(), password.getText().toString()); + + onNegativeButton(); + } + + + @OnClick(R.id.toggle_visibility) + public void onChangeVisibility() { + passwordVisible ^= true; // toggle + + int start = password.getSelectionStart(); + int end = password.getSelectionEnd(); + password.setInputType(passwordVisible ? + InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD : + InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); + CalligraphyUtils.applyFontToTextView(getActivity(), password, CalligraphyConfig.get().getFontPath()); + password.setSelection(start, end); + + toggleVisibilityBtn.setImageResource(passwordVisible ? + R.drawable.toggle_visible_nobel_32dp : + R.drawable.toggle_invisible_nobel_32dp); + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData( + R.string.change_user_pass_title, + ConstantsUtils.NO_RESOURCE, + R.string.send, + R.string.cancel, + ConstantsUtils.NO_RESOURCE + ); + } + + @Override + protected void initUI(Context context) { + + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/SingleTextFieldDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/SingleTextFieldDialog.java new file mode 100755 index 0000000..cba1342 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/SingleTextFieldDialog.java @@ -0,0 +1,103 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.content.Context; +import android.os.Bundle; +import android.support.design.widget.TextInputEditText; +import android.support.design.widget.TextInputLayout; +import android.view.ViewGroup; +import android.widget.EditText; +import android.widget.TextView; + +import com.mobsandgeeks.saripaar.ValidationError; +import com.mobsandgeeks.saripaar.Validator; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.SimpleSettingTextSetEvent; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.presentation.view.activity.SettingActivity.SettingType; +import com.shaya.poinila.android.util.BusProvider; + +import java.util.List; + +import butterknife.Bind; + +/** + * Created by iran on 2015-07-21. + */ +public abstract class SingleTextFieldDialog extends BusDialogFragment implements Validator.ValidationListener { + public static final String ADAPTER_POSITION = "adapter position"; + public int adapterPosition = -1; + protected Validator validator; + //protected SettingType settingType; + @Bind(R.id.input_field) public TextInputEditText inputField; + @Bind(R.id.field_input_layout) public TextInputLayout inputLayout; + + @Override + public int getLayoutResId() { + return R.layout.dialog_single_text_input; + } + + + @Override + protected void initUI(Context context) { + setTextFieldInputMethod(); + validator = new Validator(this); + validator.setValidationListener(this); + } + + protected abstract SettingType getSettingType(); + + protected abstract void setTextFieldInputMethod(); + + /** + * We need this in time of catching event; + */ + protected abstract int getItemPosition(); + + @Override + public void onPositiveButton() { + validator.validate(false); + } + + @Override + protected void loadStateFromBundle(Bundle savedInstanceState) { + adapterPosition = savedInstanceState.getInt(ADAPTER_POSITION, -1); + } + + @Override + protected void saveStateToBundle(Bundle outState) { + outState.putInt(ADAPTER_POSITION, adapterPosition); + } + + @Override + public void onValidationSucceeded() { + BusProvider.getBus().post(new SimpleSettingTextSetEvent( + getSettingType(), inputField.getText().toString(), getItemPosition())); + onNegativeButton(); + } + + @Override + public void onValidationFailed(List errors) { + ViewUtils.handleSaripaarErrors(errors, getActivity()); + } + + @Override + protected boolean sendsRequestAutomatically() { + return false; + } + + @Override + protected void requestInitialData() { + + } + + @Override + public ViewGroup getLoadableView() { + return null; + } + + @Override + public boolean mustShowProgressView() { + return false; + } + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/dialog/VerificationRequestCodeDialog.java b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/VerificationRequestCodeDialog.java new file mode 100755 index 0000000..1c6a4f6 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/dialog/VerificationRequestCodeDialog.java @@ -0,0 +1,182 @@ +package com.shaya.poinila.android.presentation.view.dialog; + +import android.content.Context; +import android.os.Bundle; +import android.text.InputType; +import android.util.Patterns; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.RadioGroup; +import android.widget.TextView; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.presentation.view.activity.SignUpLoginActivity; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.Logger; +import com.squareup.otto.Subscribe; + +import butterknife.Bind; +import butterknife.ButterKnife; +import data.PoinilaNetService; +import data.event.VerificationRequestResponse; +import manager.DBFacade; + +/** + * Created by iran on 7/18/2016. + */ +public class VerificationRequestCodeDialog extends BusDialogFragment { + + @Bind(R.id.radio_group) + RadioGroup optionsRadioGroup; + @Bind(R.id.input_field) + EditText inputField; + @Bind(R.id.input_filed_icon) + ImageView inputFieldIcon; + private boolean mVerificationByEmail; + private String inputValue; + private boolean disableRadioBtns = false; + private int titleRes = R.string.verifying; + + public static VerificationRequestCodeDialog newInstance(){ + VerificationRequestCodeDialog fragment = new VerificationRequestCodeDialog(); + return fragment; + } + + public static VerificationRequestCodeDialog newInstance(int titleRes, String inputValue, boolean mVerificationByEmail){ + VerificationRequestCodeDialog fragment = new VerificationRequestCodeDialog(); + fragment.inputValue = inputValue; + fragment.mVerificationByEmail = mVerificationByEmail; + fragment.disableRadioBtns = true; + fragment.titleRes = titleRes; + return fragment; + } + + + @Override + protected boolean sendsRequestAutomatically() { + return false; + } + + @Override + protected void requestInitialData() { + + } + + @Override + public ViewGroup getLoadableView() { + return null; + } + + @Override + public boolean mustShowProgressView() { + return false; + } + + @Override + public int getLayoutResId() { + return R.layout.dialog_verification_request; + } + + @Override + protected void loadStateFromBundle(Bundle savedInstanceState) { + + } + + @Override + protected void saveStateToBundle(Bundle outState) { + + } + + @Override + protected GeneralDialogData getDialogGeneralAttributes() { + return new GeneralDialogData(titleRes, ConstantsUtils.NO_RESOURCE, R.string.send, R.string.cancel, ConstantsUtils.NO_RESOURCE); + } + + @Override + protected void initUI(Context context) { + //backForthButtonsBox.setBackForthListener(this); + optionsRadioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(RadioGroup group, int checkedId) { + switch (checkedId) { + case R.id.email_option: + onMailOption(); + break; + case R.id.sms_option: + onSmsOption(); + break; + } + } + }); + + + optionsRadioGroup.setVisibility(disableRadioBtns ? View.GONE : View.VISIBLE); + + inputField.setText(inputValue); + if (mVerificationByEmail) onMailOption(); + else onSmsOption(); + } + + private void onSmsOption() { + optionsRadioGroup.check(R.id.sms_option); + mVerificationByEmail = false; + inputField.setInputType(InputType.TYPE_CLASS_PHONE); + inputFieldIcon.setImageResource(R.drawable.phone_48dp); + } + + private void onMailOption() { + optionsRadioGroup.check(R.id.email_option); + mVerificationByEmail = true; + inputField.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS); + inputFieldIcon.setImageResource(R.drawable.email_48dp); + } + + @Subscribe + public void onVerifyResponse(VerificationRequestResponse event) { + dismissProgressDialog(); + + if (event.succeed) { + if(disableRadioBtns) + DialogLauncher.launchInputVerificationCodeDialog(getFragmentManager(), + inputField.getText().toString(), mVerificationByEmail, disableRadioBtns); + else + DialogLauncher.launchInputVerificationCodeDialog(getFragmentManager(), + inputField.getText().toString(), mVerificationByEmail); + Logger.toast(mVerificationByEmail ? R.string.success_verification_mail : R.string.success_verification_sms); + dismiss(); + } else { + Logger.toastError(event.errorExplanation); + } + optionsRadioGroup.setEnabled(true); + } + + @Override + public void onPositiveButton() { +// super.onPositiveButton(); + + showProgressDialog(); + String userText = inputField.getText().toString().trim(); + if (mVerificationByEmail && !Patterns.EMAIL_ADDRESS.matcher(userText).matches()) { + inputField.setText(""); + ViewUtils.temporaryError(inputField, getString(R.string.error_invalid_email)); + return; + } else if (!mVerificationByEmail && !Patterns.PHONE.matcher(userText).matches()) { + inputField.setText(""); + ViewUtils.temporaryError(inputField, getString(R.string.error_invalid_phone)); + return; + } + /*if (!mVerificationByEmail){ // sms + initData(); + }else{*//* + Logger.toast(R.string.success_verification_mail); + //}*/ + optionsRadioGroup.setEnabled(false); + if(DBFacade.getCachedMyInfo() != null) + PoinilaNetService.requestVerificationCode(mVerificationByEmail, userText, disableRadioBtns ? 0 : DBFacade.getCachedMyInfo().id); + } + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/AnonymousInfoFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/AnonymousInfoFragment.java new file mode 100755 index 0000000..1afbd56 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/AnonymousInfoFragment.java @@ -0,0 +1,54 @@ +package com.shaya.poinila.android.presentation.view.fragments; + +import android.os.Bundle; +import android.widget.TextView; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.util.ConstantsUtils; + +import butterknife.Bind; +import butterknife.OnClick; +import manager.DataRepository; + +/** + * Created by iran on 2/28/2016. + */ +public class AnonymousInfoFragment extends BaseFragment { + public static final int FOLLOWING_COLLECTIONS = 1; + public static final int NOTIFICATIONS = 3; + public static final int PROFILE = 4; + + @Bind(R.id.page_info_text) + public TextView anonymousInfoText; + + public static AnonymousInfoFragment newInstance(int page) { + Bundle args = new Bundle(); + args.putInt(ConstantsUtils.KEY_ANONYMOUS_PAGE, page); + AnonymousInfoFragment fragment = new AnonymousInfoFragment(); + fragment.setArguments(args); + return fragment; + } + + @Override + public int getLayoutID() { + return R.layout.fragment_anonymous; + } + + @Override + protected void initUI() { + int page = getArguments().getInt(ConstantsUtils.KEY_ANONYMOUS_PAGE); + String info = null; + if (page == FOLLOWING_COLLECTIONS) + info = getString(R.string.anonymous_info_following_collections); + else if (page == NOTIFICATIONS) + info = getString(R.string.anonymous_info_notifications); + else if (page == PROFILE) + info = getString(R.string.anonymous_info_profile); + ViewUtils.setText(anonymousInfoText, info); + } + + @OnClick(R.id.back_to_login) public void onExitGuestToLogin(){ + DataRepository.logoutEvent(); + } + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/AppSettingFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/AppSettingFragment.java new file mode 100755 index 0000000..cd5d808 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/AppSettingFragment.java @@ -0,0 +1,19 @@ +package com.shaya.poinila.android.presentation.view.fragments; + +import android.os.Bundle; +import android.preference.PreferenceFragment; + +import com.shaya.poinila.android.presentation.R; + +/** + * Created by iran on 2015-11-07. + */ +public class AppSettingFragment extends PreferenceFragment{ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Load the preferences from an XML resource + addPreferencesFromResource(R.xml.preferences); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/BaseFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/BaseFragment.java new file mode 100755 index 0000000..9ded382 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/BaseFragment.java @@ -0,0 +1,139 @@ +package com.shaya.poinila.android.presentation.view.fragments; + + +import android.content.Context; +import android.os.Bundle; +import android.support.annotation.LayoutRes; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.google.android.gms.analytics.HitBuilders; +import com.google.android.gms.analytics.Tracker; +import com.shaya.poinila.android.presentation.PoinilaApplication; +import com.shaya.poinila.android.presentation.view.OnHelpShowListener; +import com.shaya.poinila.android.utils.uisynchronize.UISynchronizeBus; +import com.shaya.poinila.android.utils.uisynchronize.UISynchronizeReceiver; + +import java.io.Serializable; + +import butterknife.ButterKnife; + +/** + * Created by iran on 2015-06-21. + * @author Alireza Farahani + * + * Abstract class, father of all fragments for poinila app + * + */ +public abstract class BaseFragment extends Fragment implements UISynchronizeReceiver.OnLoadDataSynchronizeListener{ + + protected ViewGroup rootView; + private String defaultTag = getClass().getName(); + + boolean viewedHelp = false; + protected boolean selected = false; + protected boolean ready = false; + + private Tracker mTracker; + + /** + * Avoiding memory leak by not returning a activity. + * We need getting context from activity in many cases. + * @return Application context + */ + public Context getContext(){ + return getActivity(); + } + + public abstract @LayoutRes int getLayoutID(); + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + PoinilaApplication application = (PoinilaApplication)getActivity().getApplication(); + mTracker = application.getDefaultTracker(); + + mTracker.setScreenName(getDefaultTag()); + mTracker.send(new HitBuilders.ScreenViewBuilder().build()); + + UISynchronizeBus.getInstance().getReceiver().setOnLoadDataSynchronizeListener(this); + + + /** + * Preventing loss of data on configuration change(like rotation). + * May be saving data on a fragment object and retrieving by fragmentManager is a + * better idea. + * + FragmentManager fm = getFragmentManager(); + dataFragment = (DataFragment) fm.findFragmentByTag(“data”); + + // create the fragment and data the first time + if (dataFragment == null) { + // add the fragment + dataFragment = new DataFragment(); + fm.beginTransaction().add(dataFragment, “data”).commit(); + // load the data from the web + dataFragment.setData(loadMyData()); + } + + */ + //setRetainInstance(true); + } + + /*private BroadcastReceiver mRequestFailedReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + requestingIsLocked = false; + } + };*/ + + @Override + public void onStart() { + // Register to receive messages. + // We are registering an observer (mMessageReceiver) to receive Intents + // with actions named "custom-event-name". + super.onStart(); + /*LocalBroadcastManager.getInstance(getActivity()).registerReceiver(mRequestFailedReceiver, + new IntentFilter(ConstantsUtils.INTENT_FILTER_REQUEST_FAILED));*/ + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + rootView = ((ViewGroup) inflater.inflate(getLayoutID(), container, false)); + ButterKnife.bind(this, rootView); + initUI(); + return rootView; + } + + protected abstract void initUI(); + + @Override + public void onDestroy() { + super.onDestroy(); + ButterKnife.unbind(this); + } + + public String getDefaultTag() { + return defaultTag; + } + + public boolean isReady() { + return ready; + } + + @Override + public UISynchronizeBus.UI_SYNCHRONIZE_ACTION getSynchronizeAction() { + return UISynchronizeBus.UI_SYNCHRONIZE_ACTION.OFF; + } + + @Override + public void loadDataForSynchronize(Serializable data, UISynchronizeBus.UI_SYNCHRONIZE_ACTION action) { + Log.i(getClass().getName(), "loadDataForSynchronize"); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/BusFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/BusFragment.java new file mode 100755 index 0000000..73c41aa --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/BusFragment.java @@ -0,0 +1,174 @@ +package com.shaya.poinila.android.presentation.view.fragments; + +import android.app.ProgressDialog; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.support.annotation.LayoutRes; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ProgressBar; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.PoinilaPreferences; + +import butterknife.ButterKnife; +import data.RequestTracker; +import data.event.BaseEvent; + +/** + * Created by iran on 2015-06-30. + * General Behavior a fragment must do if it subscribes on the bus + */ +public abstract class BusFragment extends BaseFragment { + protected boolean initDataResponseReceived = false; + protected ViewGroup progressView; + private View loadableView; + private ViewGroup loadableViewParent; + private ViewGroup.LayoutParams loadableViewLayoutParams; + protected RequestTracker requestTracker; + private int loadableViewIndex; + private ProgressDialog mProgressDialog; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestTracker = new RequestTracker(); + + mProgressDialog = new ProgressDialog(getActivity(), 0); + mProgressDialog.setMessage(getString(R.string.progress_dialog_message)); + } + + @Override + public void onStart() { + super.onStart(); + BusProvider.getBus().register(this); + if (sendsRequestAutomatically() && !initDataResponseReceived) + initData(); + } + + protected abstract boolean sendsRequestAutomatically(); + + @Override + public void onStop() { + super.onStop(); + BusProvider.getBus().unregister(this); + } + + protected void onGettingInitDataResponse(BaseEvent event) { + if (isInitDataResponseValid(event)) + onSuccessfulInitData(event); + } + + /** + * called only on creating view. (with regard to {@link #initDataResponseReceived} + * if somethings need to be updated, update with events and bus. + */ + public void initData() { + initDataResponseReceived = false; + if (mustShowProgressView()) + showProgress(); + //requestTracker.addInitRequestID(RandomUtils.getRandomInt()); + requestInitialData(); + } + + protected abstract void requestInitialData(); + + public abstract ViewGroup getLoadableView(); + + protected void showProgress() { + progressView = (ViewGroup) LayoutInflater.from(getActivity()).inflate(getProgressViewLayoutID(), rootView, false); + initProgressBar(getProgressBar()); + /*int progressBarSize = getResources().getDimensionPixelSize(R.dimen.icon_big); + FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(progressBarSize, progressBarSize); + lp.gravity = Gravity.CENTER; + progressView.addView(getProgressBar(), lp);*/ + + loadableView = getLoadableView(); +// if(loadableView != null){ +// loadableViewLayoutParams = loadableView.getLayoutParams(); +// loadableViewParent = (ViewGroup) loadableView.getParent(); +// //TODO: null pointer sometimes. but why? +// if (loadableViewParent != null) { +// loadableViewIndex = loadableViewParent.indexOfChild(loadableView); +// loadableViewParent.removeView(loadableView); +// loadableViewParent.addView(progressView, loadableViewIndex, loadableViewLayoutParams); +// } +// +// if (getProgressBar().isIndeterminate()) { +// new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { +// @Override +// public void run() { +// if (rootView.findViewById(R.id.progress_view) != null) +// dismissProgress(false); +// } +// }, ConstantsUtils.CONNECT_TIME_OUT_MILLISECONDS); +// } +// } + + } + + @LayoutRes + protected int getProgressViewLayoutID() { + return R.layout.progress; + } + + protected void initProgressBar(ProgressBar progressBar) { + + } + + + public void showProgressDialog(){ + if(mProgressDialog != null){ + mProgressDialog.show(); + } + + } + + public void dismissProgressDialog(){ + mProgressDialog.dismiss(); + } + + public ProgressBar getProgressBar() { + /*ProgressBar progressBar = ButterKnife.findById(progressView, R.id.progress_bar); + if (progressBar == null){ + progressBar = new ProgressBar(getActivity(), null, android.R.attr.progressBarStyleLarge); + } + return progressBar;*/ + return ButterKnife.findById(progressView, R.id.progress_bar); + } + + // TODO: sometimes getting null loadableViewParent. But what times exactly? + protected void dismissProgress(boolean dataLoadSuccessful) { + if (loadableViewParent == null) return; + loadableViewParent.removeView(progressView); + // -- kasif! + if (loadableView.getParent() != null) + ((ViewGroup) loadableView.getParent()).removeView(loadableView); + // + if (dataLoadSuccessful) { + //ViewUtils.enableLayoutChildes(getLoadableView(), true); + loadableViewParent.addView(loadableView, loadableViewIndex, loadableViewLayoutParams); + } + } + + public void onSuccessfulInitData(BaseEvent baseEvent) { + if (mustShowProgressView() && !initDataResponseReceived) + dismissProgress(true); + initDataResponseReceived = true; + } + + public abstract boolean mustShowProgressView(); + + protected boolean isInitDataResponseValid(BaseEvent baseEvent) { + // in many cases of list pages, requestForInitData just calls the requestForLoadMore so we call this + // function on getting response data of list. In order to avoid view hierarchy traversing on every + // list data response, we check the requestOnFirstTime flag to be true. + return true; + } + /*---temporary validity check functions----*/ + //TODO: later, these methods must +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/BusRefreshableListFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/BusRefreshableListFragment.java new file mode 100755 index 0000000..00e40d3 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/BusRefreshableListFragment.java @@ -0,0 +1,84 @@ +package com.shaya.poinila.android.presentation.view.fragments; + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.widget.SwipeRefreshLayout; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.util.DeviceInfoUtils; + +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; + +/** + * Created by iran on 2015-08-02. + */ +public abstract class BusRefreshableListFragment extends ListBusFragment implements SwipeRefreshLayout.OnRefreshListener{ + + //@Bind(R.actorID.refresh_layout) + protected SwipeRefreshLayout swipeRefreshLayout; + + /** + * Child view root layout got from {#getLayoutID} must be a scrollable view + * @param inflater + * @param container + * @param savedInstanceState + * @return + */ + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + swipeRefreshLayout = (SwipeRefreshLayout) inflater.inflate(R.layout.swipe_refresh, container, false); + + //View content = inflater.inflate(getLayoutID(), swipeRefreshLayout, false); + swipeRefreshLayout.addView(super.onCreateView(inflater, container, savedInstanceState), MATCH_PARENT, MATCH_PARENT); + initSwipeLayout(); + return swipeRefreshLayout; + } + + private void initSwipeLayout() { + swipeRefreshLayout.setOnRefreshListener(this); + if (DeviceInfoUtils.isTablet()) { + swipeRefreshLayout.setSize(SwipeRefreshLayout.LARGE); + } + setAppearance(); + } + + private void setAppearance() { + swipeRefreshLayout.setColorSchemeResources(R.color.flamingo, + R.color.west_side, + R.color.holo_blue_bright, + R.color.holo_green_light); + } + + public abstract void refresh(); + + + public final void onRefreshFinished(){ + swipeRefreshLayout.setRefreshing(false); + } + + @Override + public void onRefresh() { + /*new Handler().postDelayed(new Runnable() { + @Override public void run() { + + swipeRefreshLayout.setRefreshing(false); + } + }, 5000);*/ + refresh(); + } + + @Override + public void onPause() { + super.onPause(); + + if (swipeRefreshLayout!=null) { + swipeRefreshLayout.setRefreshing(false); + swipeRefreshLayout.destroyDrawingCache(); + swipeRefreshLayout.clearAnimation(); + } + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/CollectionListFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/CollectionListFragment.java new file mode 100755 index 0000000..2511750 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/CollectionListFragment.java @@ -0,0 +1,365 @@ +package com.shaya.poinila.android.presentation.view.fragments; + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.StaggeredGridLayoutManager; +import android.util.Log; +import android.view.View; +import android.widget.TextView; + +import com.getbase.floatingactionbutton.FloatingActionButton; +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewProvider; +import com.shaya.poinila.android.presentation.uievent.CollectionClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.EditItemUIEvent; +import com.shaya.poinila.android.presentation.uievent.MemberClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.OnFollowUnfollowCollectionUIEvent; +import com.shaya.poinila.android.presentation.uievent.PositiveButtonClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.RemoveItemUIEvent; +import com.shaya.poinila.android.presentation.view.dialog.DialogLauncher; +import com.shaya.poinila.android.presentation.view.dialog.NewCollectionDialog; +import com.shaya.poinila.android.presentation.viewholder.BaseViewHolder; +import com.shaya.poinila.android.presentation.viewholder.CollectionViewHolder; +import com.shaya.poinila.android.presentation.viewholder.DashboardPostViewHolder; +import com.shaya.poinila.android.presentation.viewholder.EditableCollectionViewHolder; +import com.shaya.poinila.android.presentation.viewholder.FollowableCollectionViewHolder; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.Logger; +import com.squareup.otto.Subscribe; + +import butterknife.Bind; +import data.PoinilaNetService; +import data.event.BaseEvent; +import data.event.CollectionReceivedEvent; +import data.event.CollectionUpdatedEvent; +import data.event.CollectionsReceivedEvent; +import data.model.Collection; +import data.model.Loading; +import data.model.Member; +import data.model.Post; +import manager.DataRepository; + +import static android.support.v7.widget.LinearLayoutManager.VERTICAL; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_ENTITY; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_ITEM_COUNT; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_REQUEST_ID; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_MEMBER_COLLECTIONS; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_MEMBER_FOLLOWED_COLLECTIONS; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_POST_REPOSTING_COLLECTIONS; +import static com.shaya.poinila.android.util.ResourceUtils.getInteger; + + +/** + * Created by iran on 2015-08-06. + */ +public class CollectionListFragment extends ListBusFragment { + + private String mainActorID; + private int requestID; + + + @Nullable + @Bind(R.id.item_count) + TextView mItemCountView; + + FloatingActionButton fabMenu; + + public static android.support.v4.app.Fragment newInstance(String id, int requestID) { + CollectionListFragment f = new CollectionListFragment(); + Bundle b = new Bundle(); + b.putString(KEY_ENTITY, id); + b.putInt(KEY_REQUEST_ID, requestID); + f.setArguments(b); + return f; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getArguments() != null) { + mainActorID = getArguments().getString(KEY_ENTITY); + requestID = getArguments().getInt(KEY_REQUEST_ID); + } + } + + + @Override + protected RecyclerView.OnScrollListener getRecyclerViewListener() { + switch (requestID) { + case REQUEST_POST_REPOSTING_COLLECTIONS: + case REQUEST_MEMBER_FOLLOWED_COLLECTIONS: + return RecyclerViewProvider.staggeredListEndDetectorListener(getRecyclerViewAdapter(), this); + case REQUEST_MEMBER_COLLECTIONS: + return (areMyCollections() ? + RecyclerViewProvider.gridListEndDetectionListener(getRecyclerViewAdapter(), this) : + RecyclerViewProvider.staggeredListEndDetectorListener(getRecyclerViewAdapter(), this)); + default: // my followed collections + return RecyclerViewProvider.gridListEndDetectionListener(getRecyclerViewAdapter(), this); + } + } + + + @Subscribe + public void onCollectionsReceived(CollectionsReceivedEvent event) { + + if(event.collections.size() >= 25 && mRecyclerView.getAdapter().getItemCount() == 0){ + setLoading(new Loading()); + } + + onGettingInitDataResponse(event); + onGettingListDataResponse(event, event.bookmark); + + + } + + @Override + public void onSuccessfulListData(BaseEvent baseEvent, String newBookmark) { + super.onSuccessfulListData(baseEvent, newBookmark); + getRecyclerViewAdapter().addItems(((CollectionsReceivedEvent) baseEvent).collections); + } + + @Override + public void onEndListData() { + removeLoading(); + } + + @Override + public int getLayoutID() { + if (requestID == REQUEST_POST_REPOSTING_COLLECTIONS) + return R.layout.fragment_reposts; + if (requestID == REQUEST_MEMBER_COLLECTIONS) + return R.layout.fragment_member_collections; + return R.layout.recycler_view_full; + } + + @Override + protected void initUI() { + super.initUI(); + Bundle b = getArguments(); + requestID = b.getInt(KEY_REQUEST_ID); + mainActorID = b.getString(KEY_ENTITY); + switch (requestID) { + case ConstantsUtils.REQUEST_MEMBER_COLLECTIONS: + if (areMyCollections()) { + mRecyclerView = new RecyclerViewProvider(mRecyclerView). + setAdapter(getRecyclerViewAdapter()). + setGridLayoutManager(VERTICAL, getResources().getInteger(R.integer.column_count), new GridLayoutManager.SpanSizeLookup() { + @Override + public int getSpanSize(int position) { + if(getRecyclerViewAdapter().getItemViewType(position) == RecyclerViewAdapter.VIEW_TYPE_LOAD_PROGRESS ){ + return getResources().getInteger(R.integer.column_count); + } + return 1; + } + }). + bindViewToAdapter(); + + + fabMenu = (FloatingActionButton)rootView.findViewById(R.id.fab_add_collection); + + fabMenu.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + new NewCollectionDialog().show(getFragmentManager(), null); + } + }); + break; + } + case REQUEST_POST_REPOSTING_COLLECTIONS: + setText(mItemCountView, getString(R.string.reposts_formatted, + getActivity().getIntent().getIntExtra(KEY_ITEM_COUNT, 0))); + case REQUEST_MEMBER_FOLLOWED_COLLECTIONS: + mRecyclerView = new RecyclerViewProvider(mRecyclerView). + setAdapter(getRecyclerViewAdapter()). + setStaggeredLayoutManager(VERTICAL, getInteger(R.integer.column_count)). + bindViewToAdapter(); + break; + } + mRecyclerView.getItemAnimator().setChangeDuration(0); + + + } + + @Override + public RecyclerViewAdapter createAndReturnRVAdapter() { + switch (requestID) { + case ConstantsUtils.REQUEST_MEMBER_COLLECTIONS: + if (areMyCollections()) + return new RecyclerViewAdapter(getActivity(), R.layout.collection_editable) { + @Override + protected BaseViewHolder getProperViewHolder(View v, int viewType) { + if(viewType == RecyclerViewAdapter.VIEW_TYPE_LOAD_PROGRESS ){ + return new BaseViewHolder.EmptyViewHolder(v); + } + return new EditableCollectionViewHolder(v, BaseEvent.ReceiverName.CollectionListFragment); + } + }; + case REQUEST_POST_REPOSTING_COLLECTIONS: + case REQUEST_MEMBER_FOLLOWED_COLLECTIONS: + return new RecyclerViewAdapter(getActivity(), R.layout.collection_followable) { + + @Override + protected BaseViewHolder getProperViewHolder(View v, int viewType) { + if(viewType == RecyclerViewAdapter.VIEW_TYPE_LOAD_PROGRESS ){ + return new BaseViewHolder.EmptyViewHolder(v); + } + return new FollowableCollectionViewHolder(v, BaseEvent.ReceiverName.CollectionListFragment); + } + + @Override + public void onBindViewHolder(BaseViewHolder holder, int position) { + if (getItemViewType(position) == VIEW_TYPE_LOAD_PROGRESS) { + StaggeredGridLayoutManager.LayoutParams layoutParams = (StaggeredGridLayoutManager.LayoutParams) holder.itemView.getLayoutParams(); + layoutParams.setFullSpan(true); + } else { + super.onBindViewHolder(holder, position); + } + + } + }; + default: + return null; + } + } + + private boolean areMyCollections() { + return mainActorID.equals(DataRepository.getInstance().getMyId()); + } + + + @Subscribe + public void onRemoveCollection(RemoveItemUIEvent event) { + // must not happen but anyway... :) + if (DataRepository.isUserAnonymous()){ + Logger.toastError(R.string.error_guest_action); + return; + } + + clickedItemPosition = event.adapterPosition; + DialogLauncher.launchDeleteCollection(getFragmentManager()); + } + + @Subscribe + public void onEditCollection(EditItemUIEvent event) { + // must not happen but anyway... :) + if (DataRepository.isUserAnonymous()){ + Logger.toastError(R.string.error_guest_action); + return; + } + + clickedItemPosition = event.adapterPosition; + Collection collection = (Collection)getRecyclerViewAdapter().getItem(event.adapterPosition); + DialogLauncher.launchEditCollectionDialog(getFragmentManager(), collection); + } + + @Subscribe + public void onPositiveDialogButtonClicked(PositiveButtonClickedUIEvent event) { + PoinilaNetService.deleteCollection((Collection)getRecyclerViewAdapter().getItem(clickedItemPosition)); + getRecyclerViewAdapter().removeItem(clickedItemPosition); + } + + @Subscribe + public void onFollowCollection(OnFollowUnfollowCollectionUIEvent event) { + if (DataRepository.isUserAnonymous()){ + Logger.toastError(R.string.error_guest_action); + return; + } + + if (event.follow) { + PoinilaNetService.followCollection(((Collection)getRecyclerViewAdapter().getItem(event.adapterPosition)).getId()); + } else { + PoinilaNetService.unfollowCollection(((Collection)getRecyclerViewAdapter().getItem(event.adapterPosition)).getId()); + } + ((Collection)getRecyclerViewAdapter().getItem(event.adapterPosition)).followedByMe ^= true; // toggle boolean by xor ing with "True". + getRecyclerViewAdapter().notifyItemChanged(event.adapterPosition); + } + + @Subscribe + public void onProfilePicClickedEvent(MemberClickedUIEvent event) { + if (event.receiverName != BaseEvent.ReceiverName.CollectionListFragment) + return; + Member member = ((Collection)getRecyclerViewAdapter().getItem(event.adapterPosition)).owner; + PageChanger.goToProfile(getActivity(), member); + } + + @Subscribe + public void onCollectionClicked(CollectionClickedUIEvent event) { + if (event.receiverName != BaseEvent.ReceiverName.CollectionListFragment) + return; + Collection collection = (Collection)getRecyclerViewAdapter().getItem(event.adapterPosition); + PageChanger.goToCollection(getActivity(), collection); + } + + @Subscribe + public void onCollectionUpdated(CollectionUpdatedEvent event) { + int collectionPosition = findCollectionPositionInAdapter(event.collection); + if (collectionPosition == -1) return; + Collection collection = (Collection)getRecyclerViewAdapter().getItem(collectionPosition); + collection.description = event.collection.description; + collection.circleIDs = event.collection.circleIDs; + collection.name = event.collection.name; + collection.topic = event.collection.topic; + Logger.log("coverImageUrls = " + event.collection.coverImageUrls, Logger.LEVEL_INFO); + collection.coverImageUrls = event.collection.coverImageUrls; + getRecyclerViewAdapter().setItem(collection, collectionPosition); + getRecyclerViewAdapter().notifyItemChanged(collectionPosition); + } + + @Subscribe + public void onCollectionReceived(CollectionReceivedEvent event){ + int collectionPosition = findCollectionPositionInAdapter(event.collection); + if (collectionPosition == -1) return; + Collection collection = (Collection)getRecyclerViewAdapter().getItem(collectionPosition); + collection.description = event.collection.description; + collection.circleIDs = event.collection.circleIDs; + collection.name = event.collection.name; + collection.topic = event.collection.topic; + collection.coverImageUrls = event.collection.coverImageUrls; + getRecyclerViewAdapter().setItem(collection, collectionPosition); + getRecyclerViewAdapter().notifyItemChanged(collectionPosition); + } + + private int findCollectionPositionInAdapter(Collection collection) { + return getRecyclerViewAdapter().getItems().indexOf(collection); + } + + + /*-----------*/ + + + @Override + protected boolean sendsRequestAutomatically() { + return true; + } + + @Override + protected void requestInitialData() { + requestForMoreData(); + } + + @Override + public boolean mustShowProgressView() { + return true; + } + + @Override + public void requestForMoreData() { + switch (requestID) { + case ConstantsUtils.REQUEST_MEMBER_COLLECTIONS: + DataRepository.getInstance().getMemberCollections(mainActorID, bookmark); + break; + case REQUEST_MEMBER_FOLLOWED_COLLECTIONS: + DataRepository.getInstance().getPeopleFollowingCollections(mainActorID, bookmark); + break; + case REQUEST_POST_REPOSTING_COLLECTIONS: + //DataRepository.getInstance().get + PoinilaNetService.getRepostCollections(mainActorID, bookmark); + break; + } + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/CollectionPageFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/CollectionPageFragment.java new file mode 100755 index 0000000..25caecf --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/CollectionPageFragment.java @@ -0,0 +1,551 @@ +package com.shaya.poinila.android.presentation.view.fragments; + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.widget.NestedScrollView; +import android.support.v7.widget.RecyclerView; +import android.text.TextUtils; +import android.util.Log; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; + +import com.getbase.floatingactionbutton.FloatingActionButton; +import com.getbase.floatingactionbutton.FloatingActionsMenu; +import com.makeramen.roundedimageview.RoundedImageView; +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewProvider; +import com.shaya.poinila.android.presentation.uievent.CollectionClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.MemberClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.PositiveButtonClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.PostClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.PostComponentClickedUIEvent; +import com.shaya.poinila.android.presentation.view.dialog.DialogLauncher; +import com.shaya.poinila.android.presentation.view.help.Help; +import com.shaya.poinila.android.presentation.viewholder.BaseViewHolder; +import com.shaya.poinila.android.presentation.viewholder.PostsOfCollectionViewHolder; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.util.PoinilaPreferences; +import com.shaya.poinila.android.util.RandomUtils; +import com.shaya.poinila.android.util.StringUtils; +import com.squareup.otto.Subscribe; +import com.squareup.picasso.Picasso; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import butterknife.Bind; +import butterknife.OnClick; +import data.PoinilaNetService; +import data.event.BaseEvent; +import data.event.CollectionReceivedEvent; +import data.event.PostReceivedEvent; +import data.event.PostsReceivedEvent; +import data.model.Collection; +import data.model.ImageUrls; +import data.model.Loading; +import data.model.Member; +import data.model.Post; +import manager.DataRepository; +import manager.RequestSource; + +import static android.support.v7.widget.LinearLayoutManager.VERTICAL; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setFont; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setImage; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_ENTITY; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_REQUEST_ID; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_SECOND_ENTITY_ID; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_COLLECTION_POSTS; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_EXPLORE; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_MEMBER_POSTS; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_POST_RELATED_POSTS; +import static com.shaya.poinila.android.util.ResourceUtils.getStringFormatted; + +/** + * Created by hossein on 8/30/16. + */ +public class CollectionPageFragment extends ListBusFragment { + + @Nullable + @Bind(R.id.collection_name) + TextView collectionName; + + @Nullable + @Bind(R.id.image_big) + RoundedImageView collectionImage; + + @Nullable + @Bind(R.id.follow_button) + Button followButton; + @Nullable + @Bind(R.id.follow_button_img) + ImageView followButtonImg; + @Nullable + @Bind(R.id.edit_button) + Button editButton; + @Nullable + @Bind(R.id.remove_button) + Button removeButton; + + @Nullable + @Bind(R.id.collection_description) + TextView collectionDescription; + +// @Nullable +// @Bind(R.id.collection_info_bar) +// View collectionInfoView; + + @Nullable + @Bind(R.id.item_count) + TextView itemCountView; + + @Bind(R.id.nested_scroll_view) + NestedScrollView nestedScrollView; + + protected Collection collection; + + private int requestType; + private String mainEntityId; + private String secondEntityId; + private Set activeRequests; + + FloatingActionsMenu fabMenu; + FloatingActionButton addFromUrl; + FloatingActionButton addPost; + + private boolean showedHelp = false; + + public static CollectionPageFragment newInstance(String mainEntityId, int requestID) { + return newInstance(mainEntityId, null, requestID); + } + + + public static CollectionPageFragment newInstance(String mainEntityId, String secondEntityId, int requestID) { + CollectionPageFragment f = new CollectionPageFragment(); + Bundle arguments = new Bundle(); + arguments.putString(KEY_ENTITY, mainEntityId); + arguments.putString(KEY_SECOND_ENTITY_ID, secondEntityId); + arguments.putInt(KEY_REQUEST_ID, requestID); + //arguments.putBoolean(ConstantsUtils.KEY_IS_USER_COLLECTION, isCollectionOwnedByUser); + f.setArguments(arguments); + return f; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mainEntityId = getArguments().getString(KEY_ENTITY); + requestType = getArguments().getInt(KEY_REQUEST_ID); + secondEntityId = getArguments().getString(KEY_SECOND_ENTITY_ID); + + activeRequests = new HashSet<>(2); + setHasOptionsMenu(true); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + menu.clear(); + + // Inflate menu resource file. + inflater.inflate(R.menu.menu_post, menu); + menu.findItem(R.id.menu_item_report).setVisible(false); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // handle item selection + switch (item.getItemId()) { + case R.id.menu_item_share: + // Handle this selection + launchShareMenu(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + private void launchShareMenu() { + Intent shareIntent = new Intent(Intent.ACTION_SEND); + shareIntent.setType("text/plain"); + String extra = null; + extra = getString(R.string.checkout_this_collection) + "\n" + + getString(R.string.collection_share_url, + ConstantsUtils.POINILA_ORIGIN_ADDRESS, + Uri.encode(collection.owner.uniqueName), + Uri.encode(collection.name)) + "\n" + + getString(R.string.ponila_world_of_interest); + shareIntent.putExtra(Intent.EXTRA_TEXT, extra); + startActivity(Intent.createChooser(shareIntent, getString(R.string.share_dialog_title))); + } + + @Override + protected void initUI() { + super.initUI(); + + mRecyclerView = new RecyclerViewProvider(mRecyclerView).setAdapter(getRecyclerViewAdapter()). + setStaggeredLayoutManager(VERTICAL, getResources().getInteger(R.integer.column_count)). + bindViewToAdapter(); + mRecyclerView.getItemAnimator().setChangeDuration(0); + mRecyclerView.setNestedScrollingEnabled(false); + + nestedScrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() { + @Override + public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) { + if (scrollY == (v.getChildAt(0).getMeasuredHeight() - v.getMeasuredHeight())) { + onLoadMore(); + } + } + }); + + fabMenu = (FloatingActionsMenu)rootView.findViewById(R.id.fab_menu); + addFromUrl = (FloatingActionButton)rootView.findViewById(R.id.fab_add_post_from_site); + addPost = (FloatingActionButton)rootView.findViewById(R.id.fab_add_post); + + addPost.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + fabMenu.collapse(); + //DialogLauncher.launchNewPost(getChildFragmentManager(), null); + PageChanger.goToNewPost(getFragmentManager(), null); + } + }); + + addFromUrl.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + fabMenu.collapse(); + DialogLauncher.launchNewWebsitePost(getFragmentManager()); + } + }); + + getActivity().setTitle(R.string.collection); + collection = DataRepository.getInstance().getTempModel(Collection.class); + if (collection == null) + return; + fillCollection(collection); + setLoading(new Loading()); + } + + private boolean byDeepLink(String collectionIdOrName) { + return !StringUtils.isInteger(collectionIdOrName); + } + + @Override + protected RecyclerView.OnScrollListener getRecyclerViewListener() { + return RecyclerViewProvider.staggeredListEndDetectorListener(getRecyclerViewAdapter(), this); + } + + private void fillCollection(Collection collection) { + setText(itemCountView, getStringFormatted(R.string.posts_formatted, collection.postCount)); + + if (!TextUtils.isEmpty(collection.description)) { + setText(collectionDescription, collection.description); + setFont(collectionDescription, getString(R.string.default_bold_font_path)); + } + + if(!TextUtils.isEmpty(collection.name)){ + setText(collectionName, collection.name); + setFont(collectionName, getString(R.string.default_font_path)); + } + + if (!DataRepository.isMyCollection(collection)) { + followButton.setVisibility(View.VISIBLE); + followButtonImg.setVisibility(View.VISIBLE); + updateFollowButton(); + } else { + editButton.setVisibility(View.VISIBLE); + removeButton.setVisibility(View.VISIBLE); + } + + setImage(collectionImage, collection.coverImageUrls, ImageUrls.ImageType.COLLECTION, ImageUrls.ImageSize.AVATAR); + //getActivity().setTitle(getString(R.string.title_activity_collection, mainEntityId)); + } + + private void updateFollowButton() { + setText(followButton, collection.followedByMe + ? getString(R.string.unfollow_item) + : getString(R.string.follow_item)); + + followButtonImg.setSelected(collection.followedByMe); + followButton.setSelected(collection.followedByMe); + } + + @Override + public void requestForMoreData() { + + int requestId = RandomUtils.getRandomInt(); + activeRequests.add(requestId); + + DataRepository.getCollectionPosts( + mainEntityId, + byDeepLink(mainEntityId) ? secondEntityId : null, + bookmark, + BaseEvent.ReceiverName.CollectionPageFragment); + } + + @Override + public RecyclerViewAdapter createAndReturnRVAdapter() { + final BaseEvent.ReceiverName receiverName = BaseEvent.ReceiverName.CollectionPageFragment; + return new RecyclerViewAdapter(getActivity(), R.layout.post_in_collection) { + @Override + protected BaseViewHolder getProperViewHolder(View v, int viewType) { + if(viewType == VIEW_TYPE_LOAD_PROGRESS){ + return new BaseViewHolder.EmptyViewHolder(v); + } + return new PostsOfCollectionViewHolder(v, receiverName); + } + + @Override + protected boolean isStaggeredGridLayoutManager() { + return true; + } + }; + } + + @Override + protected boolean sendsRequestAutomatically() { + return true; + } + + @Override + protected void requestInitialData() { + // deep links are unique with two parameters -> user name, collection name +// Log.i(getClass().getName(), "collection unige name : " + (byDeepLink(mainEntityId) ? secondEntityId : null)); + DataRepository.getInstance().getCollection( + mainEntityId, + byDeepLink(mainEntityId) ? secondEntityId : null, + RequestSource.FORCE_ONLINE); + + requestForMoreData(); + } + + @Override + public boolean mustShowProgressView() { + return false; + } + + @Override + public int getLayoutID() { + return R.layout.fragment_collection_detail_owned; + } + + + @Override + public void onSuccessfulInitData(BaseEvent baseEvent) { + super.onSuccessfulInitData(baseEvent); + + if (baseEvent instanceof CollectionReceivedEvent) { + collection = ((CollectionReceivedEvent) baseEvent).collection; + fillCollection(collection); + } else if (baseEvent instanceof PostReceivedEvent) { + if (getRecyclerViewAdapter().isEmpty()) { // click on notification participant + getRecyclerViewAdapter().addItem(((PostReceivedEvent) baseEvent).post, 0); + } else { + //TODO: replace nemikone ba avvalin item related post ha? + getRecyclerViewAdapter().setItem(((PostReceivedEvent) baseEvent).post, 0); + } + } + + /*collection.description = event.collection.description; + collection.circleIDs = event.collection.circleIDs; + collection.name = event.collection.name; + collection.topic = event.collection.topic; + collection.coverImageUrls = event.collection.coverImageUrls;*/ + + } + + @Subscribe + public void onCollectionReceived(CollectionReceivedEvent event){ + onGettingInitDataResponse(event); + } + + + @Subscribe + public void onPostsReceived(PostsReceivedEvent event) { + + if(event.posts.size() >= 25 && mRecyclerView.getAdapter().getItemCount() == 0){ + setLoading(new Loading()); + } + + populateIfNecessary(event); // was necessary earlier because response came from server lacked info about posts collection + onGettingInitDataResponse(event); + onGettingListDataResponse(event, event.bookmark); + + } + + public void populateIfNecessary(PostsReceivedEvent event) { + if (collection != null) { + for (Post post : event.posts) { + post.collection = collection; + //post.author = collection.owner; + } + } + } + + @Override + public void onSuccessfulListData(BaseEvent baseEvent, String newBookmark) { + super.onSuccessfulListData(baseEvent, newBookmark); + List posts = ((PostsReceivedEvent) baseEvent).posts; + + getRecyclerViewAdapter().addItems(posts); + + if(!showedHelp && !PoinilaPreferences.getHelpStatus(getClass().getName()) && requestType == REQUEST_COLLECTION_POSTS){ + showHelp(); + showedHelp = true; + PoinilaPreferences.putHelpStatus(getClass().getName(), true); + } + + } + + @Nullable + @OnClick(R.id.remove_button) + public void onRemoveCollection() { + // must not happen but anyway... :) + if (DataRepository.isUserAnonymous()) { + Logger.toastError(R.string.error_guest_action); + return; + } + + DialogLauncher.launchDeleteCollection(getFragmentManager()); + } + + @Nullable + @OnClick(R.id.follow_button) + public void onFollowCollection(Button followButton) { + if (DataRepository.isUserAnonymous()) { + Logger.toastError(R.string.error_guest_action); + return; + } + + if (collection.followedByMe) + PoinilaNetService.unfollowCollection(collection.getId()); + else + PoinilaNetService.followCollection(collection.getId()); + + collection.followedByMe = !collection.followedByMe; + + updateFollowButton(); + } + + + @Nullable + @OnClick(R.id.edit_button) + public void onEditCollection() { + // must not happen but anyway... :) + if (DataRepository.isUserAnonymous()) { + Logger.toastError(R.string.error_guest_action); + return; + } + + DialogLauncher.launchEditCollectionDialog(getFragmentManager(), collection); + } + + @Subscribe + public void onProfilePicClickedEvent(MemberClickedUIEvent event) { + Member member = ((Post)getRecyclerViewAdapter().getItem(event.adapterPosition)).author; + PageChanger.goToProfile(getActivity(), member); + } + + @Subscribe + public void onPostClicked(PostClickedUIEvent event) { + Post post = (Post)getRecyclerViewAdapter().getItem(event.adapterPosition); + PageChanger.goToPost(getActivity(), post); + } + + @Subscribe + public void onCollectionClicked(CollectionClickedUIEvent event) { + Collection collection = ((Post)getRecyclerViewAdapter().getItem(event.adapterPosition)).collection; + PageChanger.goToCollection(getActivity(), collection); + } + + @Subscribe + public void onPositiveDialogButtonClicked(PositiveButtonClickedUIEvent event) { + PoinilaNetService.deleteCollection(collection); + getActivity().finish(); + } + + @Subscribe + public void onPostDetailsComponentClickEvent(PostComponentClickedUIEvent event) { + if (DataRepository.isUserAnonymous() && PostComponentClickedUIEvent.Type.guestCantPerformActions.contains(event.type)) { + Logger.toastError(R.string.error_guest_action); + return; + } + + + Post post = (Post)getRecyclerViewAdapter().getItem(0); + switch (event.type) { + case FaversList: + PageChanger.goToLikersList(getActivity(), post.faveCount, post.getId()); + break; + case Comments: + PageChanger.goToCommentList(getActivity(), post.commentCount, post.getId()); + getRecyclerViewAdapter().notifyItemChanged(0); + break; + case RepostersList: + PageChanger.goToRepostList(getActivity(), post.repostCount, post.getId()); + break; + case Repost: + DialogLauncher.launchRepostDialog(getFragmentManager(), post); + getRecyclerViewAdapter().notifyItemChanged(0); + break; + case Poster: + Member member = post.author; + PageChanger.goToProfile(getActivity(), member); + break; + case Collection: + Collection collection = post.collection; + PageChanger.goToCollection(getActivity(), collection); + break; + case OriginalCollection: + collection = post.originalCollection; + PageChanger.goToCollection(getActivity(), collection); + break; + case Reference: + PageChanger.goToInlineBrowser(getActivity(), post.originalWebpage.toLowerCase(), post.getId(), post.name); + break; + case FullImage: + PageChanger.goToFullImage(getActivity(), post.imagesUrls.properPostImage(ImageUrls.ImageSize.FULL_SIZE).url); + break; + } + } + + + @Override + protected boolean isInitDataResponseValid(BaseEvent baseEvent) { + boolean res = requestType == REQUEST_COLLECTION_POSTS; + return res && super.isInitDataResponseValid(baseEvent); + } + + @Override + protected boolean isListDataResponseValid(BaseEvent baseEvent, String responseBookmark) { + boolean res; + PostsReceivedEvent event = (PostsReceivedEvent) baseEvent; + res = event.receiverName == BaseEvent.ReceiverName.CollectionPageFragment; + + /*switch (requestID) { + case ((PostsReceivedEvent) baseEvent).receiverName + }*/ + return res && super.isListDataResponseValid(baseEvent, responseBookmark); + + } + + + + public void showHelp(){ + Help.getInstance().showPostsOfCollectionHelp(getActivity(), followButton); + } + + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/DashboardFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/DashboardFragment.java new file mode 100755 index 0000000..c9ebebf --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/DashboardFragment.java @@ -0,0 +1,489 @@ +package com.shaya.poinila.android.presentation.view.fragments; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.support.annotation.LayoutRes; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.StaggeredGridLayoutManager; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; + +import com.shaya.poinila.android.presentation.AndroidUtilities; +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +//import com.shaya.poinila.android.presentation.presenter.HelpProvider; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewProvider; +import com.shaya.poinila.android.presentation.uievent.CollectionClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.MemberClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.PostClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.RatePonilaEvent; +import com.shaya.poinila.android.presentation.uievent.UpdateUICommentEvent; +import com.shaya.poinila.android.presentation.uievent.UpdateUiRepostEvent; +import com.shaya.poinila.android.presentation.uievent.sync.PostActionSyncEvent; +import com.shaya.poinila.android.presentation.view.help.Help; +import com.shaya.poinila.android.presentation.view.dialog.DialogLauncher; +import com.shaya.poinila.android.presentation.view.fragments.DashboardFragment.DashboardRecyclerViewAdapter.AskIfUserLikesPonila; +import com.shaya.poinila.android.presentation.viewholder.AskUserLikesPonilaViewHolder; +import com.shaya.poinila.android.presentation.viewholder.BaseViewHolder; +import com.shaya.poinila.android.presentation.viewholder.DashboardPostViewHolder; +import com.shaya.poinila.android.presentation.viewholder.RatePonilaViewHolder; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.ConnectionUitls; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.util.PoinilaPreferences; +import com.shaya.poinila.android.util.StringUtils; +import com.shaya.poinila.android.utils.uisynchronize.UISynchronizeBus; +import com.squareup.otto.Subscribe; + +import java.io.Serializable; + +import data.event.BaseEvent; +import data.event.ContentReceivedEvent; +import data.event.DashboardEvent; +import data.model.Collection; +import data.model.Loading; +import data.model.Member; +import data.model.Post; +import manager.DataRepository; + +import static com.shaya.poinila.android.presentation.view.fragments.PostListFragment.findPostInAdapter; + +/** + * Important Documentation + *

On Creating this fragment, we request the server and cache both. we control reading + * from cache by {@link #loadFromCache} (firstly true) and reading from server by + * {@link ConnectionUitls#isNetworkOnline()} method.


+ * If response is from the cache, we sets the received items count as offset for next db query. + * (working in offline mode)
+ * If response is from the server (which comes after response from cache logically!), loadFromCache + * is set to false so we doesn't read db items anymore and items array (populating with cached items) + * is cleared. In this case response bookmark is kept and + * would be send with next request.
+ * When refreshing (meaningful in online mode) items are added to list's head. + */ +public class DashboardFragment extends BusRefreshableListFragment { + + boolean loadFromCache; + // offset in sql query when scrolling in offline mode + private int cachedItems; + protected boolean showedHelp = false; +// private HelpProvider mHelpProvider; + + + +// @Bind(R.id.fab_menu) +// FloatingActionsMenu fabMenu; +// +// @Bind(R.id.fab_add_post_from_site) +// FloatingActionButton addFromUrl; +// +// @Bind(R.id.fab_add_post) +// FloatingActionButton addPost; + + // TODO: Rename and change types and number of parameters + public static DashboardFragment newInstance() { + return new DashboardFragment(); + } + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + loadFromCache = true; + + } + + @Override + public int getLayoutID() { + return R.layout.recycler_view_full; + } + + @Override + protected void initUI() { + super.initUI(); + mRecyclerView = new RecyclerViewProvider(mRecyclerView). + setStaggeredLayoutManager(StaggeredGridLayoutManager.VERTICAL, + getResources().getInteger(R.integer.column_count)). + setAdapter(getRecyclerViewAdapter()). + bindViewToAdapter(); + } + + @Override + public void onStart() { + super.onStart(); + + BusProvider.getSyncUIBus().register(this); + } + + // TODO: candidate of refactoring. Can use static method from `RecyclerViewProvider` instead. + @Override + protected RecyclerView.OnScrollListener getRecyclerViewListener() { + return RecyclerViewProvider.staggeredListEndDetectorListener(getRecyclerViewAdapter(), this); + } + + @Override + public void requestForMoreData() { + DataRepository.getInstance().getSuggestions(ConnectionUitls.isNetworkOnline(), + loadFromCache, bookmark, cachedItems); + } + + @Subscribe + public void onPostActionSyncEvent(PostActionSyncEvent event){ + + int position = ((DashboardRecyclerViewAdapter)getRecyclerViewAdapter()) + .getItemPositionByPostId(event.post.id); + + if(position == -1) return; + + Post post = (Post)getRecyclerViewAdapter().getItem(position); + post.faveCount += event.post.favedByMe ? 1: -1; + post.favedByMe = event.post.favedByMe; + getRecyclerViewAdapter().setItem(post, position); + getRecyclerViewAdapter().notifyItemChanged(position); + } + + @Subscribe + public void onUpdateUiRepostEvent(UpdateUiRepostEvent event){ + +// int position = ((DashboardRecyclerViewAdapter)getRecyclerViewAdapter()) +// .getItemPositionByPostId(event.postId); +// +// if(position == -1) return; +// +// Post post = (Post)getRecyclerViewAdapter().getItem(position); +// post.repostCount += event.isSuccess ? 1: -1; +// getRecyclerViewAdapter().setItem(post, position); +// getRecyclerViewAdapter().notifyItemChanged(position); + } + + @Subscribe + public void onUpdateUICommentEvent(UpdateUICommentEvent event){ + + int position = ((DashboardRecyclerViewAdapter)getRecyclerViewAdapter()) + .getItemPositionByPostId(Integer.parseInt(event.postId)); + + if(position == -1) return; + + if(event.action == UpdateUICommentEvent.INCREMENT_COMMENTS) + ((Post)getRecyclerViewAdapter().getItem(position)).commentCount++; + else + ((Post)getRecyclerViewAdapter().getItem(position)).commentCount--; + + getRecyclerViewAdapter().notifyItemChanged(position); + } + + @Override + protected void requestInitialData() { + refresh(); + } + +// @OnClick(R.id.fab_add_post) public void onAddPost(){ +// fabMenu.collapse(); +// //DialogLauncher.launchNewPost(getChildFragmentManager(), null); +// PageChanger.goToNewPost(getFragmentManager(), null); +// } +// +// @OnClick(R.id.fab_add_post_from_site) public void onAddPostFromUrl(){ +// // todo: dialog, its layout and how to get a url imagesUrls; +// fabMenu.collapse(); +// DialogLauncher.launchNewWebsitePost(getFragmentManager()); +// } + + @Subscribe + public void onPostClickedEvent(PostClickedUIEvent event) { + if (event.receiverName != BaseEvent.ReceiverName.DashboardFragment) return; + PageChanger.goToPost(getActivity(), (Post) getRecyclerViewAdapter().getItem(event.adapterPosition)); + } + + @Subscribe + public void onProfilePicClickedEvent(MemberClickedUIEvent event) { + if (event.receiverName != BaseEvent.ReceiverName.DashboardFragment) return; + Member member =((Post)getRecyclerViewAdapter().getItem(event.adapterPosition)).author; + PageChanger.goToProfile(getActivity(), member); + } + + @Subscribe + public void onPostCollectionClickedEvent(CollectionClickedUIEvent event) { + if (event.receiverName != BaseEvent.ReceiverName.DashboardFragment) return; + Collection collection = ((Post)getRecyclerViewAdapter().getItem(event.adapterPosition)).collection; + PageChanger.goToCollection(getActivity(), collection); + } + + @Subscribe + public void onSuggestedPostsReceived(DashboardEvent event) { + super.onGettingInitDataResponse(event); + super.onGettingListDataResponse(event, event.bookmark); + } + + @Override + protected boolean isListDataResponseValid(BaseEvent baseEvent, String responseBookmark) { + DashboardEvent event = ((DashboardEvent) baseEvent); + return event.getData() != null && event.getData().size() != 0 && super.isListDataResponseValid(event, responseBookmark); + } + + @SuppressWarnings("unchecked") + @Override + public RecyclerViewAdapter createAndReturnRVAdapter() { + return new DashboardRecyclerViewAdapter(getActivity(), -1, getFragmentManager()); + } + + + @Override + public void onSuccessfulListData(BaseEvent baseEvent, String newBookmark) { + super.onSuccessfulListData(baseEvent, newBookmark); + DashboardEvent dashboardEvent = ((DashboardEvent) baseEvent); + if (dashboardEvent.isFromCache) { // from cache/db + setLoading(new Loading()); + getRecyclerViewAdapter().addItems(dashboardEvent.getData()); + cachedItems = cachedItems + dashboardEvent.getData().size(); + } else { // from server + loadFromCache = false; + // hiding swipe refresh layout and adding items to head + if (swipeRefreshLayout.isRefreshing()) { + onRefreshFinished(); + + + //mAdapter.addItemsToListHead(event.getData()); + getRecyclerViewAdapter().resetData(dashboardEvent.getData()); + // TODO: needs better approach. why we should clear on every refresh? + + setLoading(new Loading()); + + // checking if it's the time to show "ask rating" + if (DataRepository.shouldAskForRating()) { + //noinspection unchecked + getRecyclerViewAdapter().getUngenericedItems().add(0, new AskIfUserLikesPonila()); + getRecyclerViewAdapter().notifyItemChanged(0); + } + } else { + getRecyclerViewAdapter().addItems(dashboardEvent.getData()); + } + + + DataRepository.getInstance().saveSuggestions(dashboardEvent.getData()); + } + + if(!showedHelp && !PoinilaPreferences.getHelpStatus(getClass().getName())){ + this.showedHelp = true; + PoinilaPreferences.putHelpStatus(getClass().getName(), true); + showHelp(); + } + + } + + @Override + protected boolean sendsRequestAutomatically() { + return true; + } + + @Override + public void refresh() { + bookmark = null; // TODO: is it necessary?! + swipeRefreshLayout.setRefreshing(true); + requestForMoreData(); + if (!ConnectionUitls.isNetworkOnline()) + Logger.toast(R.string.warning_connect_to_network); + //refreshRequest = true; + } + + @Subscribe + public void onContentReceivedEvent(final ContentReceivedEvent event) { + final int postIndex = findPostInAdapter(getRecyclerViewAdapter().getItems(), event.postID); + if (postIndex != -1) { + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + ((Post) getRecyclerViewAdapter().getItem(postIndex)).content = StringUtils.removeHtmlDirAttribute(event.content); + getRecyclerViewAdapter().notifyItemChanged(postIndex); + } + }); + } + } + + + + + @Override + public boolean mustShowProgressView() { + return false; + } + + // no need implement since it doesn't show progressBar. Bad design :) + @Override + public ViewGroup getLoadableView() { + return null; + } + + @Subscribe + public void onRateApplication(RatePonilaEvent event) { + // TODO: read market destination from server response + AndroidUtilities.rateApplication(getActivity(), DataRepository.getDestinationMarket()); + } + + public void showHelp() { + + if(getRecyclerViewAdapter().getItem(0) instanceof Post){ + mRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + + Help.getInstance().showDashboardHelp(getActivity(), mRecyclerView.getLayoutManager().findViewByPosition(0)); + viewedHelp = true; + + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) { + mRecyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this); + } else { + mRecyclerView.getViewTreeObserver().removeGlobalOnLayoutListener(this); + } + } + }); + + } + } + + @Override + public UISynchronizeBus.UI_SYNCHRONIZE_ACTION getSynchronizeAction() { + return UISynchronizeBus.UI_SYNCHRONIZE_ACTION.UPDATE_DASHBOARD_POST; + } + + @Override + public void loadDataForSynchronize(Serializable data, UISynchronizeBus.UI_SYNCHRONIZE_ACTION action) { + super.loadDataForSynchronize(data, action); + Log.i(getClass().getName(), "loadDataForSynchronize"); + + } + + public static class DashboardRecyclerViewAdapter extends RecyclerViewAdapter { + public static final int VIEW_TYPE_POST = 1; + public static final int VIEW_TYPE_RATE_APP = 2; + private static final int VIEW_TYPE_LIKES_APP = 3; + private final android.support.v4.app.FragmentManager fragmentManager; + + public DashboardRecyclerViewAdapter(Context context, @LayoutRes int itemLayoutID, android.support.v4.app.FragmentManager fragmentManager) { + super(context, itemLayoutID); + this.fragmentManager = fragmentManager; + } + + @Override + protected BaseViewHolder getProperViewHolder(View v, int viewType) { + return null; + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + if (getItemViewType(position) == VIEW_TYPE_RATE_APP || getItemViewType(position) == VIEW_TYPE_LIKES_APP || getItemViewType(position) == VIEW_TYPE_LOAD_PROGRESS) { + StaggeredGridLayoutManager.LayoutParams layoutParams = (StaggeredGridLayoutManager.LayoutParams) holder.itemView.getLayoutParams(); + layoutParams.setFullSpan(true); + } else { + ((DashboardPostViewHolder) holder).fill(((Post) getItem(position))); + } + } + + public int getItemPositionByPostId(int id){ + int length = getItems().size(); + for( int i = 0 ; i < length ; i++){ + if(getItem(i) instanceof Post){ + Post post = (Post)getItem(i); + if(post.id == id) return i; + } + } + return -1; + } + + @Override + protected boolean isStaggeredGridLayoutManager() { + return true; + } + + // TODO: can be written cleaner + @Override + public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + if (viewType == VIEW_TYPE_LIKES_APP) { + final AskUserLikesPonilaViewHolder holder = new AskUserLikesPonilaViewHolder(mLayoutInflater.inflate(R.layout.ask_if_user_likes_ponila, parent, false)); + holder.dontKnowButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + removeItem(holder.getAdapterPosition()); + // TODO increase show rate dialog ask interval + DataRepository.updateAskRatingThreshold(false); + } + }); + holder.positiveButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + setItem(new AskIfUserRatesPonila(), 0); + } + }); + holder.negativeButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + removeItem(holder.getAdapterPosition()); + DialogLauncher.launchContactUsDialog(fragmentManager); + // TODO disable asking (later we want to ask in every update) + DataRepository.updateAskRatingThreshold(true); + } + }); + return holder; + } else if (viewType == VIEW_TYPE_RATE_APP) { + final RatePonilaViewHolder holder = new RatePonilaViewHolder(mLayoutInflater.inflate(R.layout.ask_if_user_rates_ponila, parent, false)); + holder.notNowButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + removeItem(holder.getAdapterPosition()); + DataRepository.updateAskRatingThreshold(false); + } + }); + holder.positiveButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + removeItem(holder.getAdapterPosition()); + DataRepository.updateAskRatingThreshold(true); + BusProvider.getBus().post(new RatePonilaEvent()); + } + }); + holder.negativeButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + removeItem(holder.getAdapterPosition()); + DataRepository.updateAskRatingThreshold(true); + } + }); + return holder; + }else if(viewType == VIEW_TYPE_LOAD_PROGRESS){ + return new BaseViewHolder.EmptyViewHolder(mLayoutInflater.inflate(R.layout.progress, parent, false)); + }else + return new DashboardPostViewHolder(mLayoutInflater.inflate(R.layout.post_dashboard, parent, false), BaseEvent.ReceiverName.DashboardFragment); + + } + + + + @Override + public int getItemViewType(int position) { + int type = super.getItemViewType(position); + if(type == VIEW_TYPE_LOAD_PROGRESS){ + return super.getItemViewType(position); + } + if (getItem(position) instanceof Post) + return VIEW_TYPE_POST; + else if (getItem(position) instanceof AskIfUserLikesPonila) + return VIEW_TYPE_LIKES_APP; + else + return VIEW_TYPE_RATE_APP; + + } + + /* do not delete */ + public static class AskIfUserLikesPonila { + } + + public static class AskIfUserRatesPonila { + } + } + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/EditInterestsFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/EditInterestsFragment.java new file mode 100755 index 0000000..a9acf75 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/EditInterestsFragment.java @@ -0,0 +1,125 @@ +package com.shaya.poinila.android.presentation.view.fragments; + +import android.os.Bundle; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.StaggeredGridLayoutManager; +import android.view.View; + +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewProvider; +import com.shaya.poinila.android.presentation.uievent.RemoveTagEvent; +import com.shaya.poinila.android.presentation.viewholder.RemovableInterestViewHolder; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.ResourceUtils; +import com.squareup.otto.Subscribe; + +import butterknife.Bind; +import butterknife.OnClick; +import data.PoinilaNetService; +import data.event.BaseEvent; +import data.event.UserInterestsReceivedEvent; +import data.model.ImageTag; +import data.model.Tag; +import manager.DataRepository; + +/** + * Created by iran on 2015-09-08. + */ +public class EditInterestsFragment extends ListBusFragment { + + @Bind(R.id.new_interest) View newInterest; + private String actorID; + + @Override + protected RecyclerView.OnScrollListener getRecyclerViewListener() { + return new RecyclerView.OnScrollListener() {}; + } + +/* @Override + protected RecyclerView.OnScrollListener getRecyclerViewListener() { + return RecyclerViewProvider.gridListEndDetectionListener(getRecyclerViewAdapter(), this); + }*/ + + @Override + public int getLayoutID() { + return R.layout.activity_edit_interests; + } + + @Override + protected void initUI() { + actorID = getArguments().getString(ConstantsUtils.KEY_ENTITY); + + mRecyclerView = new RecyclerViewProvider(mRecyclerView). + setGridLayoutManager(StaggeredGridLayoutManager.VERTICAL, + ResourceUtils.getInteger(R.integer.column_count)). + setAdapter(getRecyclerViewAdapter()). + bindViewToAdapter(); + } + + public static android.support.v4.app.Fragment newInstance(String actorID) { + Bundle b = new Bundle(); + b.putString(ConstantsUtils.KEY_ENTITY, actorID); + EditInterestsFragment f = new EditInterestsFragment(); + f.setArguments(b); + return f; + } + + @OnClick(R.id.new_interest) public void onNewInterest(){ + initDataResponseReceived = false; + DataRepository.getInstance().putTempModel(getRecyclerViewAdapter().getItems()); + PageChanger.goToSelectInterest(getActivity(), false); + } + + @Subscribe public void onUserInterestesReceived(UserInterestsReceivedEvent event){ + onGettingInitDataResponse(event); + } + + @Subscribe public void onDeleteInterest(RemoveTagEvent event){ + Tag tag = getRecyclerViewAdapter().getItem(event.adapterPosition); + PoinilaNetService.removeInterest(tag); + getRecyclerViewAdapter().removeItem(event.adapterPosition); + } + + + + /*-------------------*/ + + @Override + public void onSuccessfulInitData(BaseEvent baseEvent) { + super.onSuccessfulInitData(baseEvent); + getRecyclerViewAdapter().resetData(((UserInterestsReceivedEvent) baseEvent).userInterests); + } + + @Override + protected boolean sendsRequestAutomatically() { + return true; + } + + @Override + protected void requestInitialData() { + PoinilaNetService.getMemberInterests(actorID); + } + + @Override + public boolean mustShowProgressView() { + return true; + } + + @Override + public void requestForMoreData() { + + } + + @Override + public RecyclerViewAdapter createAndReturnRVAdapter() { + return new RecyclerViewAdapter(getActivity(), R.layout.removable_imaged_interest) { + @Override + protected RemovableInterestViewHolder getProperViewHolder(View v, int viewType) { + return new RemovableInterestViewHolder(v); + } + }; + }//implements LoaderList { + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/InvitationNotifListFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/InvitationNotifListFragment.java new file mode 100755 index 0000000..5e07f24 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/InvitationNotifListFragment.java @@ -0,0 +1,117 @@ +package com.shaya.poinila.android.presentation.view.fragments; + +import android.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.View; +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewProvider; +import com.shaya.poinila.android.presentation.uievent.AnswerFriendshipUIEvent; +import com.shaya.poinila.android.presentation.uievent.NotifActorClickedUIEvent; +import com.shaya.poinila.android.presentation.viewholder.InviteNotifViewHolder; +import com.squareup.otto.Subscribe; + +import data.PoinilaNetService; +import data.event.AnswerFriendRequestResponse; +import data.event.BaseEvent; +import data.event.MyFriendshipRequestsEvent; +import data.model.InvitationNotif; +import data.model.Member; + +import static android.support.v7.widget.LinearLayoutManager.VERTICAL; + +/** + * Created by iran on 2015-09-29. + */ +public class InvitationNotifListFragment extends ListBusFragment { + + @Override + protected RecyclerView.OnScrollListener getRecyclerViewListener() { + return RecyclerViewProvider.linearListEndDetectorListener(getRecyclerViewAdapter(), this); + } + + @Override + public int getLayoutID() { + return R.layout.recycler_view_full; + } + + @Override + protected void initUI() { + super.initUI(); + mRecyclerView = new RecyclerViewProvider(mRecyclerView). + setAdapter(getRecyclerViewAdapter()). + setLinearLayoutManager(VERTICAL).bindViewToAdapter(); + } + + public static android.support.v4.app.Fragment newInstance() { + return new InvitationNotifListFragment(); + } + + @Subscribe public void onNotifsReceived(MyFriendshipRequestsEvent event){ + onGettingInitDataResponse(event); + onGettingListDataResponse(event, event.bookmark); + } + + @Override + public void onSuccessfulListData(BaseEvent baseEvent, String newBookmark) { + super.onSuccessfulListData(baseEvent, newBookmark); + getRecyclerViewAdapter().addItems(((MyFriendshipRequestsEvent) baseEvent).data); + } + + @Override + public void onEndListData() { + + } + + @Subscribe public void onAnswerFriendRequest(AnswerFriendshipUIEvent event){ + PoinilaNetService.answerFriendRequest( + getRecyclerViewAdapter().getItem(event.adapterPosition).member.id, event.accept, -1); + //ViewUtils.removeView(mInvitationsNotificationsContainer, event.adapterPosition); + clickedItemPosition = event.adapterPosition; + } + + @Subscribe public void onAnswerFriendRequestResponse(AnswerFriendRequestResponse event){ + if (event.succeed){ + getRecyclerViewAdapter().removeItem(clickedItemPosition); + // TODO: on accepting as a friend, chanage item view to "you and felani are now friends" + } + } + + @Subscribe public void onMainActorClicked(NotifActorClickedUIEvent event){ + Member member = getRecyclerViewAdapter().getItem(event.adapterPosition).member; + if (member == null) return; // invitations accepted friendship has got no main actor. + PageChanger.goToProfile(getActivity(), member.getId()); + } + +//--------------- + @Override + protected boolean sendsRequestAutomatically() { + return true; + } + + @Override + protected void requestInitialData() { + requestForMoreData(); + } + + @Override + public boolean mustShowProgressView() { + return true; + } + + @Override + public void requestForMoreData() { + PoinilaNetService.getMyFriendshipRequests(bookmark); + } + + @Override + public RecyclerViewAdapter createAndReturnRVAdapter() { + return new RecyclerViewAdapter(getActivity(), R.layout.notif_requested_tobe_your_friend) { + @Override + protected InviteNotifViewHolder getProperViewHolder(View v, int viewType) { + return new InviteNotifViewHolder(v); + } + }; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/ListBusFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/ListBusFragment.java new file mode 100755 index 0000000..95c8474 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/ListBusFragment.java @@ -0,0 +1,118 @@ +package com.shaya.poinila.android.presentation.view.fragments; + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.ViewGroup; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.view.LoaderList; + +import butterknife.Bind; +import data.event.BaseEvent; +import data.model.Loading; + +/** + * Created by iran on 2015-08-06. + */ +public abstract class ListBusFragment extends BusFragment implements LoaderList{ + private static final String CLICKED_ITEM_POSITION = "clicked item position"; + protected boolean requestingIsLocked = false; + public String bookmark; + protected int clickedItemPosition = -1; + @Bind(R.id.recycler_view) protected RecyclerView mRecyclerView; + private RecyclerViewAdapter mAdapter; + protected boolean hasLoading = false; + + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (savedInstanceState != null) + clickedItemPosition = savedInstanceState.getInt(CLICKED_ITEM_POSITION); + } + + @Override + protected void initUI() { + mRecyclerView.removeAllViews(); // clear view after rotation and other forms of activity recreation + // but may be it's better to just call notifyDataSetChange on related adapter + if(getRecyclerViewListener() != null) + mRecyclerView.addOnScrollListener(getRecyclerViewListener()); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt(CLICKED_ITEM_POSITION, clickedItemPosition); + } + + /** + * Called in {@link #onStart()} after {@link #initUI()} so it's safe to assume class variables + * are initialized. + * @return + */ + protected abstract RecyclerView.OnScrollListener getRecyclerViewListener(); + + public void onLoadMore(){ + if (!requestingIsLocked) { + requestingIsLocked = true; + requestForMoreData(); + } + } + + public void onSuccessfulListData(BaseEvent baseEvent, String newBookmark){ + requestingIsLocked = false; + bookmark = newBookmark; + } + + public void onEndListData(){ + removeLoading(); + } + + public void setLoading(T loading){ + if(!hasLoading || !(getRecyclerViewAdapter().getItem(getRecyclerViewAdapter().getItemCount() - 1) instanceof Loading)) { + hasLoading = true; + getRecyclerViewAdapter().setLoading(loading); + } + } + + public void removeLoading(){ + if(hasLoading) { + hasLoading = false; + getRecyclerViewAdapter().removeLoading(); + } + } + + public abstract void requestForMoreData(); + + @Override + public ViewGroup getLoadableView() { + return mRecyclerView; + } + + public RecyclerViewAdapter getRecyclerViewAdapter(){ + if (mAdapter == null) + mAdapter = createAndReturnRVAdapter(); + return mAdapter; + } + + protected void onGettingListDataResponse(BaseEvent event, String responseBookmark) { + if (isListDataResponseValid(event, responseBookmark)) + onSuccessfulListData(event, responseBookmark); + else + onEndListData(); + } + + protected boolean isListDataResponseValid(BaseEvent baseEvent, String responseBookmark){ + // bookmark != null && + return checkBookMark(this.bookmark, responseBookmark); // this.bookmark may be null + } + + public boolean checkBookMark(String pageBookmark, String serverBookmark) { + return serverBookmark == null || !serverBookmark.equals(pageBookmark); + } + + public abstract RecyclerViewAdapter createAndReturnRVAdapter(); +} + diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/LoginFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/LoginFragment.java new file mode 100755 index 0000000..4bf4b3e --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/LoginFragment.java @@ -0,0 +1,345 @@ +package com.shaya.poinila.android.presentation.view.fragments; + + +import android.app.Activity; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.v4.app.ActivityCompat; +import android.text.InputType; +import android.util.Log; +import android.util.Patterns; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.TextView; + +import com.google.android.gms.auth.api.signin.GoogleSignInAccount; +import com.google.android.gms.auth.api.signin.GoogleSignInOptions; +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.api.GoogleApiClient; +import com.mobsandgeeks.saripaar.annotation.Email; +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.SmsReceiver; +import com.shaya.poinila.android.presentation.uievent.GoogleLoginSucceedEvent; +import com.shaya.poinila.android.presentation.view.activity.SignUpLoginActivity; +import com.shaya.poinila.android.util.ConnectionUitls; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.util.PoinilaPreferences; +import com.shaya.poinila.android.utils.PonilaAccountManager; +import com.squareup.otto.Subscribe; + +import butterknife.Bind; +import butterknife.OnClick; +import data.PoinilaNetService; +import data.event.LoginFailedEvent; +import data.event.LoginSucceedEvent; +import data.event.MyInfoReceivedEvent; +import manager.DataRepository; +import uk.co.chrisjenx.calligraphy.CalligraphyConfig; +import uk.co.chrisjenx.calligraphy.CalligraphyUtils; + +public class LoginFragment extends BusFragment + implements GoogleApiClient.OnConnectionFailedListener, PonilaAccountManager.OnGoogleSignInResult {//implements BackForthButtonsBox.OnBackForthListener + + + /*@Bind(R.id.signup_login_buttons) + BackForthButtonsBox backForthButtonsBox;*/ + @Bind(R.id.card_title) + TextView title; + + @Bind(R.id.button_signup) + Button signupButton; + @Bind(R.id.button_login) + Button loginButton; + @Bind(R.id.button_guest_login) + Button guestLoginButton; + + @Email + @Bind(R.id.username_input) + EditText username; + + @Bind(R.id.toggle_visibility) + ImageView toggleVisibilityBtn; + + // TODO: custom rule to send request + @Bind(R.id.password_input) + EditText password; + + //actually doesn't exist in graphic design. I chose a similar layout + //for reusability purposes. + @Bind(R.id.left_arrow) + ImageView arrow; + + @Bind(R.id.google_sign_in_btn) + Button signInButton; + + @Bind(R.id.forgot_password_textview) + TextView forgotPassTV; + private boolean forgotPasswordShowing; + private boolean passwordVisible; + //Validator mValidator; + + GoogleApiClient mGoogleApiClient; + GoogleSignInOptions gso; + + // Just for Sign in With Google + private boolean firstLoginDoneByGoogle = false; + private boolean isSignInWithGoogle = false; + private static final int REQUEST_GET_ACCOUNT = 112; + + public LoginFragment() { + // Required empty public constructor + } + + + @Override + public int getLayoutID() { + return R.layout.fragment_login; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // this permission is for login by google(Android Marshmallow & above) + if(android.os.Build.VERSION.SDK_INT > 22){ + ActivityCompat.requestPermissions(getActivity(),new String[]{android.Manifest.permission.GET_ACCOUNTS},REQUEST_GET_ACCOUNT); + } + } + + @Override + protected void initUI() { + arrow.setVisibility(View.GONE); + title.setText(getString(R.string.login_in_poinila)); + //backForthButtonsBox.setBackForthListener(this); + } + + @OnClick(R.id.forgot_password_textview) + public void onForgotPassword() { + goToForgotPassword(); + } + + private void goToForgotPassword() { + ((SignUpLoginActivity) getActivity()).goToForgotPassword(); + } + + @Subscribe + public void onSuccessfulLogin(LoginSucceedEvent event) { + //DataRepository.setUserAsAnonymous(true); + DataRepository.getInstance().getMyInfo(ConnectionUitls.isNetworkOnline(), MyInfoReceivedEvent.MY_INFO_TYPE.LOAD); + } + + @Subscribe + public void onSuccessfulLogin(GoogleLoginSucceedEvent event) { + //DataRepository.setUserAsAnonymous(true); + firstLoginDoneByGoogle = event.firstLoginDoneByGoogle; + isSignInWithGoogle = true; + DataRepository.getInstance().getMyInfo(ConnectionUitls.isNetworkOnline(), MyInfoReceivedEvent.MY_INFO_TYPE.LOAD); + + } + + @Subscribe + public void onLoginFail(LoginFailedEvent event) { + //Logger.toast("failed"); + switch (event.code) { + case 401: // access error + Logger.toast(R.string.error_login); + break; + case 400: // bad parameter like sending a null device id + break; + } + dismissProgressDialog(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + } + + @Subscribe + public void onUserInfoReceived(MyInfoReceivedEvent event) { + onGettingInitDataResponse(event); + + if (event.type != MyInfoReceivedEvent.MY_INFO_TYPE.LOAD) return; + + if(LoginFragment.this.getActivity() == null) return; + + new AsyncTask() { + + @Override + protected Void doInBackground(Object... params) { + DataRepository.syncWithMyInfoResponse((MyInfoReceivedEvent) params[0]); + return null; + } + + @Override + protected void onPostExecute(Void o) { + + if(LoginFragment.this.getActivity() == null) return; + + PonilaAccountManager.getInstance().initUserTag(); + + if(isSignInWithGoogle) + PonilaAccountManager.getInstance().setGoogle(); + + dismissProgressDialog(); + if(firstLoginDoneByGoogle){ + PageChanger.goToSelectInterest(LoginFragment.this.getContext(), true); + }else { + PageChanger.goToDashboard(LoginFragment.this.getContext()); + } + getActivity().finish(); // one must not be able to navigate back to login + } + + + }.execute(event); + + + } + + SmsReceiver smsReceiver = new SmsReceiver(); + + @Override + public void onStart() { + super.onStart(); + // TODO: listen for sms delivery + getActivity().registerReceiver(smsReceiver, new IntentFilter("android.provider.Telephony.SMS_RECEIVED")); + + PonilaAccountManager.getInstance().initGoogleAPIClient(getActivity(), this); + PonilaAccountManager.getInstance().connectGoogleApiClient(); + } + + @Override + public void onStop() { + super.onStop(); + // TODO: unregister receiver. but I don't know if it's correct. may be we should listen + // in all application life cycle + getActivity().unregisterReceiver(smsReceiver); + + PonilaAccountManager.getInstance().stopAutoManageGoogleApiClient(getActivity()); + PonilaAccountManager.getInstance().disconnectGoogleApiClient(); + } + + /* @Override + public void onValidationSucceeded() { + NavigationUtils.goToActivity(MainActivity.class, getActivity()); + } + + @Override + public void onValidationFailed(List errors) { + Logger.toast(getString(R.string.login_faild)); + + }*/ + + @OnClick(R.id.toggle_visibility) + public void onChangeVisibility() { + passwordVisible ^= true; // toggle + + int start = password.getSelectionStart(); + int end = password.getSelectionEnd(); + password.setInputType(passwordVisible ? + InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD : + InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); + CalligraphyUtils.applyFontToTextView(getActivity(), password, CalligraphyConfig.get().getFontPath()); + password.setSelection(start, end); + + toggleVisibilityBtn.setImageResource(passwordVisible ? + R.drawable.toggle_visible_nobel_32dp : + R.drawable.toggle_invisible_nobel_32dp); + } + + /*---------*/ + + @Override + protected boolean sendsRequestAutomatically() { + return false; + } + + @Override + protected void requestInitialData() { + String uniqueNameOrEmail = username.getText().toString(); + boolean isEmail = Patterns.EMAIL_ADDRESS.matcher(uniqueNameOrEmail).matches(); + if (isEmail) { + PoinilaNetService.login(null, uniqueNameOrEmail, password.getText().toString()); + } else { + PoinilaNetService.login(uniqueNameOrEmail, null, password.getText().toString()); + } + } + + @Override + public ViewGroup getLoadableView() { + return null; + } + + @Override + public boolean mustShowProgressView() { + return false; + } + + //----------------- + + @OnClick(R.id.button_login) + public void onLogin() { + if (!ConnectionUitls.isNetworkOnline()) { + Logger.toast(R.string.warning_connect_to_network); + return; + } + showProgressDialog(); + initData(); + } + + @OnClick(R.id.button_signup) + public void onSignUp() { + ((SignUpLoginActivity) getActivity()).goToVerificationRequest(); + } + + @OnClick(R.id.button_guest_login) + public void onGuestLogin() { + DataRepository.setUserAsAnonymous(true); + PageChanger.goToDashboard(getActivity()); + } + + @OnClick(R.id.google_sign_in_btn) + public void gLogin(){ + PonilaAccountManager.getInstance().signInWithGoogleAPI(this); + showProgressDialog(); + } + + @Override + public void onConnectionFailed(ConnectionResult connectionResult) { + + dismissProgressDialog(); + Logger.toastError(R.string.error_google_connection); + + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + + // Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...); + if(data != null && resultCode == Activity.RESULT_OK){ + PonilaAccountManager.getInstance().getGoogleSignInAccount(data, this); + }else + Logger.toastError(R.string.error_google_sign_in); + + } + + @Override + public void onSuccessGoogleSignIn(GoogleSignInAccount acct) { +// Log.i(getClass().getName(), "getIdToken = " + acct.getIdToken()); + PoinilaPreferences.putGoogleToken(acct.getIdToken()); + PoinilaNetService.loginByGoogle(acct.getIdToken()); + } + + @Override + public void onFailureGoogleSignIn(GoogleSignInAccount acct) { + dismissProgressDialog(); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/MemberListFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/MemberListFragment.java new file mode 100755 index 0000000..a846cb2 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/MemberListFragment.java @@ -0,0 +1,201 @@ +package com.shaya.poinila.android.presentation.view.fragments; + + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.widget.TextView; +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewProvider; +import com.shaya.poinila.android.presentation.uievent.FriendCirclesUpdated; +import com.shaya.poinila.android.presentation.uievent.FriendshipClickEvent; +import com.shaya.poinila.android.presentation.uievent.MemberClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.PositiveButtonClickedUIEvent; +import com.shaya.poinila.android.presentation.view.dialog.DialogLauncher; +import com.shaya.poinila.android.presentation.viewholder.MemberViewHolder; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.Logger; +import com.squareup.otto.Subscribe; + +import butterknife.Bind; +import data.PoinilaNetService; +import data.event.BaseEvent; +import data.event.MembersReceivedEvent; +import data.model.FriendshipStatus; +import data.model.Member; +import manager.DBFacade; +import manager.DataRepository; + +import static android.support.v7.widget.LinearLayoutManager.VERTICAL; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_ENTITY; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_ITEM_COUNT; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_REQUEST_ID; + +public class MemberListFragment extends ListBusFragment { + + private int requestID; + private String mainActorID; + @Nullable @Bind(R.id.item_count) TextView mItemCountView; + + public MemberListFragment() { + // Required empty public constructor + } + + + @Override + public int getLayoutID() { + if (requestID == ConstantsUtils.REQUEST_POST_LIKERS) + return R.layout.fragment_favorites; + return R.layout.recycler_view_full; + } + + + @Override + protected void initUI() { + super.initUI(); + mRecyclerView = new RecyclerViewProvider(mRecyclerView). + setAdapter(getRecyclerViewAdapter()). + setLinearLayoutManager(VERTICAL). + bindViewToAdapter(); + //mRecyclerView.setHasFixedSize(true); + + switch (requestID){ + case ConstantsUtils.REQUEST_MEMBER_FOLLOWERS: + getActivity().setTitle(R.string.title_activity_member_followers); + break; + case ConstantsUtils.REQUEST_MEMBER_FRIENDS: + getActivity().setTitle(R.string.title_activity_member_friends); + break; + case ConstantsUtils.REQUEST_POST_LIKERS: + setText(mItemCountView, getString(R.string.favorites_formatted, + getActivity().getIntent().getIntExtra(KEY_ITEM_COUNT, 0))); + getActivity().setTitle(R.string.title_activity_post_likers); + break; + } + } + + @Override + protected RecyclerView.OnScrollListener getRecyclerViewListener() { + return RecyclerViewProvider.linearListEndDetectorListener(getRecyclerViewAdapter(), this); + } + + @Subscribe public void onGotoProfile(MemberClickedUIEvent event){ + Member member = getRecyclerViewAdapter().getItem(event.adapterPosition); + PageChanger.goToProfile(getActivity(), member); + } + + @Subscribe public void onShowFriendShipDialog(FriendshipClickEvent event){ + if (DataRepository.isUserAnonymous()){ + Logger.toastError(R.string.error_guest_action); + return; + } + + Member member = getRecyclerViewAdapter().getItem(event.adapterPosition); + DialogLauncher.launchFriendshipDialog(member, getFragmentManager()); + clickedItemPosition = event.adapterPosition; + } + + @Subscribe public void onMembersReceived(MembersReceivedEvent event){ + onGettingInitDataResponse(event); + onGettingListDataResponse(event, event.bookmark); + } + + @Subscribe + public void onPositiveDialogButton(PositiveButtonClickedUIEvent event) { + Member member = getRecyclerViewAdapter().getItem(clickedItemPosition); + switch (member.friendshipStatus){ + case NotFriend: + case WaitingForAction: // sending request + PoinilaNetService.friendRequest(member.getId(), DBFacade.getDefaultCircle().id); + member.friendshipStatus = FriendshipStatus.Pending; + break; + // we handle this case in EditFriendshipDialog + /*case IsFriend: // removing friend + PoinilaNetService.removeFriend(member.getId()); + member.friendshipStatus = FriendshipStatus.NotFriend; + break;*/ + case Pending: // no action yet + break; + } + getRecyclerViewAdapter().notifyItemChanged(clickedItemPosition); + } + + /*@Subscribe public void onNeutralDialogButton(NeutralDialogButtonClickedUIEvent event){ + DialogLauncher.launchChangeFriendCircle( + getChildFragmentManager(), + getRecyclerViewAdapter().getItem(clickedItemPosition)); + }*/ + + public static android.support.v4.app.Fragment newInstance(String id, int requestID) { + MemberListFragment f = new MemberListFragment(); + Bundle b = new Bundle(); + b.putString(KEY_ENTITY, id); + b.putInt(KEY_REQUEST_ID, requestID); + f.setArguments(b); + return f; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mainActorID = getArguments().getString(KEY_ENTITY); + requestID = getArguments().getInt(KEY_REQUEST_ID); + } + + @Subscribe public void onFriendCirclesUpdated(FriendCirclesUpdated event){ + int index = getRecyclerViewAdapter().getItems().indexOf(event.member); + if (index < 0) return; + getRecyclerViewAdapter().getItem(index).circle_ids = event.selectedCirclesIDs; + } +/*---------------*/ + @Override + public void requestForMoreData() { + switch (requestID){ + case ConstantsUtils.REQUEST_MEMBER_FOLLOWERS: + PoinilaNetService.getMemberFollowers(mainActorID, bookmark); + break; + case ConstantsUtils.REQUEST_MEMBER_FRIENDS: + PoinilaNetService.getMemberFriends(mainActorID, bookmark); + break; + case ConstantsUtils.REQUEST_POST_LIKERS: + PoinilaNetService.getPostLikers(mainActorID, bookmark); + break; + } + } + + @Override + public RecyclerViewAdapter createAndReturnRVAdapter() { + return new RecyclerViewAdapter(getActivity(), R.layout.member_inlist) { + @Override + protected MemberViewHolder getProperViewHolder(View v, int viewType) { + return new MemberViewHolder(v, BaseEvent.ReceiverName.MemberListFragment); + } + }; + } + + @Override + protected boolean sendsRequestAutomatically() { + return true; + } + + @Override + protected void requestInitialData() { + requestForMoreData(); + } + + @Override + public boolean mustShowProgressView() { + return true; + } + + @Override + public void onSuccessfulListData(BaseEvent baseEvent, String newBookmark) { + super.onSuccessfulListData(baseEvent, newBookmark); + getRecyclerViewAdapter().addItems(((MembersReceivedEvent) baseEvent).members); + } + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/MyFollowedCollectionsFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/MyFollowedCollectionsFragment.java new file mode 100755 index 0000000..21fb350 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/MyFollowedCollectionsFragment.java @@ -0,0 +1,328 @@ +package com.shaya.poinila.android.presentation.view.fragments; + + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.os.Handler; +import android.preference.PreferenceManager; +import android.support.annotation.NonNull; +import android.support.v7.app.AlertDialog; +import android.support.v7.widget.GridLayoutManager; +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.BaseAdapter; +import android.widget.ImageButton; +import android.widget.TextView; + +import com.getbase.floatingactionbutton.FloatingActionButton; +import com.getbase.floatingactionbutton.FloatingActionsMenu; +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewProvider; +import com.shaya.poinila.android.presentation.uievent.CollectionClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.FramesUpdatedUIEvent; +import com.shaya.poinila.android.presentation.uievent.HelpMyFollowedCollectionListFragment; +import com.shaya.poinila.android.presentation.uievent.MemberClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.OnFrameClickedUIEvent; +import com.shaya.poinila.android.presentation.view.dialog.NewCollectionDialog; +import com.shaya.poinila.android.presentation.view.help.Help; +import com.shaya.poinila.android.presentation.viewholder.BaseViewHolder; +import com.shaya.poinila.android.presentation.viewholder.MyFollowedCollectionViewHolder; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.util.PoinilaPreferences; +import com.squareup.otto.Subscribe; + +import java.util.List; + +import butterknife.Bind; +import butterknife.OnClick; +import data.event.BaseEvent; +import data.event.CollectionsReceivedEvent; +import data.model.Collection; +import data.model.Frame; +import data.model.Member; +import manager.DBFacade; +import manager.DataRepository; + +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_MEMBER_COLLECTIONS; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_MEMBER_FOLLOWED_COLLECTIONS; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_POST_REPOSTING_COLLECTIONS; + +// TODO: public class MyFollowedCollectionsFragment extends BusFragment { +public class MyFollowedCollectionsFragment extends CollectionListFragment{ + + //private MySpinnerAdapter mSpinnerAdapter; + //private ArrayAdapter mSpinnerAdapter; + //public @Bind(R.actorID.select_frame) Spinner mFrameSpinner; + private String selectedFrameID = null; + private List frames; + @Bind(R.id.select_frame_container) View selectFrameContainer; + @Bind(R.id.select_frame_button) ImageButton selectFrameBtn; + @Bind(R.id.select_frame_text) TextView selectFrameText; + + public MyFollowedCollectionsFragment() { + // Required empty public constructor + } + + public static MyFollowedCollectionsFragment newInstance() { + return new MyFollowedCollectionsFragment(); + } + + @Subscribe + public void onFramesUpdated(FramesUpdatedUIEvent event){ + updateFrames(event.frames); + } + + + public void updateFrames(List frames){ + if (frames != null && !frames.isEmpty()){ + this.frames.addAll(frames); + } + } + + @OnClick({R.id.select_frame_button, R.id.select_frame_text}) public void onSelectingFrame(){ + if (frames == null || frames.isEmpty()) + Logger.toast(R.string.error_no_frame_exist); + else + FramesDialog.newInstance(frames).show(getFragmentManager(), null); + } + + @Subscribe public void onFrameSelected(OnFrameClickedUIEvent event){ + selectedFrameID = event.frame.id == -1 ? null : event.frame.getId(); + setText(selectFrameText, event.frame.id == -1 ? getString(R.string.select_frame) : event.frame.name); + getRecyclerViewAdapter().clear(); + bookmark = null; + initDataResponseReceived = false; + initData(); + } + + @Override + protected RecyclerView.OnScrollListener getRecyclerViewListener() { + return RecyclerViewProvider.gridListEndDetectionListener(getRecyclerViewAdapter(), this); + } + + @Override + public void onStart() { + super.onStart(); + if (frames == null) { + frames = DBFacade.getMyFrames(); + frames.add(0, new Frame(-1, getString(R.string.default_no_frame))); + } + } + + @Override + public int getLayoutID() { + return R.layout.fragment_my_followed_collections; + } + + + @Override + public void onSuccessfulListData(BaseEvent baseEvent, String newBookmark) { + super.onSuccessfulListData(baseEvent, newBookmark); + + } + + @Override + protected void initUI() { + + mRecyclerView = new RecyclerViewProvider(mRecyclerView). + setGridLayoutManager(GridLayoutManager.VERTICAL, + getResources().getInteger(R.integer.column_count), new GridLayoutManager.SpanSizeLookup() { + @Override + public int getSpanSize(int position) { + if(getRecyclerViewAdapter().getItemViewType(position) == RecyclerViewAdapter.VIEW_TYPE_LOAD_PROGRESS ){ + return getResources().getInteger(R.integer.column_count); + } + return 1; + } + }). + //setAdapter(MY_FOLLOWING_COLLECTION_ADAPTER, getActivity()). + setAdapter(getRecyclerViewAdapter()). + bindViewToAdapter(); + SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity()); + Boolean showFrame = sharedPref.getBoolean(getString(R.string.pref_show_select_frame_key), false); + if (!showFrame) + selectFrameContainer.setVisibility(View.GONE); + + + mRecyclerView.addOnScrollListener(getRecyclerViewListener()); + //frames = new ArrayList<>(); + + } + + @Override + public RecyclerViewAdapter createAndReturnRVAdapter() { + return new RecyclerViewAdapter(getActivity(), R.layout.collection_simple) { + @Override + protected BaseViewHolder getProperViewHolder(View v, int viewType) { + if(viewType == RecyclerViewAdapter.VIEW_TYPE_LOAD_PROGRESS ){ + return new BaseViewHolder.EmptyViewHolder(v); + } + return new MyFollowedCollectionViewHolder(v, BaseEvent.ReceiverName.MyFollowedCollections); + } + }; + } + + @Override + @Subscribe + public void onCollectionsReceived(CollectionsReceivedEvent event) { + super.onCollectionsReceived(event); + + + } + + @Subscribe + public void answerAvailable(HelpMyFollowedCollectionListFragment event) { + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + + if(!PoinilaPreferences.getHelpStatus(getClass().getName())){ + showHelp(); + PoinilaPreferences.putHelpStatus(getClass().getName(), true); + } + + } + }, 500); + + } + + @Override + protected boolean isListDataResponseValid(BaseEvent baseEvent, String responseBookmark) { + CollectionsReceivedEvent event = ((CollectionsReceivedEvent) baseEvent); + boolean condition = event.receiverName == BaseEvent.ReceiverName.MyFollowedCollections; + return super.isListDataResponseValid(baseEvent, responseBookmark) && condition; + + } + + @Override + protected boolean isInitDataResponseValid(BaseEvent event) { + return super.isInitDataResponseValid(event) && event.receiverName == BaseEvent.ReceiverName.MyFollowedCollections; + } + + @Override + public void requestForMoreData() { + + DataRepository.getInstance().getMyFollowedCollections(selectedFrameID, bookmark); + } + + @Subscribe public void onProfilePicClickedEvent(MemberClickedUIEvent event){ + if (event.receiverName != BaseEvent.ReceiverName.MyFollowedCollections) + return; + Member member = ((Collection)getRecyclerViewAdapter().getItem(event.adapterPosition)).owner; + PageChanger.goToProfile(getActivity(), member); + } + + @Subscribe public void onCollectionClicked(CollectionClickedUIEvent event){ + if (event.receiverName != BaseEvent.ReceiverName.MyFollowedCollections) + return; + Collection collection = (Collection)getRecyclerViewAdapter().getItem(event.adapterPosition); + PageChanger.goToCollection(getActivity(), collection); + } + + + public void showHelp() { + if(getRecyclerViewAdapter().getItemCount() > 0 && getRecyclerViewAdapter().getItem(0) instanceof Collection){ + Help.getInstance().showFollowedCollectionHelp(getActivity(), mRecyclerView.getLayoutManager().findViewByPosition(0)); + viewedHelp = true; + } + } + + + /*-------------------------------------*/ + + public static class MySpinnerAdapter extends BaseAdapter{ + + List items; + private Context context; + + public MySpinnerAdapter(Context context, List frames) { + this.context = context; + items = frames; + } + + public void addItems(List frames){ + /*for (Frame frame : circles) + items.add(frame);*/ + items.addAll(frames); + } + + @Override + public int getCount() { + return items.size(); + } + + @Override + public Object getItem(int position) { + return items.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View spinView; + if( convertView == null ){ + LayoutInflater inflater = LayoutInflater.from(context); + spinView = inflater.inflate(R.layout.spinner_item, parent, false); + } else { + spinView = convertView; + } + ((TextView) spinView.findViewById(R.id.spinner_row_title)).setText(((Frame)getItem(position)).name); + return spinView; + } + + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + View spinView; + if( convertView == null ){ + LayoutInflater inflater = LayoutInflater.from(context); + spinView = inflater.inflate(R.layout.spinner_item, parent, false); + } else { + spinView = convertView; + } + ((TextView) spinView.findViewById(R.id.spinner_row_title)).setText(((Frame)getItem(position)).name); + return spinView; + } + } + + + public static class FramesDialog extends android.support.v4.app.DialogFragment { + + List frames; + + public static FramesDialog newInstance(List frames){ + FramesDialog d = new FramesDialog(); + // TODO: bullshit! + d.frames = frames; + return d; + } + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + return builder.setTitle(R.string.frames).setAdapter(new MySpinnerAdapter(getActivity(), frames), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + BusProvider.getBus().post(new OnFrameClickedUIEvent(frames.get(which))); + } + }).create(); + } + } + + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/MyProfileFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/MyProfileFragment.java new file mode 100755 index 0000000..cd58d5f --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/MyProfileFragment.java @@ -0,0 +1,544 @@ +package com.shaya.poinila.android.presentation.view.fragments; + + +import android.Manifest; +import android.content.Intent; +import android.os.Handler; +import android.support.annotation.NonNull; +import android.support.v13.app.FragmentCompat; +import android.support.v4.widget.Space; +import android.support.v7.widget.CardView; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.getbase.floatingactionbutton.FloatingActionButton; +import com.getbase.floatingactionbutton.FloatingActionsMenu; +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.ExploreTagEvent; +import com.shaya.poinila.android.presentation.uievent.FABMenuCollapseUIEvent; +import com.shaya.poinila.android.presentation.uievent.FABMenuExpandUIEvent; +import com.shaya.poinila.android.presentation.uievent.HelpMyProfileFragment; +import com.shaya.poinila.android.presentation.uievent.NewWebsitePostEvent; +import com.shaya.poinila.android.presentation.uievent.ShowVerifySnackbarEvent; +import com.shaya.poinila.android.presentation.view.ViewInflater; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.presentation.view.activity.BaseActivity; +import com.shaya.poinila.android.presentation.view.activity.CollectionListActivity; +import com.shaya.poinila.android.presentation.view.activity.CropImageActivity; +import com.shaya.poinila.android.presentation.view.activity.EditInterestsActivity; +import com.shaya.poinila.android.presentation.view.activity.MemberListActivity; +import com.shaya.poinila.android.presentation.view.activity.PostListActivity; +import com.shaya.poinila.android.presentation.view.activity.SettingActivity; +import com.shaya.poinila.android.presentation.view.costom_view.ActivityResultPermissionDelegate.ImagePickerResultPermissionDelegate; +import com.shaya.poinila.android.presentation.view.dialog.DialogLauncher; +import com.shaya.poinila.android.presentation.view.dialog.NewCollectionDialog; +import com.shaya.poinila.android.presentation.view.dialog.PoinilaInviteDialog; +import com.shaya.poinila.android.presentation.view.help.Help; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.NavigationUtils; +import com.shaya.poinila.android.util.PoinilaPreferences; +import com.shaya.poinila.android.util.ResourceUtils; +import com.shaya.poinila.android.util.StorageUtils; +import com.shaya.poinila.android.util.StringUtils; +import com.squareup.otto.Subscribe; + + +import org.apmem.tools.layouts.FlowLayout; + +import java.util.List; + +import butterknife.Bind; +import butterknife.ButterKnife; +import butterknife.OnClick; +import data.event.BaseEvent; +import data.event.InviteUsedEvent; +import data.event.MemberReceivedEvent; +import data.event.ProfileDirtyEvent; +import data.model.Collection; +import data.model.ImageUrls; +import data.model.Member; +import manager.DBFacade; +import manager.DataRepository; + +import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setFont; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setImage; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_ENTITY; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_IMAGE_ADDRESS; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_REQUEST_ID; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_MEMBER_COLLECTIONS; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_MEMBER_FAVED_POSTS; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_MEMBER_FOLLOWED_COLLECTIONS; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_MEMBER_FOLLOWERS; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_MEMBER_FRIENDS; + +/** + * A simple {@link android.app.Fragment} subclass. + * Use the {@link MyProfileFragment#newInstance} factory method to + * create an instance of this fragment. + */ +public class MyProfileFragment extends BusFragment implements View.OnClickListener, FragmentCompat.OnRequestPermissionsResultCallback{ + + @Bind(R.id.fab_menu) + FloatingActionsMenu fabMenu; + @Bind(R.id.fab_add_post) + FloatingActionButton addPost; + @Bind(R.id.fab_add_collection) FloatingActionButton addCollection; + @Bind(R.id.fab_add_post_from_site) FloatingActionButton addFromUrl; + @Bind(R.id.fab_invite) FloatingActionButton inviteToPoinila; + + /*-------Related to general member actions-------*/ + @Bind(R.id.followers) ViewGroup followersStatViewGroup; + @Bind(R.id.favorited) ViewGroup favoritedStatViewGroup; + @Bind(R.id.posts) ViewGroup postsStatViewGroup; + @Bind(R.id.friends) ViewGroup friendsStatViewGroup; + @Bind(R.id.owning_collections_container) View owningCollections; + @Bind(R.id.following_collections_container) View followingCollections; + @Bind(R.id.profile_general_info) View profileGeneralInfo; + @Bind(R.id.interest_container) View interestsContainer; + @Bind(R.id.blog_info) View blogInfo; + @Bind(R.id.about_me) TextView aboutMe; + + private ImageView settingIcon; + private Member member; + private ImageView mAvatarImageView; + private ImagePickerResultPermissionDelegate resultHandlerIMPL; + + /** + * Use this factory method to create a new instance of + * this fragment using the provided parameters. + * @return A new instance of fragment MyProfileFragment. + */ + public static MyProfileFragment newInstance() { + MyProfileFragment fragment = new MyProfileFragment(); + return fragment; + } + + public MyProfileFragment() { + // Required empty public constructor + } + + @OnClick(R.id.followers) public void onShowFollowers(){ + NavigationUtils.goToActivity(MemberListActivity.class, getActivity(), + KEY_ENTITY, member.getId(), KEY_REQUEST_ID, REQUEST_MEMBER_FOLLOWERS); + } + + @OnClick(R.id.friends) public void onShowFriends(){ + NavigationUtils.goToActivity(MemberListActivity.class, getActivity(), + KEY_ENTITY, member.getId(), KEY_REQUEST_ID, REQUEST_MEMBER_FRIENDS); + } + + @OnClick(R.id.favorited) public void onShowFavorited(){ + NavigationUtils.goToActivity(PostListActivity.class, getActivity(), + KEY_ENTITY, member.getId(), KEY_REQUEST_ID, REQUEST_MEMBER_FAVED_POSTS); + } + + @OnClick(R.id.posts) public void onShowPosts(){ + PageChanger.goToMemberPosts(getActivity(), member.getId(), member.fullName); + } + + @OnClick(R.id.owning_collections_container) public void onShowOwniningCollections(){ + NavigationUtils.goToActivity(CollectionListActivity.class, getActivity(), + KEY_ENTITY, member.getId(), KEY_REQUEST_ID, REQUEST_MEMBER_COLLECTIONS); + } + + @OnClick(R.id.following_collections_container) public void onShowFollowingCollections(){ + NavigationUtils.goToActivity(CollectionListActivity.class, getActivity(), + KEY_ENTITY, member.getId(), KEY_REQUEST_ID, REQUEST_MEMBER_FOLLOWED_COLLECTIONS); + } + + // for my profile + @OnClick(R.id.interest_container) public void onShowInterests(){ + NavigationUtils.goToActivity(EditInterestsActivity.class, getActivity(), + KEY_ENTITY, member.getId()); + } + + + @OnClick(R.id.fab_add_post) public void onAddPost(){ + fabMenu.collapse(); + //DialogLauncher.launchNewPost(getChildFragmentManager(), null); + PageChanger.goToNewPost(getFragmentManager(), null); + } + + @OnClick(R.id.fab_add_post_from_site) public void onAddPostFromUrl(){ + // todo: dialog, its layout and how to get a url imagesUrls; + fabMenu.collapse(); +// DialogLauncher.launchNewWebsitePost(getFragmentManager()); + PageChanger.goToNewWebSitePost(getActivity(), null); + } + + @Subscribe public void onNewUrlImagePostEvent(NewWebsitePostEvent event){ + //DialogLauncher.launchNewPost(getChildFragmentManager(), event.suggestedPost); + PageChanger.goToNewPost(getFragmentManager(), event.suggestedPost); + } + + @OnClick(R.id.fab_add_collection) public void onAddCollection(){ + fabMenu.collapse(); + new NewCollectionDialog().show(getFragmentManager(), null); + //new NewCollectionDialog().show(getChildFragmentManager(), TAG_NEW_COLLECTION_DIALOG); + } + + @OnClick(R.id.fab_invite) public void onInviteToPoinila(){ + fabMenu.collapse(); + new PoinilaInviteDialog().show(getFragmentManager(), null); + } + + @Subscribe + public void onInviteUsedEvent(InviteUsedEvent event){ + updateInviteFAB(); + } + + private void updateInviteFAB() { + if (DataRepository.getInstance().getRemainedInvites() <= 0) + inviteToPoinila.setVisibility(View.GONE); + else + inviteToPoinila.setTitle(getString(R.string.invite_to_poinila_formatted, + DataRepository.getInstance().getRemainedInvites())); + } + + @Override + protected void initUI() { + //TODO: what the? + resultHandlerIMPL = new ImagePickerResultPermissionDelegate(){ + @Override + public void handleValidResults(int requestCode, Intent data) { + super.handleValidResults(requestCode, data); + + if (TextUtils.isEmpty(this.imageAddress)) + return; + Intent intent = NavigationUtils.makeNavigationIntent(CropImageActivity.class, getActivity()); + intent.putExtra(KEY_IMAGE_ADDRESS, this.imageAddress); + startActivity(intent); + } + + @Override + public void handlePermissionGranted() { + startForResult(MyProfileFragment.this, + StorageUtils.dispatchCapturePhotoIntent(), + ConstantsUtils.REQUEST_CODE_TAKE_PHOTO); + } + }; + + settingIcon = ButterKnife.findById(profileGeneralInfo, R.id.icon); + settingIcon.setImageResource(R.drawable.action_settings); + settingIcon.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onSetting(v); + } + }); + mAvatarImageView = (ImageView) profileGeneralInfo.findViewById(R.id.image); + + //setRemainedInvites(new RemainedInvitesEvent(DataRepository.getInstance().getRemainedInvites())); + updateInviteFAB(); + fabMenu.setOnFloatingActionsMenuUpdateListener(new FloatingActionsMenu.OnFloatingActionsMenuUpdateListener() { + @Override + public void onMenuExpanded() { + BusProvider.getBus().post(new FABMenuExpandUIEvent(BaseEvent.ReceiverName.MyProfileFragment)); + } + + @Override + public void onMenuCollapsed() { + BusProvider.getBus().post(new FABMenuCollapseUIEvent(BaseEvent.ReceiverName.MyProfileFragment)); + } + }); + generalProfileInit(); + + + } + + public void onSetting(View v){ + NavigationUtils.goToActivity(SettingActivity.class, getActivity()); + } + + @Override + public int getLayoutID() { + return R.layout.fragment_profile; + } + + @Override + public void onStart() { + // TODO: use better approaches later + initDataResponseReceived = false; // this causes parent initData to be called. + super.onStart(); + + if (member == null) + member = DBFacade.getCachedMyInfo(); + if (member != null) { // due to crash (LG Optimus 1.1.9.2) https://play.google.com/apps/publish/?dev_acc=18170414618191752575#ErrorClusterDetailsPlace:p=com.shaya.poinila&et=CRASH&lr=LAST_7_DAYS&ecn=java.lang.NullPointerException&tf=MyProfileFragment.java&tc=com.shaya.poinila.android.presentation.view.fragments.MyProfileFragment&tm=fill&nid&an&c&s=new_status_desc + // however I dont know why this could happen + fill(member); + } + } + + /*-------Related to general member actions-------*/ + + @Subscribe public void onProfileReceived(MemberReceivedEvent event) { + onGettingInitDataResponse(event); + } + + @Override + public void onSuccessfulInitData(BaseEvent baseEvent) { + super.onSuccessfulInitData(baseEvent); + this.member = ((MemberReceivedEvent)baseEvent).member; + fill(member); + } + + private void fill(final Member member) { + // TODO: fill page with actual posts + setImage(mAvatarImageView, member.imageUrls, + ImageUrls.ImageType.MEMBER, ImageUrls.ImageSize.BIG); + mAvatarImageView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + DialogLauncher.launchSelectImage(getActivity().getSupportFragmentManager(), member, MyProfileFragment.this); +// inflateAvatarMenu(v); + } + }); + + setText((TextView) profileGeneralInfo.findViewById(R.id.title), member.fullName); + setText((TextView) profileGeneralInfo.findViewById(R.id.subtitle), member.uniqueName); + + setFont((TextView) profileGeneralInfo.findViewById(R.id.title), getString(R.string.default_bold_font_path)); + setFont((TextView) profileGeneralInfo.findViewById(R.id.subtitle), getString(R.string.default_font_path)); + + setText(aboutMe, member.aboutMe); + setFont(aboutMe, getString(R.string.default_font_path)); + + if (TextUtils.isEmpty(member.url)) + blogInfo.setVisibility(View.GONE); + else { + blogInfo.setVisibility(View.VISIBLE); +// setText((TextView) blogInfo.findViewById(R.id.title), member.urlName); + setText((TextView) blogInfo.findViewById(R.id.url),member.url); + setFont((TextView) blogInfo.findViewById(R.id.url), getString(R.string.default_bold_font_path)); + setFont((TextView) blogInfo.findViewById(R.id.url_label), getString(R.string.default_bold_font_path)); + blogInfo.setOnClickListener(new View.OnClickListener() { + @Override + + public void onClick(View v) { + String url = member.url.toLowerCase().startsWith("http://") ? + member.url.toLowerCase() : "http://" + member.url.toLowerCase(); + PageChanger.goToInlineBrowser(getActivity(), url, null, null); + } + }); + } + + setText((TextView)followersStatViewGroup.findViewById(R.id.top_text) ,member.followerCount); + setText((TextView) favoritedStatViewGroup.findViewById(R.id.top_text), member.likesCount); + setText((TextView) postsStatViewGroup.findViewById(R.id.top_text), member.postsCount); + setText((TextView) friendsStatViewGroup.findViewById(R.id.top_text), member.friendsCount); + + setFont((TextView) followersStatViewGroup.findViewById(R.id.top_text), getString(R.string.default_bold_font_path)); + setFont((TextView) favoritedStatViewGroup.findViewById(R.id.top_text), getString(R.string.default_bold_font_path)); + setFont((TextView) postsStatViewGroup.findViewById(R.id.top_text), getString(R.string.default_bold_font_path)); + setFont((TextView) friendsStatViewGroup.findViewById(R.id.top_text), getString(R.string.default_bold_font_path)); + + setFont((TextView) followersStatViewGroup.findViewById(R.id.bottom_text), getString(R.string.default_bold_font_path)); + setFont((TextView) favoritedStatViewGroup.findViewById(R.id.bottom_text), getString(R.string.default_bold_font_path)); + setFont((TextView) postsStatViewGroup.findViewById(R.id.bottom_text), getString(R.string.default_bold_font_path)); + setFont((TextView) friendsStatViewGroup.findViewById(R.id.bottom_text), getString(R.string.default_bold_font_path)); + + setText((TextView) owningCollections.findViewById(R.id.card_title), + getString(R.string.member_collections_formatted, member.fullName)); + + setFont((TextView) owningCollections.findViewById(R.id.card_title), + getString(R.string.default_bold_font_path)); + + /*-----OWNING COLLECTIONS-------*/ + fillCollectionsSummery(owningCollections, member.owningCollections, member.owningCollectionsCount); + /*----FOLLOWING COLLECTIONS-----*/ + fillCollectionsSummery(followingCollections, member.followingCollections, member.followingCollectionsCount); + /*-------Interests--------*/ + if (member.interests != null && !member.interests.isEmpty()) { + FlowLayout flowLayout = ButterKnife.findById(interestsContainer, R.id.tags_container); + flowLayout.removeAllViews(); + for (int i = 0; i < 5 && i < member.interests.size(); i++) { + ViewInflater.addTagToContainer(flowLayout, member.interests.get(i)); + } + } + } + + private void fillCollectionsSummery(View collectionsViewContainer, + List collections, int itemCount) { + LinearLayout ll = (LinearLayout) collectionsViewContainer.findViewById(R.id.cards_container); + if (collections == null || collections.isEmpty()) { + ll.setVisibility(View.GONE); + return; + } + ll.removeAllViews(); + ll.setVisibility(View.VISIBLE); + int collectionsCount = getResources().getInteger(R.integer.profile_page_collection_summary_count); + for (int i = 0; i < collectionsCount && i < collections.size(); i++) { + final Collection collection = collections.get(i); + CardView card = (CardView)ViewInflater.inflateImageCaption(ll, collection.name, collection.coverImageUrls); + card.findViewById(R.id.image).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onCollectionClicked(collection); + } + }); + ll.addView(card, new LinearLayout.LayoutParams(0, WRAP_CONTENT, 1)); + if (i != collectionsCount - 1) { + Space margin = new Space(getActivity()); + ll.addView(margin, new LinearLayout.LayoutParams( + (int) ResourceUtils.getDimen(R.dimen.margin_lvl1), 1)); + } + } + TextView itemCountView = ButterKnife.findById(collectionsViewContainer, R.id.item_count); + itemCountView.setVisibility(View.VISIBLE); + ViewUtils.setFont(itemCountView, getString(R.string.default_bold_font_path)); + ViewUtils.setText(itemCountView, StringUtils.getStringWithPersianNumber("(%d)", itemCount)); + + } + + private void onCollectionClicked(final Collection collection) { + PageChanger.goToCollection(getActivity(), collection); + } + + private void generalProfileInit() { + ((TextView)followersStatViewGroup.findViewById(R.id.bottom_text)) + .setText(getString(R.string.follower)); + ((TextView)favoritedStatViewGroup.findViewById(R.id.bottom_text)) + .setText(getString(R.string.favorited)); + ((TextView)postsStatViewGroup.findViewById(R.id.bottom_text)) + .setText(getString(R.string.post)); + ((TextView)friendsStatViewGroup.findViewById(R.id.bottom_text)) + .setText(getString(R.string.friend)); + + + ((TextView)followingCollections.findViewById(R.id.card_title)). + setText(getString(R.string.follows)); + + ((TextView)interestsContainer.findViewById(R.id.card_title)). + setText(getString(R.string.interest)); + + setFont((TextView) followingCollections.findViewById(R.id.card_title), getString(R.string.default_bold_font_path)); + setFont((TextView)interestsContainer.findViewById(R.id.card_title), getString(R.string.default_bold_font_path)); + // TODO: member ro chejuri avval set konim? + } + +// private void inflateAvatarMenu(View v) { +// popupMenu = new PopupMenu(getActivity(), v); +// popupMenu.setOnMenuItemClickListener(MyProfileFragment.this); +// MenuInflater inflater = popupMenu.getMenuInflater(); +// inflater.inflate(R.menu.menu_popup_profile_picture, popupMenu.getMenu()); +// if (member.imageUrls == null || !member.imageUrls.isNotEmpty()) +// popupMenu.getMenu().removeItem(R.id.view_photo); +// popupMenu.show(); +// } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + resultHandlerIMPL.onActivityResult(requestCode, resultCode, data); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + + resultHandlerIMPL.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + + @Subscribe public void onProfileChanged(ProfileDirtyEvent event) { + DataRepository.getInstance().getMyProfile(); + } + + // If root layout be LinearLayout, below approach is useless. + @Subscribe + public void onFABMenuExpanded(FABMenuExpandUIEvent event){ + View maskView = LayoutInflater.from(getActivity()).inflate(R.layout.white_transarent_mask, rootView, false); + maskView.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + fabMenu.collapse(); + return true; + } + }); + // adding to index 1, after profile layout and before FAB menu + rootView.addView(maskView, 1); + } + + @Subscribe public void onFABMenuCollapsed(FABMenuCollapseUIEvent event){ + rootView.removeView(rootView.findViewById(R.id.white_transparent_mask)); + } + + + @Subscribe public void onExploreTag(ExploreTagEvent event){ + PageChanger.goToExplore(getActivity(), event.text); + } + /*------------------------*/ + + + @Subscribe + public void answerAvailable(HelpMyProfileFragment event) { + + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + + if(!PoinilaPreferences.getHelpStatus(getClass().getName())){ + showHelp(); + PoinilaPreferences.putHelpStatus(getClass().getName(), true); + } + + } + }, 500); + + + + } + + @Override + protected boolean sendsRequestAutomatically() { + return true; + } + + @Override + protected void requestInitialData() { + DataRepository.getInstance().getMyProfile(); //ConnectionUitls.isNetworkOnline() + } + + @Override + public ViewGroup getLoadableView() { + return null; + } + + @Override + public boolean mustShowProgressView() { + return false; + } + + public void showHelp() { + if(this.isVisible()){ + Help.getInstance().showProfileHelp(getActivity(), fabMenu); + viewedHelp = true; + } + } + + @Override + public void onClick(View v) { + switch (v.getId()){ + case R.id.show_profile_image: + PageChanger.goToFullImage(getActivity(), member.imageUrls.properMemberImage(ImageUrls.ImageSize.FULL_SIZE).url); + break; + case R.id.select_image_camera: + resultHandlerIMPL.askForPermission(this, + Manifest.permission.WRITE_EXTERNAL_STORAGE, + BaseActivity.MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE); + break; + case R.id.select_image_gallery: + resultHandlerIMPL.startForResult(this, StorageUtils.dispatchSelectImageIntent(), ConstantsUtils.REQUEST_CODE_PICK_IMAGE); + break; + + } + } + /*-----------------------------*/ +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/NewPostFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/NewPostFragment.java new file mode 100755 index 0000000..05c1e8d --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/NewPostFragment.java @@ -0,0 +1,38 @@ +package com.shaya.poinila.android.presentation.view.fragments; + +import android.view.ViewGroup; + +/** + * Created by hossein on 8/20/16. + */ +public class NewPostFragment extends BusFragment { + @Override + protected boolean sendsRequestAutomatically() { + return false; + } + + @Override + protected void requestInitialData() { + + } + + @Override + public ViewGroup getLoadableView() { + return null; + } + + @Override + public boolean mustShowProgressView() { + return false; + } + + @Override + public int getLayoutID() { + return 0; + } + + @Override + protected void initUI() { + + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/NewWebSitePostInputURLFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/NewWebSitePostInputURLFragment.java new file mode 100755 index 0000000..b6d48a6 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/NewWebSitePostInputURLFragment.java @@ -0,0 +1,169 @@ +package com.shaya.poinila.android.presentation.view.fragments; + +import android.net.Uri; +import android.support.design.widget.TextInputEditText; +import android.support.design.widget.TextInputLayout; +import android.util.Patterns; +import android.view.ViewGroup; +import android.widget.ProgressBar; + +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.presentation.view.activity.NewPostActivity; +import com.shaya.poinila.android.presentation.view.costom_view.PonilaChoiceView; +import com.squareup.otto.Subscribe; +import com.squareup.picasso.Picasso; + +import java.util.Arrays; +import java.util.List; + +import butterknife.Bind; +import butterknife.OnClick; +import data.PoinilaNetService; +import data.event.BaseEvent; +import data.event.SuggestedWebpagePostReceived; +import data.model.Image; +import data.model.PostType; +import data.model.SuggestedWebPagePost; + +/** + * Created by iran on 8/15/2016. + */ +public class NewWebSitePostInputURLFragment extends BusFragment implements PonilaChoiceView.OnOptionSelected { + + private static final String KEY_SITE_ADDRESS = "site address"; +// @Bind(R.id.image_radio_btn) +// protected RadioButton image; +// @Bind(R.id.text_radio_btn) +// protected RadioButton text; +// @Bind(R.id.post_type_container) +// RadioGroup postTypeContainer; + + @Bind(R.id.new_web_site_post_options) + PonilaChoiceView ponilaChoiceView; + + @Bind(R.id.url_field) + TextInputEditText urlField; + // @Bind(R.id.recycler_view) +// RecyclerView mRecyclerView; + @Bind(R.id.progress_bar) + ProgressBar progressWheel; + @Bind(R.id.url_textinputlayout) + TextInputLayout urlInputLayout; + RecyclerViewAdapter mRecyclerViewAdapter; + String[] protocols = {"http", "https"}; + List validProtocols = Arrays.asList(protocols); + private String siteAddress; + private PostType postType = PostType.TEXT; + private SuggestedWebPagePost suggestedPost; + + @Override + public int getLayoutID() { + return R.layout.fragment_new_web_site_post_input_url; + } + + @Override + protected void initUI() { + ponilaChoiceView.setOptionsText(R.string.text, R.string.image, R.string.video); + ponilaChoiceView.setOnOptionSelected(this); + + } + + @OnClick(R.id.new_web_site_post_next_btn) + public void nextLevel() { + + setSiteAddress(urlField.getText().toString()); + if (isAddressValid(siteAddress)) { + if (postType.equals(PostType.TEXT)) { + PoinilaNetService.getWebsiteInfo(siteAddress, postType); + showProgressDialog(); + } else { + String siteAddress = urlField.getText().toString(); + ((NewPostActivity) getActivity()).goToSelectMediaFragment(postType, siteAddress); + } + + } + } + + @Override + protected boolean sendsRequestAutomatically() { + return false; + } + + @Override + protected void requestInitialData() { + + } + + @Override + public ViewGroup getLoadableView() { + return null; + } + + @Subscribe + public void onSuggestedWebpagePostReceived(SuggestedWebpagePostReceived event) { + suggestedPost = event.webpagePost; + suggestedPost.siteAddress = this.siteAddress; + dismissProgressDialog(); + PageChanger.goToNewPost(getFragmentManager(), suggestedPost); + } + + @Override + public boolean mustShowProgressView() { + return false; + } + + private boolean isAddressValid(String address) { + String error; + Uri uri = Uri.parse(address); + + if (Patterns.WEB_URL.matcher(uri.toString()).matches()) { // trim is essential. + String protocol = uri.getScheme(); + if (protocol == null) { + protocol = "http"; + setSiteAddress("http://" + address); + } + if (!validProtocols.contains(protocol)) { + error = getString(R.string.wrong_protocol); + } else { + setSiteAddress(new Uri.Builder().scheme(uri.getScheme()). + authority(uri.getAuthority()). + path(uri.getPath()). + query(uri.getQuery()).build().toString()); + urlInputLayout.setErrorEnabled(false); + return true; + } + } else { + error = getString(R.string.error_invalid_url); + } + ViewUtils.setInputError(urlField, error); + return false; + } + + public void setSiteAddress(String siteAddress) { + this.siteAddress = siteAddress; + } + + @Override + public void onFirstOption() { + + postType = PostType.TEXT; + + } + + @Override + public void onSecondOption() { + + postType = PostType.IMAGE; + + } + + @Override + public void onThirdOption() { + + postType = PostType.VIDEO; + + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/NewWebSitePostSelectMediaFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/NewWebSitePostSelectMediaFragment.java new file mode 100755 index 0000000..97a8af5 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/NewWebSitePostSelectMediaFragment.java @@ -0,0 +1,189 @@ +package com.shaya.poinila.android.presentation.view.fragments; + +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.StaggeredGridLayoutManager; +import android.util.Log; +import android.view.View; + +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewProvider; +import com.shaya.poinila.android.presentation.uievent.ImageClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.NewWebsitePostEvent; +import com.shaya.poinila.android.presentation.view.costom_view.AspectRatioImageView; +import com.shaya.poinila.android.presentation.viewholder.SingleImageViewHolder; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.util.ResourceUtils; +import com.squareup.otto.Subscribe; +import com.squareup.picasso.Picasso; +import com.squareup.picasso.Target; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import data.PoinilaNetService; +import data.event.BaseEvent; +import data.event.SuggestedWebpagePostReceived; +import data.model.Image; +import data.model.PostType; +import data.model.SuggestedWebPagePost; + +/** + * Created by hossein on 8/18/16. + */ +public class NewWebSitePostSelectMediaFragment extends ListBusFragment { + + + private PostType postType; + private List targets; + private String siteAddress; + private SuggestedWebPagePost suggestedPost; + String[] protocols = {"http", "https"}; + List validProtocols = Arrays.asList(protocols); + RecyclerViewAdapter mRecyclerViewAdapter; + + public static NewWebSitePostSelectMediaFragment newInstance(PostType postType, String siteAddress){ + + NewWebSitePostSelectMediaFragment fragment = new NewWebSitePostSelectMediaFragment(); + fragment.postType = postType; + fragment.siteAddress = siteAddress; + + return fragment; + } + + @Override + protected void initUI() { + super.initUI(); + + targets = new ArrayList<>(); + + // TODO: parse url and extract imagesUrls greater than a specific size in each dimension + mRecyclerView = new RecyclerViewProvider(mRecyclerView). + setStaggeredLayoutManager(StaggeredGridLayoutManager.VERTICAL, + ResourceUtils.getInteger(R.integer.column_count)). + setAdapter(new RecyclerViewAdapter>(getActivity(), R.layout.single_image_staggered) { + @Override + protected SingleImageViewHolder getProperViewHolder(View v, int viewType) { + return new SingleImageViewHolder(v) { + @Override + public void fill(Image image) { + ((AspectRatioImageView) imageView).setAspectRatio(image.height * 1f / image.width); + imageView.requestLayout(); + Picasso.with(imageView.getContext()).load(image.url).into(imageView); + } + }; + } + }).bindViewToAdapter(); + + mRecyclerViewAdapter = (RecyclerViewAdapter) mRecyclerView.getAdapter(); + } + + @Override + protected RecyclerView.OnScrollListener getRecyclerViewListener() { + return null; + } + + @Override + public void requestForMoreData() { + //TODO + } + + @Override + public RecyclerViewAdapter createAndReturnRVAdapter() { + return mRecyclerViewAdapter; + } + + @Override + protected boolean sendsRequestAutomatically() { + return true; + } + + @Override + protected void requestInitialData() { + fetchSiteInfo(); + } + + private void fetchSiteInfo() { + targets.clear(); + suggestedPost = null; +// setLoading(new Loading()); + PoinilaNetService.getWebsiteInfo(siteAddress, postType); + } + + @Subscribe + public void urlsReceivedEvent(SuggestedWebpagePostReceived event) { + onGettingInitDataResponse(event); + } + + @Override + public void onSuccessfulInitData(BaseEvent baseEvent) { + super.onSuccessfulInitData(baseEvent); + getRecyclerViewAdapter().clear(); + suggestedPost = ((SuggestedWebpagePostReceived) baseEvent).webpagePost; + suggestedPost.siteAddress = this.siteAddress; + feedback(); + for (final Image image : suggestedPost.images) { + MeasureTarget target = new MeasureTarget(image.url); + targets.add(target); + Picasso.with(getActivity()).load(image.url).into(target); + } + } + + private void feedback() { + // TODO: + if (suggestedPost.images.isEmpty()) { + Logger.toast(R.string.error_no_image_found); + } + if ((suggestedPost.name != null || suggestedPost.summary != null)) { + Logger.toast(R.string.successfully_loaded); + } + } + + @Subscribe + public void onSiteImageClickEvent(ImageClickedUIEvent event) { + suggestedPost.imageAddress = mRecyclerViewAdapter.getItem(event.adapterPosition).url; + BusProvider.getBus().post(new NewWebsitePostEvent(suggestedPost)); + PageChanger.goToNewPost(getFragmentManager(), suggestedPost); + } + + @Override + public boolean mustShowProgressView() { + return false; + } + + @Override + public int getLayoutID() { + return R.layout.recycler_view_full; + } + + class MeasureTarget implements Target { + private final String address; + + public MeasureTarget(String address) { + this.address = address; + } + + @Override + public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { + if (bitmap.getWidth() > ConstantsUtils.MINIMUM_POST_IMAGE_WIDTH && + bitmap.getHeight() > ConstantsUtils.MINIMUM_POST_IMAGE_HEIGHT) { + getRecyclerViewAdapter().addItem(new Image(address, bitmap.getWidth(), bitmap.getHeight())); + Log.w("poinila_image", String.format("width: %d, height: %d", bitmap.getWidth(), bitmap.getHeight())); + } + } + + @Override + public void onBitmapFailed(Drawable errorDrawable) { + } + + @Override + public void onPrepareLoad(Drawable placeHolderDrawable) { + } + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/NotificationFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/NotificationFragment.java new file mode 100755 index 0000000..96e705f --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/NotificationFragment.java @@ -0,0 +1,406 @@ +package com.shaya.poinila.android.presentation.view.fragments; + + +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 com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.NotificationAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewProvider; +import com.shaya.poinila.android.presentation.uievent.MemberClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.NotifActorClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.NotifParticipantClickedUIEvent; +import com.shaya.poinila.android.presentation.view.activity.InvitationNotifListActivity; +import com.shaya.poinila.android.util.NavigationUtils; +import com.squareup.otto.Subscribe; +import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration; + +import java.util.ArrayList; +import java.util.List; + +import butterknife.Bind; +import butterknife.BindInt; +import butterknife.OnClick; +import data.PoinilaNetService; +import data.event.AbstractNotificationsReceivedEvent; +import data.event.AnswerFriendRequestResponse; +import data.event.BaseEvent; +import data.event.MyFriendshipRequestsEvent; +import data.model.FriendRequestAnswer; +import data.model.ImageUrls; +import data.model.InvitationNotif; +import data.model.Loading; +import data.model.Member; +import data.model.Notification; +import data.model.Participant; +import data.model.Post; + +import static android.support.v7.widget.LinearLayoutManager.VERTICAL; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setFont; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setImage; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; + +public class NotificationFragment extends ListBusFragment { + + @BindInt(R.integer.friendship_invitations_summary_limit) + int INVITATION_LIMIT; + private static final int INVITE_PER_REQUEST = 10; + @Bind(R.id.my_notifs) + TextView mMyNotificationsBtn; + @Bind(R.id.others_notifs) + TextView mOthersNotificationsBtn; + @Bind(R.id.invitations_notifications) + ViewGroup mInvitationsNotificationsContainer; + @Bind(R.id.invitations_header) + TextView mInvitationHeaderView; + @Bind(R.id.container) + ViewGroup rootView; + + private String myNotifsBookmark, othersNotifBookmark, acceptedFriendshipBookmark, myFriendshipRequessBookmark; + List myNotifs; + List othersNotifs; + List mInviteNotifs; + + + private int state; + private static final int STATE_NOTHING_SELECTED = 1; + private static final int STATE_MY_NOTIFS = 2; + private static final int STATE_OTHERS_NOTIF = 3; + private int partsReceived; + + + public NotificationFragment() { + // Required empty public constructor + } + + public static NotificationFragment newInstance(){ + NotificationFragment fragment = new NotificationFragment(); + + return fragment; + } + + + @Override + public int getLayoutID() { + return R.layout.fragment_notification; + } + + @Override + protected void initUI() { + /*getChildFragmentManager().beginTransaction().add(R.actorID.inner_container, + new MyNotificationsListFragment(), + TAG_MY_NOTIFICATION_FRAGMENT).commit();*/ + mRecyclerView = new RecyclerViewProvider(mRecyclerView). + setLinearLayoutManager(VERTICAL). + setAdapter(getRecyclerViewAdapter()). + bindViewToAdapter(); +// mRecyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getContext()) +// .marginResId(R.dimen.margin_lvl1) +// .build()); + + myNotifs = new ArrayList<>(); + othersNotifs = new ArrayList<>(); + mInviteNotifs = new ArrayList<>(); + + switchToMyNotifications(); + } + + public void switchToMyNotifications() { + mInvitationsNotificationsContainer.setVisibility(mInviteNotifs.isEmpty() ? View.GONE : View.VISIBLE); + mInvitationHeaderView.setVisibility(mInviteNotifs.isEmpty() ? View.GONE : View.VISIBLE); + + setFont(mMyNotificationsBtn, getString(R.string.default_bold_font_path)); + setFont(mOthersNotificationsBtn, getString(R.string.default_font_path)); + + mMyNotificationsBtn.setSelected(true); + mOthersNotificationsBtn.setSelected(false); + + mMyNotificationsBtn.setTextColor(getResources().getColor(R.color.white)); + mOthersNotificationsBtn.setTextColor(getResources().getColor(R.color.poinila_dark_gray)); + +// mMyNotificationsBtn.setTextSize(getResources().getDimension(R.dimen.fontsize_medium)); +// mOthersNotificationsBtn.setTextSize(getResources().getDimension(R.dimen.fontsize_small)); + + getRecyclerViewAdapter().resetData(myNotifs); + state = STATE_MY_NOTIFS; + } + + /* private void updateAdapter(List list) { + mAdapter.resetData(list); + } + + private List sortMyNotifs(List myNotifs) { + return myNotifs; + }*/ + + public void switchToOthersNotifications() { + mInvitationsNotificationsContainer.setVisibility(View.GONE); + mInvitationHeaderView.setVisibility(View.GONE); + + setFont(mMyNotificationsBtn, getString(R.string.default_font_path)); + setFont(mOthersNotificationsBtn, getString(R.string.default_bold_font_path)); + + mMyNotificationsBtn.setSelected(false); + mOthersNotificationsBtn.setSelected(true); + + mMyNotificationsBtn.setTextColor(getResources().getColor(R.color.poinila_dark_gray)); + mOthersNotificationsBtn.setTextColor(getResources().getColor(R.color.white)); + +// mMyNotificationsBtn.setTextSize(getResources().getDimension(R.dimen.fontsize_small)); +// mOthersNotificationsBtn.setTextSize(getResources().getDimension(R.dimen.fontsize_medium)); + + getRecyclerViewAdapter().resetData(othersNotifs); + //updateAdapter(sortNotifs(othersNotif)); + state = STATE_OTHERS_NOTIF; + } + + private List sortNotifs(List othersNotif) { + // TODO: + return othersNotif; + } + + @OnClick(R.id.my_notifs) + public void onMyNotifications() { + switchToMyNotifications(); + } + + @OnClick(R.id.others_notifs) + public void onOthersNotifications() { + switchToOthersNotifications(); + } + + @Override + protected RecyclerView.OnScrollListener getRecyclerViewListener() { + return RecyclerViewProvider.linearListEndDetectorListener(getRecyclerViewAdapter(), this); + } + + @Override + public void onLoadMore() { + if (!isResumed()) + return; + super.onLoadMore(); + } + + @Override + public void initData() { + super.initData(); + myNotifs.clear(); + } + + @Override + public RecyclerViewAdapter createAndReturnRVAdapter() { + return new NotificationAdapter(getActivity()); + } + + @Override + protected boolean sendsRequestAutomatically() { + return true; + } + + @Override + protected void requestInitialData() { + PoinilaNetService.getMyFriendshipRequests(null); + PoinilaNetService.getMyNotifications(myNotifsBookmark); + PoinilaNetService.getOthersNotification(othersNotifBookmark); + } + + @Override + public void onSuccessfulInitData(BaseEvent baseEvent) { + super.onSuccessfulInitData(baseEvent); + mInviteNotifs = ((MyFriendshipRequestsEvent) baseEvent).data; + updateInviteNotifs(); + } + + @Override + protected boolean isListDataResponseValid(BaseEvent baseEvent, String responseBookmark) { + if (baseEvent instanceof AbstractNotificationsReceivedEvent.MyNotificationsReceivedEvent) { + return checkBookMark(myNotifsBookmark, responseBookmark); + } else if (baseEvent instanceof AbstractNotificationsReceivedEvent.OthersNotificationsReceivedEvent) { + return checkBookMark(othersNotifBookmark, responseBookmark); + } + return false; + } + + @Override + public boolean mustShowProgressView() { + return true; + } + + @Override + public void requestForMoreData() { + switch (state) { + case STATE_MY_NOTIFS: + PoinilaNetService.getMyNotifications(myNotifsBookmark); + break; + case STATE_OTHERS_NOTIF: + PoinilaNetService.getOthersNotification(othersNotifBookmark); + break; + } + } + + @Override + public void onSuccessfulListData(BaseEvent baseEvent, String newBookmark) { + super.onSuccessfulListData(baseEvent, newBookmark); // bookmark is useless in this page. + AbstractNotificationsReceivedEvent event = ((AbstractNotificationsReceivedEvent) baseEvent); + + if (baseEvent instanceof AbstractNotificationsReceivedEvent.MyNotificationsReceivedEvent) { + myNotifs.addAll(event.data); + myNotifsBookmark = event.bookmark; + if (state == STATE_MY_NOTIFS) getRecyclerViewAdapter().addItems(event.data); + } else if (baseEvent instanceof AbstractNotificationsReceivedEvent.OthersNotificationsReceivedEvent) { + othersNotifs.addAll(event.data); + othersNotifBookmark = event.bookmark; + if (state == STATE_OTHERS_NOTIF) getRecyclerViewAdapter().addItems(event.data); + } + + if(getRecyclerViewAdapter().getItemCount() >= 25){ + setLoading(new Loading()); + } + + + // myNotifs/othersNotifs has already set as adapter's data source. so by adding to adapters item, we + // update myNotifs as well + //getRecyclerViewAdapter().notifyDataSetChanged(); + } + + @Subscribe + public void onMyNotifsReceived(AbstractNotificationsReceivedEvent.MyNotificationsReceivedEvent event) { + onGettingListDataResponse(event, event.bookmark); + } + + @Subscribe + public void onOthersNotifsReceived(AbstractNotificationsReceivedEvent.OthersNotificationsReceivedEvent event) { + onGettingListDataResponse(event, event.bookmark); + } + + @Subscribe + public void onMyFriendshipRequestsReceived(MyFriendshipRequestsEvent event) { + onGettingInitDataResponse(event); +// partOfInitDataReceived(); + } + +// @Subscribe +// public void onProfilePicClickedEvent(MemberClickedUIEvent event) { +// if (event.receiverName != BaseEvent.ReceiverName.NotificationFragment) return; +// Member member =((Notification)getRecyclerViewAdapter().getItem(event.adapterPosition)).mainActor.userName; +// PageChanger.goToProfile(getActivity(), member); +// } + /*private void partOfInitDataReceived() { + partsReceived++; + if (partsReceived >= 3){ + onGettingInitDataResponse(); + } + }*/ + + private void updateInviteNotifs() { + mInvitationsNotificationsContainer.removeAllViews(); + if (mInviteNotifs.isEmpty()) { + mInvitationsNotificationsContainer.setVisibility(View.GONE); + mInvitationHeaderView.setVisibility(View.GONE); + return; + } + + mInvitationsNotificationsContainer.setVisibility(View.VISIBLE); + mInvitationHeaderView.setVisibility(View.VISIBLE); + LayoutInflater inflater = LayoutInflater.from(getActivity()); + for (int i = 0; i < INVITATION_LIMIT && i < mInviteNotifs.size(); i++) { + addInviteNotif(inflater, mInviteNotifs.get(i)); + } + updateNotifCountText(); + } + + private void updateNotifCountText() { + String res; + int count = mInviteNotifs.size(); + if (count < INVITATION_LIMIT) + res = getString(R.string.view_all_invitations); + else if (count < INVITE_PER_REQUEST) + res = getString(R.string.view_all_invitations).concat(String.format(" (%d)", count)); + else + res = getString(R.string.view_all_invitations).concat(String.format(" (%d+)", INVITE_PER_REQUEST)); + mInvitationHeaderView.setText(res); + } + + @OnClick(R.id.invitations_header) + public void viewAllInvitations() { + NavigationUtils.goToActivity(InvitationNotifListActivity.class, getActivity()); + } + + private void addInviteNotif(LayoutInflater inflater, final InvitationNotif notif) { + final View inviteNotifView = inflater.inflate(R.layout.notif_requested_tobe_your_friend, + mInvitationsNotificationsContainer, false); + setImage((ImageView) inviteNotifView.findViewById(R.id.image), notif.member.imageUrls, ImageUrls.ImageType.MEMBER, ImageUrls.ImageSize.AVATAR); + setText((TextView) inviteNotifView.findViewById(R.id.title), notif.member.fullName); + setText((TextView) inviteNotifView.findViewById(R.id.subtitle), getString(R.string.requested_to_be_your_friend)); + + mInvitationsNotificationsContainer.addView(inviteNotifView); + + /*--Accept--*/ + inviteNotifView.findViewById(R.id.agree).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + PoinilaNetService.answerFriendRequest(notif.member.id, FriendRequestAnswer.ACCEPT, -1); + clickedNotif = notif; + } + }); + /*--Reject--*/ + inviteNotifView.findViewById(R.id.ignore).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + PoinilaNetService.answerFriendRequest(notif.member.id, FriendRequestAnswer.REJECT, -1); + clickedNotif = notif; + } + }); + inviteNotifView.findViewById(R.id.image).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + PageChanger.goToProfile(getActivity(), notif.member.getId()); + } + }); + } + + private InvitationNotif clickedNotif; + + @Subscribe + public void onAnswerFriendRequestResponse(AnswerFriendRequestResponse event) { + if (event.succeed) { + mInviteNotifs.remove(clickedNotif); + // TODO: on accepting as a friend, chanage item view to "you and felani are now friends" + updateInviteNotifs(); + } + } + + @Subscribe + public void onMainActorClicked(NotifActorClickedUIEvent event) { + Notification notification = ((Notification)getRecyclerViewAdapter().getItem(event.adapterPosition)); + Participant participant = notification.mainActor; + if (participant == null) + return; + goToPage(participant, participant.type); + } + + @Subscribe + public void onParticipantActorClicked(NotifParticipantClickedUIEvent event) { + goToPage(event.participant, event.participantsType); + } + + private void goToPage(Participant participant, ImageUrls.ImageType type) { + switch (type) { + case MEMBER: + PageChanger.goToProfile(getActivity(), participant.getId()); + break; + case COLLECTION: + PageChanger.goToCollection(getActivity(), participant.getId(), participant.collectionName, null); + break; + case POST: + PageChanger.goToPost(getActivity(), participant.getId()); + break; + } + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/NotificationSwitchFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/NotificationSwitchFragment.java new file mode 100755 index 0000000..ea7f585 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/NotificationSwitchFragment.java @@ -0,0 +1,200 @@ +package com.shaya.poinila.android.presentation.view.fragments; + +import android.os.Bundle; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.SwitchCompat; +import android.support.v7.widget.Toolbar; +import android.view.Gravity; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.View; +import android.widget.CompoundButton; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewProvider; +import com.shaya.poinila.android.presentation.uievent.OnOffSettingToggledUIEvent; +import com.shaya.poinila.android.presentation.view.activity.FragmentHostActivity; +import com.shaya.poinila.android.presentation.viewholder.SwitchTextViewHolder; +import com.shaya.poinila.android.util.ResourceUtils; +import com.squareup.otto.Subscribe; +import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration; + +import data.PoinilaNetService; +import data.event.BaseEvent; +import data.event.NotificationSettingsReceivedEvent; +import data.model.OnOffSetting; + +import static android.support.v7.widget.LinearLayoutManager.VERTICAL; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_ENTITY; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_REQUEST_ID; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_APPLICATION_NOTIFICATION; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_EMAIL_NOTIFICATION; + +/** + * Created by iran on 2015-09-07. + */ +public class NotificationSwitchFragment extends ListBusFragment { + + private int requestID; + private OnOffSetting emailOnOffSetting; + private SwitchCompat emailOnOffSwitch; + + public static NotificationSwitchFragment newInstance(String actorID, int requestID){ + NotificationSwitchFragment f = new NotificationSwitchFragment(); + f.requestID = requestID; + Bundle b = new Bundle(); + b.putInt(KEY_REQUEST_ID, requestID); + b.putString(KEY_ENTITY, actorID); + f.setArguments(b); + return f; + } + + @Override + protected RecyclerView.OnScrollListener getRecyclerViewListener() { + return new RecyclerView.OnScrollListener() { + }; + } + + @Override + public int getLayoutID() { + return R.layout.recycler_view_full; + } + + @Override + protected void initUI() { + Bundle b = getArguments(); + requestID = b.getInt(KEY_REQUEST_ID); + + int padding = (int) ResourceUtils.getDimen(R.dimen.padding_lvl1); + mRecyclerView.setPadding(padding, padding, padding, padding); + mRecyclerView = new RecyclerViewProvider(mRecyclerView). + setLinearLayoutManager(VERTICAL). + setAdapter(getRecyclerViewAdapter()). + bindViewToAdapter(); + mRecyclerView.addItemDecoration( + new HorizontalDividerItemDecoration.Builder(getContext()) + .marginResId(R.dimen.margin_lvl1) + .build()); + switch (requestID){ + case REQUEST_APPLICATION_NOTIFICATION: + getActivity().setTitle(R.string.title_activity_application_notification); + setHasOptionsMenu(false); + break; + case REQUEST_EMAIL_NOTIFICATION: + emailOnOffSetting = new OnOffSetting(); + getActivity().setTitle(R.string.title_activity_email_notification); + setHasOptionsMenu(true); + emailOnOffSwitch = (SwitchCompat) getActivity().getLayoutInflater().inflate(R.layout.poinila_switch, null); + + emailOnOffSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (isChecked) emailOnOffSetting.on(); + else emailOnOffSetting.off(); + activeAllChilds(getRecyclerViewAdapter(), isChecked); + PoinilaNetService.setEmailNotificationSetting(emailOnOffSetting); + //enableLayoutChildes(mRecyclerView, isChecked); + } + }); + + Toolbar.LayoutParams lp = new Toolbar.LayoutParams(Gravity.RIGHT); + lp.setMargins(0,0, ((int) ResourceUtils.getDimen(R.dimen.margin_lvl2)), 0); + ((FragmentHostActivity) getActivity()).getToolbar().addView(emailOnOffSwitch, lp); + break; + } + } + + private void activeAllChilds(RecyclerViewAdapter recyclerViewAdapter, boolean isChecked) { + for (Object setting : recyclerViewAdapter.getItems()) { + ((OnOffSetting)setting).enabled = isChecked; + } + recyclerViewAdapter.notifyItemRangeChanged(0, recyclerViewAdapter.getItemCount()); + } + + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + // TODO Add your menu entries here + super.onCreateOptionsMenu(menu, inflater); + } + + @Subscribe public void onNotificationSettingsReceived(NotificationSettingsReceivedEvent event){ + onGettingInitDataResponse(event); + } + + @Subscribe public void onSettingChanged(OnOffSettingToggledUIEvent event){ + OnOffSetting setting = getRecyclerViewAdapter().getItem(event.adapterPosition); + + if (event.settingOn) setting.on(); + else setting.off(); + + switch (requestID){ + case REQUEST_EMAIL_NOTIFICATION: + PoinilaNetService.setEmailNotificationSetting(setting); + break; + case REQUEST_APPLICATION_NOTIFICATION: + PoinilaNetService.setApplicationNotificationSetting(setting); + break; + } + } + +/*------------------*/ + @Override + protected boolean sendsRequestAutomatically() { + return true; + } + + @Override + protected void requestInitialData() { + switch (requestID){ + case REQUEST_APPLICATION_NOTIFICATION: + PoinilaNetService.getApplicationNotification(); + break; + case REQUEST_EMAIL_NOTIFICATION: + PoinilaNetService.getEmailNotification(); + break; + } + } + + @Override + public boolean mustShowProgressView() { + return true; + } + + @Override + public void requestForMoreData() { + + } + + @Override + public RecyclerViewAdapter createAndReturnRVAdapter() { + return new RecyclerViewAdapter(getActivity(), R.layout.switch_text_setting) { + @Override + protected SwitchTextViewHolder getProperViewHolder(View v, int viewType) { + return new SwitchTextViewHolder(v); + } + }; + } + + @Override + public void onSuccessfulInitData(BaseEvent baseEvent) { + super.onSuccessfulInitData(baseEvent); + NotificationSettingsReceivedEvent event = ((NotificationSettingsReceivedEvent) baseEvent); + if (requestID == REQUEST_EMAIL_NOTIFICATION) { + // reverse loop preventing concurrent modification exception + for (int i = event.notificationSettings.size() - 1; i >= 0 ; i--) { + if (event.notificationSettings.get(i).code.equals("email_notification")){ + emailOnOffSetting = event.notificationSettings.get(i); + event.notificationSettings.remove(i); + } + } + + for (OnOffSetting setting : event.notificationSettings){ + setting.enabled = emailOnOffSetting.value == OnOffSetting.ON; + } + + emailOnOffSwitch.setChecked(emailOnOffSetting.value == OnOffSetting.ON); + } + getRecyclerViewAdapter().resetData(event.notificationSettings); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/PostAndRelatedPostFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/PostAndRelatedPostFragment.java new file mode 100755 index 0000000..a153bd9 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/PostAndRelatedPostFragment.java @@ -0,0 +1,943 @@ +package com.shaya.poinila.android.presentation.view.fragments; + +import android.Manifest; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.support.annotation.Nullable; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; +import android.support.v4.widget.NestedScrollView; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.StaggeredGridLayoutManager; +import android.text.Html; +import android.text.TextUtils; +import android.util.Log; +import android.util.TypedValue; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.Window; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewProvider; +import com.shaya.poinila.android.presentation.uievent.CollectionClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.ExploreTagEvent; +import com.shaya.poinila.android.presentation.uievent.MemberClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.NewWebsitePostEvent; +import com.shaya.poinila.android.presentation.uievent.PostClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.PostComponentClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.RemovePostUIEvent; +import com.shaya.poinila.android.presentation.uievent.sync.PostActionSyncEvent; +import com.shaya.poinila.android.presentation.view.ViewInflater; +import com.shaya.poinila.android.presentation.view.dialog.DialogLauncher; +import com.shaya.poinila.android.presentation.view.dialog.PoinilaAlertDialog; +import com.shaya.poinila.android.presentation.view.help.Help; +import com.shaya.poinila.android.presentation.view.video.PonilaVideoView; +import com.shaya.poinila.android.presentation.viewholder.BaseViewHolder; +import com.shaya.poinila.android.presentation.viewholder.DashboardPostViewHolder; +import com.shaya.poinila.android.presentation.viewholder.PostDetailViewHolder; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.util.PoinilaPreferences; +import com.shaya.poinila.android.util.RandomUtils; +import com.shaya.poinila.android.util.StringUtils; +import com.shaya.poinila.android.util.TimeUtils; +import com.squareup.otto.Subscribe; + +import org.apmem.tools.layouts.FlowLayout; + +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import butterknife.Bind; +import butterknife.ButterKnife; +import butterknife.OnClick; +import data.PoinilaNetService; +import data.event.BaseEvent; +import data.event.ContentReceivedEvent; +import data.event.PostReceivedEvent; +import data.event.PostsReceivedEvent; +import data.event.UndoFavePostEvent; +import data.event.UndoUnfavePostEvent; +import data.model.Collection; +import data.model.ImageUrls; +import data.model.Loading; +import data.model.Member; +import data.model.Post; +import data.model.PostType; +import data.model.PrivacyType; +import data.model.Tag; +import manager.DataRepository; +import manager.RequestSource; +import manager.dowload.NotificationDLManager; + +import static android.support.v7.widget.LinearLayoutManager.VERTICAL; +import static com.shaya.poinila.android.presentation.uievent.PostComponentClickedUIEvent.Type.Comments; +import static com.shaya.poinila.android.presentation.uievent.PostComponentClickedUIEvent.Type.Fave; +import static com.shaya.poinila.android.presentation.uievent.PostComponentClickedUIEvent.Type.FaversList; +import static com.shaya.poinila.android.presentation.uievent.PostComponentClickedUIEvent.Type.FullImage; +import static com.shaya.poinila.android.presentation.uievent.PostComponentClickedUIEvent.Type.OriginalCollection; +import static com.shaya.poinila.android.presentation.uievent.PostComponentClickedUIEvent.Type.Poster; +import static com.shaya.poinila.android.presentation.uievent.PostComponentClickedUIEvent.Type.Reference; +import static com.shaya.poinila.android.presentation.uievent.PostComponentClickedUIEvent.Type.Repost; +import static com.shaya.poinila.android.presentation.uievent.PostComponentClickedUIEvent.Type.RepostersList; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setImage; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_ENTITY; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_REQUEST_ID; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_SECOND_ENTITY_ID; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_COLLECTION_POSTS; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_POST_RELATED_POSTS; + +/** + * Created by iran on 8/14/2016. + */ +public class PostAndRelatedPostFragment extends ListBusFragment + implements PonilaVideoView.OnFullScreenListener{ + + + private static final String VIDEO_POSITION = "video position"; + private static final String VIDEO_IS_PLAYING = "video state"; + + + protected Collection collection; + private int requestType; + private String mainEntityId; + private String secondEntityId; + + private boolean showedHelp = false; + + // must be moved to bus fragment + private Set activeRequests; + + @Bind(R.id.post_video_view) + PonilaVideoView videoView; + + Post mainPost; + + @Bind(R.id.post_title) + ViewGroup postTitle; + /* TextView postName; + ImageView faveIcon; + TextView websiteName; + TextView creationTime;*/ + @Bind(R.id.post_image) + ImageView postImage; + + @Bind(R.id.content) + TextView postContent; + + @Bind(R.id.website) TextView website; + @Bind(R.id.reference_container) ViewGroup postReferenceContainer; + + @Bind(R.id.collection_info) View collectionInfo; + @Bind(R.id.author_info) View authorInfo; + + @Bind(R.id.tags_divider) View tagsDivider; + @Bind(R.id.tags_container) + FlowLayout tagsContainer; + + @Bind(R.id.comment_container) ViewGroup commentsContainer; + + @Bind(R.id.stats) ViewGroup postStats; + ImageButton commentBtn, repostBtn, faveBtn; + TextView faveCount, commentCount, repostCount; + + @Bind(R.id.original_collection) ViewGroup originalCollection; + + @Bind(R.id.zoom_btn) + ImageView zoomBtn; + +// + @Bind(R.id.main_post) + LinearLayout mainPostContainer; + + @Bind(R.id.nested_scroll_view) + NestedScrollView nestedScrollView; + + + private final static int PERMISSION_WRITE_EXTERNAL_STORAGE = 10; + + + public static PostAndRelatedPostFragment newInstance(String mainEntityId, int requestID) { + return newInstance(mainEntityId, null, requestID); + } + + public static PostAndRelatedPostFragment newInstance(String mainEntityId, String secondEntityId, int requestID) { + PostAndRelatedPostFragment f = new PostAndRelatedPostFragment(); + Bundle arguments = new Bundle(); + arguments.putString(KEY_ENTITY, mainEntityId); + arguments.putString(KEY_SECOND_ENTITY_ID, secondEntityId); + arguments.putInt(KEY_REQUEST_ID, requestID); + //arguments.putBoolean(ConstantsUtils.KEY_IS_USER_COLLECTION, isCollectionOwnedByUser); + f.setArguments(arguments); + return f; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mainEntityId = getArguments().getString(KEY_ENTITY); + requestType = getArguments().getInt(KEY_REQUEST_ID); + secondEntityId = getArguments().getString(KEY_SECOND_ENTITY_ID); + + activeRequests = new HashSet<>(2); + setHasOptionsMenu(true); + + } + + @Override + protected void initUI() { + super.initUI(); + + mRecyclerView = new RecyclerViewProvider(mRecyclerView).setAdapter(getRecyclerViewAdapter()). + setStaggeredLayoutManager(VERTICAL, getResources().getInteger(R.integer.column_count)). + bindViewToAdapter(); + mRecyclerView.getItemAnimator().setChangeDuration(0); + mRecyclerView.setNestedScrollingEnabled(false); + + nestedScrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() { + @Override + public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) { + if (scrollY == (v.getChildAt(0).getMeasuredHeight() - v.getMeasuredHeight())) { + onLoadMore(); + } + } + }); + + getActivity().setTitle(getString(R.string.post)); + initUIMainPost(); + mainPost = DataRepository.getInstance().getTempModel(Post.class); + if(mainPost != null) + fill(mainPost); + + mRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + + if(!PoinilaPreferences.getHelpStatus(PostAndRelatedPostFragment.this.getClass().getName() + ".PostPage")){ + Help.getInstance().showPostRelatedPostsHelp(getActivity(), postImage); + PoinilaPreferences.putHelpStatus(PostAndRelatedPostFragment.this.getClass().getName()+ ".PostPage", true); + + } + + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) { + mRecyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this); + } else { + mRecyclerView.getViewTreeObserver().removeGlobalOnLayoutListener(this); + } + + } + }); + + } + + @Override + public void onPause() { + super.onPause(); + } + + private void computeVideoSize(){ + int width = getActivity().getResources().getDisplayMetrics().widthPixels; + int height = (mainPost.imagesUrls.x736.height * width) / mainPost.imagesUrls.x736.width; + videoView.setLayoutParams( + new LinearLayout.LayoutParams(width, height)); + } + + private void initUIMainPost(){ + + faveCount = ButterKnife.findById(postStats, R.id.fave_num); + faveBtn = ButterKnife.findById(postStats, R.id.fave_icon); + faveCount.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new PostComponentClickedUIEvent(FaversList)); + } + }); + + faveBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new PostComponentClickedUIEvent(Fave)); + } + }); + + commentCount = ButterKnife.findById(postStats, R.id.comment_num); + commentBtn = ButterKnife.findById(postStats, R.id.comment_icon); + commentCount.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new PostComponentClickedUIEvent(Comments)); + } + }); + + commentBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new PostComponentClickedUIEvent(Comments)); + } + }); + + repostCount = ButterKnife.findById(postStats, R.id.repost_num); + repostBtn = ButterKnife.findById(postStats, R.id.repost_icon); + repostCount.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new PostComponentClickedUIEvent(RepostersList)); + } + }); + repostBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new PostComponentClickedUIEvent(Repost)); + } + }); + + authorInfo.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new PostComponentClickedUIEvent(Poster)); + } + }); + collectionInfo.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new PostComponentClickedUIEvent(PostComponentClickedUIEvent.Type.Collection)); + } + }); + originalCollection.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new PostComponentClickedUIEvent(OriginalCollection)); + } + }); + } + + public void fill(final Post post) { + + /*------actual fill--------*/ + + + ((TextView)postTitle.findViewById(R.id.title)).setText( + post != null && post.name != null ? post.name : ""); + //((TextView)postTitle.findViewById(R.id.subtitle)).setText(post.author.urlName); + //((TextView)postTitle).findViewById(R.actorID.image)) + ((TextView)postTitle.findViewById(R.id.date_created)). + setText(TimeUtils.getTimeString(post.creationTime, DataRepository.getInstance().getServerTimeDifference())); + + if (post.type == PostType.IMAGE) { + postImage.setVisibility(View.VISIBLE); + setImage(postImage, post.imagesUrls, ImageUrls.ImageType.POST, ImageUrls.ImageSize.BIG); + setText(postContent, post.summary); + + postImage.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (TextUtils.isEmpty(post.originalWebpage)) + BusProvider.getBus().post(new PostComponentClickedUIEvent(FullImage)); + else + BusProvider.getBus().post(new PostComponentClickedUIEvent(Reference)); + } + }); + } else{ + //DataRepository.getInstance().getPostContent(post.contentUrl.url, postContent); + postImage.setVisibility(View.GONE); + if (!TextUtils.isEmpty(post.contentUrl)){ + if (TextUtils.isEmpty(post.content)) + DataRepository.getInstance().getPostContent(post.contentUrl, post.id); + else + setText(postContent, Html.fromHtml(post.content)); + } + } + + setImage((ImageView) authorInfo.findViewById(R.id.image), + post.author.imageUrls, ImageUrls.ImageType.MEMBER, ImageUrls.ImageSize.AVATAR); + ((TextView)authorInfo.findViewById(R.id.title)).setText(post.author.fullName); + + setImage((ImageView) collectionInfo.findViewById(R.id.image), + post.collection.coverImageUrls, ImageUrls.ImageType.COLLECTION, ImageUrls.ImageSize.AVATAR); + ((TextView)collectionInfo.findViewById(R.id.title)).setText(post.collection.name); + + if (TextUtils.isEmpty(post.originalWebpage)) + postReferenceContainer.setVisibility(View.GONE); + else { + setText(website, post.originalWebpage); + postReferenceContainer.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new PostComponentClickedUIEvent(Reference)); + } + }); + } + + if (post.tags == null || post.tags.isEmpty()){ + tagsContainer.setVisibility(View.GONE); + tagsDivider.setVisibility(View.GONE); + } + else{ + tagsContainer.removeAllViews(); + for (Tag tag : post.tags){ + //tagsContainer.addView(ViewInflater.inflateNormalTag(tag, getActivity())); + // TODO: difference between tag in post and interest in member may rise some issues + ViewInflater.addTagToContainer(tagsContainer, tag); + } + } + + /*---Comments----*/ + if (post.comments == null || post.comments.isEmpty()){ + commentsContainer.setVisibility(View.GONE); + //??? findviewbyid + rootView.findViewById(R.id.comment_container_divider).setVisibility(View.GONE); + }else{ + commentsContainer.removeAllViews(); + for (int i = 0; i < 3 && i < post.comments.size(); i++){ + commentsContainer.addView(ViewInflater.inflateComment(post.comments.get(i), rootView.getContext())); // ???getActivity + } + } + + /*----stats----*/ + if (post.privacy == PrivacyType.PRIVATE){ + repostBtn.setVisibility(View.INVISIBLE); + repostCount.setVisibility(View.INVISIBLE); + }else { + setText(repostCount, post.repostCount); + } + setText(faveCount, post.faveCount); + faveBtn.setSelected(post.favedByMe); + setText(commentCount, post.commentCount); + + if (post.originalCollection != null) { + setImage((ImageView) originalCollection.findViewById(R.id.image), post.originalCollection.coverImageUrls, + ImageUrls.ImageType.COLLECTION, ImageUrls.ImageSize.AVATAR); + ((TextView) originalCollection.findViewById(R.id.subtitle)). + setText(String.valueOf(post.originalCollection.name)); + ((TextView) originalCollection.findViewById(R.id.fave_num)). + setText(String.valueOf(post.originalCollection.totalLikeCount)); + ((TextView) originalCollection.findViewById(R.id.comment_num)). + setText(String.valueOf(post.originalCollection.totalCommentCount)); + ((TextView) originalCollection.findViewById(R.id.repost_num)). + setText(String.valueOf(post.originalCollection.totalRepostCount)); + }else{ + originalCollection.setVisibility(View.GONE); + } + + if(!videoView.isPlaying() && mainPost.type.equals(PostType.VIDEO)){ + videoView.setOnFullScreenListener(this); + videoView.setVisibility(View.VISIBLE); + videoView.setVideoPreview(mainPost.imagesUrls); + videoView.setVideoPath(mainPost.videoUrl); + } + + if(mainPost.type.equals(PostType.IMAGE)) + zoomBtn.setVisibility(View.VISIBLE); + else + zoomBtn.setVisibility(View.GONE); + + if(mainPost.type.equals(PostType.VIDEO)) + computeVideoSize(); + } + + + @Override + public void onStart() { + super.onStart(); + + BusProvider.getSyncUIBus().register(this); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.menu_post, menu); + + if(mainPost != null && !mainPost.type.equals(PostType.TEXT)) + menu.findItem(R.id.menu_item_download).setVisible(true); + else + menu.findItem(R.id.menu_item_download).setVisible(false); + + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // handle item selection + switch (item.getItemId()) { + case R.id.menu_item_download: + // Here, thisActivity is the current activity + if (ContextCompat.checkSelfPermission(getActivity(), + Manifest.permission.WRITE_EXTERNAL_STORAGE) + != PackageManager.PERMISSION_GRANTED) { + + ActivityCompat.requestPermissions(getActivity(), + new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, + PERMISSION_WRITE_EXTERNAL_STORAGE); + + }else { + download(); + } + + return true; + case R.id.menu_item_share: + // Handle this selection + if(mainPost != null) + launchShareMenu(mainPost); + return true; + case R.id.menu_item_report: + // Handle this selection + if ( mainPost != null && getRecyclerViewAdapter().getItemCount() >= 1) + DialogLauncher.launchReportDialog( + getFragmentManager(), + R.string.report_post, + mainPost.id); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + private void download(){ + String url = ""; + switch (mainPost.type){ + case IMAGE: + url = mainPost.imagesUrls.x736.url; + break; + case VIDEO: + url = mainPost.videoUrl; + break; + } + NotificationDLManager.getInstance().download(url, mainPost.name, ""); + } + + @Override + public void onRequestPermissionsResult(int requestCode, + String permissions[], int[] grantResults) { + switch (requestCode) { + case PERMISSION_WRITE_EXTERNAL_STORAGE: { + // If request is cancelled, the result arrays are empty. + if (grantResults.length > 0 + && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + if(mainPost != null) + download(); + + } else { + + // permission denied, boo! Disable the + // functionality that depends on this permission. + } + return; + } + + // other 'case' lines to check for other + // permissions this app might request + } + } + + private void launchShareMenu(Post post) { + Intent shareIntent = new Intent(Intent.ACTION_SEND); + shareIntent.setType("text/plain"); + String extra = null; + switch (requestType) { + case REQUEST_COLLECTION_POSTS: + extra = getString(R.string.checkout_this_collection) + "\n" + + getString(R.string.collection_share_url, + ConstantsUtils.POINILA_ORIGIN_ADDRESS, + Uri.encode(collection.owner.uniqueName), + Uri.encode(collection.name)) + "\n" + + getString(R.string.ponila_world_of_interest); + break; + case REQUEST_POST_RELATED_POSTS: + extra = post.name + "\n\n" + + post.summary + "\n" + + getString(R.string.checkout_this_post) + "\n" + + getString(R.string.post_share_url, ConstantsUtils.POINILA_ORIGIN_ADDRESS, + mainPost.getId()) + "\n" + + getString(R.string.ponila_world_of_interest); + break; + default: + return; + } + shareIntent.putExtra(Intent.EXTRA_TEXT, extra); + startActivity(Intent.createChooser(shareIntent, getString(R.string.share_dialog_title))); + } + + @Subscribe + public void onNewUrlImagePostEvent(NewWebsitePostEvent event){ + //DialogLauncher.launchNewPost(getChildFragmentManager(), event.suggestedPost); + PageChanger.goToNewPost(getFragmentManager(), event.suggestedPost); + } + + @Subscribe + public void onPostsReceived(PostsReceivedEvent event) { + populateIfNecessary(event); // was necessary earlier because response came from server lacked info about posts collection + onGettingInitDataResponse(event); + onGettingListDataResponse(event, event.bookmark); + } + + @Subscribe + public void onContentReceivedEvent(final ContentReceivedEvent event) { + mainPost.content = StringUtils.removeHtmlDirAttribute(event.content); + fill(mainPost); + } + + @Subscribe + public void onPostDetailsComponentClickEvent(PostComponentClickedUIEvent event) { + if (DataRepository.isUserAnonymous() && PostComponentClickedUIEvent.Type.guestCantPerformActions.contains(event.type)) { + Logger.toastError(R.string.error_guest_action); + return; + } + + switch (event.type) { + case FaversList: + PageChanger.goToLikersList(getActivity(), mainPost.faveCount, mainPost.getId()); + break; + case Fave: + favePost(); + getRecyclerViewAdapter().notifyItemChanged(0); + break; + case Comments: + PageChanger.goToCommentList(getActivity(), mainPost.commentCount, mainPost.getId()); + getRecyclerViewAdapter().notifyItemChanged(0); + break; + case RepostersList: + PageChanger.goToRepostList(getActivity(), mainPost.repostCount, mainPost.getId()); + break; + case Repost: + DialogLauncher.launchRepostDialog(getFragmentManager(), mainPost); + getRecyclerViewAdapter().notifyItemChanged(0); + break; + case Poster: + Member member = mainPost.author; + PageChanger.goToProfile(getActivity(), member); + break; + case Collection: + Collection collection = mainPost.collection; + PageChanger.goToCollection(getActivity(), collection); + break; + case OriginalCollection: + collection = mainPost.originalCollection; + PageChanger.goToCollection(getActivity(), collection); + break; + case Reference: + PageChanger.goToInlineBrowser(getActivity(), mainPost.originalWebpage.toLowerCase(), mainPost.getId(), mainPost.name); + break; + case FullImage: + PageChanger.goToFullImage(getActivity(), mainPost.imagesUrls.properPostImage(ImageUrls.ImageSize.FULL_SIZE).url); + break; + } + } + + private void favePost() { + + if (!mainPost.favedByMe) { + PoinilaNetService.favePost(mainPost.getId()); + mainPost.faveCount++; + //setText(faveCount, ++post.faveCount); + //faveBtn.setSelected(true); + } else { + PoinilaNetService.unfavePost(mainPost.getId()); + mainPost.faveCount--; +// setText(faveCount, --post.faveCount); +// faveBtn.setSelected(false);*/ + } + + mainPost.favedByMe = !mainPost.favedByMe; + + faveBtn.setSelected(mainPost.favedByMe); + setText(faveCount, mainPost.faveCount); + +// mainPost.favedByMe ^= true; + + BusProvider.getSyncUIBus().post(new PostActionSyncEvent(mainPost)); + + } + + @Subscribe + public void onUndofaveEvent(UndoFavePostEvent event) { + mainPost.favedByMe = false; + mainPost.faveCount--; + faveBtn.setSelected(mainPost.favedByMe); + setText(faveCount, mainPost.faveCount); + // TODO: ??? + //faveBtn.setSelected(false); + + BusProvider.getSyncUIBus().post(new PostActionSyncEvent(mainPost)); + } + + @Subscribe + public void onUndoUnfaveEvent(UndoUnfavePostEvent event) { + mainPost.favedByMe = true; + mainPost.faveCount++; + faveBtn.setSelected(mainPost.favedByMe); + setText(faveCount, mainPost.faveCount); + // TODO: ??? + //faveBtn.setSelected(true); + + BusProvider.getSyncUIBus().post(new PostActionSyncEvent(mainPost)); + } + + @Subscribe + public void onExploreTag(ExploreTagEvent event) { + PageChanger.goToExplore(getActivity(), event.text); + } + + + @OnClick(R.id.zoom_btn) + public void zoomBtnOnCLick(){ + PageChanger.goToFullImage(getActivity(), mainPost.imagesUrls.x736.url); + } + + public static int findPostInAdapter(List adapter, int postID) { + for (int i = 0; i < adapter.size(); i++) { + if (adapter.get(i) instanceof Post && adapter.get(i).id == postID) // take account of ask rating item + return i; + } + return -1; + } + + public void populateIfNecessary(PostsReceivedEvent event) { + if (collection != null) { + for (Post post : event.posts) { + post.collection = collection; + //post.author = collection.owner; + } + } + } + + @Subscribe + public void onPostReceived(PostReceivedEvent event) { + onGettingInitDataResponse(event); + } + + @Override + public void onSuccessfulInitData(BaseEvent baseEvent) { + super.onSuccessfulInitData(baseEvent); + + if(baseEvent instanceof PostReceivedEvent){ + mainPost = ((PostReceivedEvent)baseEvent).post; + fill(mainPost); + getActivity().invalidateOptionsMenu(); + } + + } + + @Subscribe + public void onRemovePost(RemovePostUIEvent event) { + clickedItemPosition = event.adapterPosition; + new PoinilaAlertDialog.Builder().setMessage(R.string.confirm_delete_post). + setPositiveBtnText(R.string.yes).setNegativeBtnText(R.string.no). + build().show(getFragmentManager(), null); + } + + @Subscribe + public void onProfilePicClickedEvent(MemberClickedUIEvent event) { + Member member = ((Post)getRecyclerViewAdapter().getItem(event.adapterPosition)).author; + PageChanger.goToProfile(getActivity(), member); + } + + @Subscribe + public void onPostClicked(PostClickedUIEvent event) { + if(getRecyclerViewAdapter().getItem(event.adapterPosition) instanceof Post){ // sometimes getItem return Loading Model + Post post = (Post)getRecyclerViewAdapter().getItem(event.adapterPosition); + PageChanger.goToPost(getActivity(), post); + } + } + + @Subscribe + public void onCollectionClicked(CollectionClickedUIEvent event) { + Collection collection = ((Post)getRecyclerViewAdapter().getItem(event.adapterPosition)).collection; + PageChanger.goToCollection(getActivity(), collection); + } + + @Override + protected RecyclerView.OnScrollListener getRecyclerViewListener() { + return RecyclerViewProvider.staggeredListEndDetectorListener(getRecyclerViewAdapter(), this); + } + + @Override + public void requestForMoreData() { + int requestId = RandomUtils.getRandomInt(); + PoinilaNetService.getRelatedPosts(mainEntityId, bookmark, requestId); + } + + @Override + public RecyclerViewAdapter createAndReturnRVAdapter() { + return new PostAndRelatedPostAdapter(getActivity()); + } + + @Override + protected boolean sendsRequestAutomatically() { + return true; + } + + @Override + protected void requestInitialData() { + int requestId = RandomUtils.getRandomInt(); + activeRequests.add(requestId); + DataRepository.getInstance().getPost(mainEntityId, RequestSource.FORCE_ONLINE, requestId); + setLoading(new Loading()); + requestForMoreData(); + } + + @Override + public boolean mustShowProgressView() { + return false; + } + + @Override + public int getLayoutID() { + return R.layout.post_related_posts; + } + + @Override + public void onSuccessfulListData(BaseEvent baseEvent, String newBookmark) { + super.onSuccessfulListData(baseEvent, newBookmark); + List posts = ((PostsReceivedEvent) baseEvent).posts; + + getRecyclerViewAdapter().addItems(posts); + + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + + super.onConfigurationChanged(newConfig); + int currentOrientation = getResources().getConfiguration().orientation; + + if (isLandScape()) + setFullScreenStateVideo(); + else + setDefaultStateVideo(); + +// getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); + + } + + private boolean isLandScape(){ + return getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; + } + + @Override + public void stateChanged() { + if (isLandScape()) + getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + else + getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + } + + private void setDefaultStateVideo(){ + videoView.setFullScreenMode(false); + int postCountChild = mainPostContainer.getChildCount(); + ((AppCompatActivity)getActivity()).getSupportActionBar().show(); + + mRecyclerView.setVisibility(View.VISIBLE); + + computeVideoSize(); + + for(int i=0 ; i < postCountChild ; i++){ + if(mainPostContainer.getChildAt(i).getId() != R.id.post_video_view){ + mainPostContainer.getChildAt(i).setVisibility(View.VISIBLE); + } + } + + fill(mainPost); + } + + private void setFullScreenStateVideo(){ + videoView.setFullScreenMode(true); + int postCountChild = mainPostContainer.getChildCount(); + ((AppCompatActivity)getActivity()).getSupportActionBar().hide(); + + mRecyclerView.setVisibility(View.GONE); + + videoView.getLayoutParams().width = getActivity().getResources().getDisplayMetrics().widthPixels; + + TypedValue tv = new TypedValue(); + getActivity().getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true); + int actionBarHeight = getResources().getDimensionPixelSize(tv.resourceId); + videoView.getLayoutParams().height = + getActivity().getResources().getDisplayMetrics().heightPixels + + getStatusBarHeight(getActivity().getWindow()) + - actionBarHeight; + + for(int i=0 ; i < postCountChild ; i++){ + if(mainPostContainer.getChildAt(i).getId() != R.id.post_video_view){ + mainPostContainer.getChildAt(i).setVisibility(View.GONE); + } + } + } + + private int getStatusBarHeight(Window window){ + Rect rectangle = new Rect(); + window.getDecorView().getWindowVisibleDisplayFrame(rectangle); + return rectangle.top; + } + + + private class PostAndRelatedPostAdapter extends RecyclerViewAdapter> { + public static final int VIEW_TYPE_POST_FULL = 1; + public static final int VIEW_TYPE_POST_ITEM = 2; + + public PostAndRelatedPostAdapter(Context context) { + super(context, -1); + } + + @Override + protected PostDetailViewHolder getProperViewHolder(View v, int viewType) { + return null; + } + + @Override + public void onBindViewHolder(BaseViewHolder holder, int position) { + if (getItemViewType(position) == VIEW_TYPE_POST_FULL) { + StaggeredGridLayoutManager.LayoutParams layoutParams = (StaggeredGridLayoutManager.LayoutParams) holder.itemView.getLayoutParams(); + layoutParams.setFullSpan(true); + } + super.onBindViewHolder(holder, position); + } + + @Override + public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + if(viewType == VIEW_TYPE_LOAD_PROGRESS){ + return new BaseViewHolder.EmptyViewHolder(mLayoutInflater.inflate(R.layout.progress, parent, false)); + } else + return new DashboardPostViewHolder(mLayoutInflater.inflate(R.layout.post_dashboard, parent, false), BaseEvent.ReceiverName.PostRelatedPosts); + + } + + @Override + protected boolean isStaggeredGridLayoutManager() { + return true; + } + + @Override + public int getItemViewType(int position) { + int type = super.getItemViewType(position); + + if(type == VIEW_TYPE_LOAD_PROGRESS){ + return type; + } + + return VIEW_TYPE_POST_ITEM; + } + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/PostListFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/PostListFragment.java new file mode 100755 index 0000000..8084bcc --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/PostListFragment.java @@ -0,0 +1,977 @@ +package com.shaya.poinila.android.presentation.view.fragments; + +import android.content.Context; +import android.content.Intent; +import android.content.res.Configuration; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.support.annotation.Nullable; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.StaggeredGridLayoutManager; +import android.text.TextUtils; +import android.util.Log; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.TextView; + +import com.getbase.floatingactionbutton.FloatingActionButton; +import com.getbase.floatingactionbutton.FloatingActionsMenu; +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewProvider; +import com.shaya.poinila.android.presentation.uievent.CollectionClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.ExploreTagEvent; +import com.shaya.poinila.android.presentation.uievent.MemberClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.NewWebsitePostEvent; +import com.shaya.poinila.android.presentation.uievent.PositiveButtonClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.PostClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.PostComponentClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.RemovePostUIEvent; +import com.shaya.poinila.android.presentation.uievent.UpdateUICommentEvent; +import com.shaya.poinila.android.presentation.uievent.UpdateUiRepostEvent; +import com.shaya.poinila.android.presentation.uievent.sync.PostActionSyncEvent; +import com.shaya.poinila.android.presentation.view.dialog.DialogLauncher; +import com.shaya.poinila.android.presentation.view.dialog.PoinilaAlertDialog; +import com.shaya.poinila.android.presentation.view.help.Help; +import com.shaya.poinila.android.presentation.viewholder.BaseViewHolder; +import com.shaya.poinila.android.presentation.viewholder.DashboardPostViewHolder; +import com.shaya.poinila.android.presentation.viewholder.PostDetailViewHolder; +import com.shaya.poinila.android.presentation.viewholder.PostsOfCollectionViewHolder; +import com.shaya.poinila.android.presentation.viewholder.RemovablePostViewHolder; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.util.PoinilaPreferences; +import com.shaya.poinila.android.util.RandomUtils; +import com.shaya.poinila.android.util.StringUtils; +import com.squareup.otto.Subscribe; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import butterknife.Bind; +import butterknife.OnClick; +import data.PoinilaNetService; +import data.event.BaseEvent; +import data.event.CollectionReceivedEvent; +import data.event.ContentReceivedEvent; +import data.event.PostReceivedEvent; +import data.event.PostsReceivedEvent; +import data.event.UndoFavePostEvent; +import data.event.UndoUnfavePostEvent; +import data.model.Collection; +import data.model.ImageUrls; +import data.model.Loading; +import data.model.Member; +import data.model.Post; +import manager.DataRepository; +import manager.RequestSource; + +import static android.support.v7.widget.LinearLayoutManager.VERTICAL; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setFont; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_ENTITY; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_REQUEST_ID; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_SECOND_ENTITY_ID; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_COLLECTION_POSTS; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_EXPLORE; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_MEMBER_FAVED_POSTS; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_MEMBER_POSTS; +import static com.shaya.poinila.android.util.ConstantsUtils.REQUEST_POST_RELATED_POSTS; +import static com.shaya.poinila.android.util.ResourceUtils.getStringFormatted; + +/** + * Created by iran on 2015-08-09. + */ +public class PostListFragment extends ListBusFragment { + + protected Collection collection; + private int requestType; + private String mainEntityId; + private String secondEntityId; + + private boolean showedHelp = false; + + // must be moved to bus fragment + private Set activeRequests; + + @Nullable + @Bind(R.id.follow_button) + Button followButton; + @Nullable + @Bind(R.id.follow_button_img) + ImageView followButtonImg; + @Nullable + @Bind(R.id.edit_button) + Button editButton; + @Nullable + @Bind(R.id.remove_button) + Button removeButton; + + @Nullable + @Bind(R.id.collection_description) + TextView collectionDescription; + +// @Nullable +// @Bind(R.id.collection_info_bar) +// View collectionInfoView; + + @Nullable + @Bind(R.id.item_count) + TextView itemCountView; + + + FloatingActionsMenu fabMenu; + FloatingActionButton addFromUrl; + FloatingActionButton addPost; + + +// @Nullable +// @Bind(R.id.collection_description_container) +// ViewGroup descriptionContainer; + + @Nullable + @Bind(R.id.explored_tag) + TextView exploredTagView; + + Post mainPost; + + @Override + protected RecyclerView.OnScrollListener getRecyclerViewListener() { + return RecyclerViewProvider.staggeredListEndDetectorListener(getRecyclerViewAdapter(), this); + } + + private void fillCollection(Collection collection) { + setText(itemCountView, getStringFormatted(R.string.posts_formatted, collection.postCount)); + + if (!TextUtils.isEmpty(collection.description)) { + setText(collectionDescription, collection.description); + setFont(collectionDescription, getString(R.string.default_bold_font_path)); + collectionDescription.setVisibility(View.VISIBLE); + }else { + collectionDescription.setVisibility(View.GONE); + } + + if (!DataRepository.isMyCollection(collection)) { + followButton.setVisibility(View.VISIBLE); + followButtonImg.setVisibility(View.VISIBLE); + updateFollowButton(); + } else { + editButton.setVisibility(View.VISIBLE); + removeButton.setVisibility(View.VISIBLE); + } + //getActivity().setTitle(getString(R.string.title_activity_collection, mainEntityId)); + } + + public static PostListFragment newInstance(String mainEntityId, int requestID) { + return newInstance(mainEntityId, null, requestID); + } + + public static PostListFragment newInstance(String mainEntityId, String secondEntityId, int requestID) { + PostListFragment f = new PostListFragment(); + Bundle arguments = new Bundle(); + arguments.putString(KEY_ENTITY, mainEntityId); + arguments.putString(KEY_SECOND_ENTITY_ID, secondEntityId); + arguments.putInt(KEY_REQUEST_ID, requestID); + //arguments.putBoolean(ConstantsUtils.KEY_IS_USER_COLLECTION, isCollectionOwnedByUser); + f.setArguments(arguments); + return f; + } + + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mainEntityId = getArguments().getString(KEY_ENTITY); + requestType = getArguments().getInt(KEY_REQUEST_ID); + secondEntityId = getArguments().getString(KEY_SECOND_ENTITY_ID); + + activeRequests = new HashSet<>(2); + + setHasOptionsMenu(true); + + // when opening new post, in some situations we already have post data. We show that to + // user avoiding loading icons and at the same time request for post with updated data. + /*if (requestID == ConstantsUtils.REQUEST_POST_RELATED_POSTS && + DataRepository.getInstance().getTempModel(Post.class) != null){ + + }*/ + } + + @Override + public int getLayoutID() { + switch (requestType) { + case REQUEST_COLLECTION_POSTS: + return R.layout.fragment_collection_detail_owned; + case REQUEST_EXPLORE: + return R.layout.fragment_explore; + case REQUEST_MEMBER_POSTS: + return R.layout.fragment_member_posts; + default: + return R.layout.recycler_view_full; + } + } + + @Override + protected void initUI() { + super.initUI(); + mRecyclerView = new RecyclerViewProvider(mRecyclerView).setAdapter(getRecyclerViewAdapter()). + setStaggeredLayoutManager(VERTICAL, getResources().getInteger(R.integer.column_count)). + bindViewToAdapter(); + mRecyclerView.getItemAnimator().setChangeDuration(0); + + + switch (requestType) { + case REQUEST_MEMBER_POSTS: + getActivity().setTitle(getString(R.string.title_activity_member_posts, secondEntityId)); + fabMenu = (FloatingActionsMenu)rootView.findViewById(R.id.fab_menu); + addFromUrl = (FloatingActionButton)rootView.findViewById(R.id.fab_add_post_from_site); + addPost = (FloatingActionButton)rootView.findViewById(R.id.fab_add_post); + + addPost.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + fabMenu.collapse(); + //DialogLauncher.launchNewPost(getChildFragmentManager(), null); + PageChanger.goToNewPost(getFragmentManager(), null); + } + }); + + addFromUrl.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + fabMenu.collapse(); + DialogLauncher.launchNewWebsitePost(getFragmentManager()); + } + }); + + break; + case REQUEST_MEMBER_FAVED_POSTS: + getActivity().setTitle(R.string.title_activity_member_faved_posts); + break; + case ConstantsUtils.REQUEST_POST_RELATED_POSTS: + getActivity().setTitle(getString(R.string.post)); + // adding main post as first adapter item + mainPost = DataRepository.getInstance().getTempModel(Post.class); + if(mainPost != null){ + getRecyclerViewAdapter().addItem(mainPost); + getRecyclerViewAdapter().notifyDataSetChanged(); + } + + mRecyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + + View view = ((ViewGroup) mRecyclerView.getLayoutManager().findViewByPosition(0)).getChildAt(1); + if(!PoinilaPreferences.getHelpStatus(PostListFragment.this.getClass().getName() + ".PostPage")){ + Help.getInstance().showPostRelatedPostsHelp(getActivity(), view); + PoinilaPreferences.putHelpStatus(PostListFragment.this.getClass().getName()+ ".PostPage", true); + + } + + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) { + mRecyclerView.getViewTreeObserver().removeOnGlobalLayoutListener(this); + } else { + mRecyclerView.getViewTreeObserver().removeGlobalOnLayoutListener(this); + } + + } + }); + + break; + case ConstantsUtils.REQUEST_COLLECTION_POSTS: + fabMenu = (FloatingActionsMenu)rootView.findViewById(R.id.fab_menu); + addFromUrl = (FloatingActionButton)rootView.findViewById(R.id.fab_add_post_from_site); + addPost = (FloatingActionButton)rootView.findViewById(R.id.fab_add_post); + + addPost.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + fabMenu.collapse(); + //DialogLauncher.launchNewPost(getChildFragmentManager(), null); + PageChanger.goToNewPost(getFragmentManager(), null); + } + }); + + addFromUrl.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + fabMenu.collapse(); + DialogLauncher.launchNewWebsitePost(getFragmentManager()); + } + }); + + getActivity().setTitle(byDeepLink(mainEntityId) ? mainEntityId : secondEntityId); + collection = DataRepository.getInstance().getTempModel(Collection.class); + if (collection == null) + return; + fillCollection(collection); + setLoading(new Loading()); + + break; + case REQUEST_EXPLORE: + getActivity().setTitle(R.string.title_activity_explore); + exploredTagView.setText(mainEntityId); + break; + } + } + + @Subscribe + public void onNewUrlImagePostEvent(NewWebsitePostEvent event){ + //DialogLauncher.launchNewPost(getChildFragmentManager(), event.suggestedPost); + PageChanger.goToNewPost(getFragmentManager(), event.suggestedPost); + } + + private void updateFollowButton() { + setText(followButton, collection.followedByMe + ? getString(R.string.unfollow_item) + : getString(R.string.follow_item)); + + followButtonImg.setSelected(collection.followedByMe); + } + + @Subscribe + public void onPostsReceived(PostsReceivedEvent event) { + + if(event.posts.size() >= 25 && mRecyclerView.getAdapter().getItemCount() == 0){ + setLoading(new Loading()); + } + + populateIfNecessary(event); // was necessary earlier because response came from server lacked info about posts collection + onGettingInitDataResponse(event); + onGettingListDataResponse(event, event.bookmark); + + } + + @Subscribe + public void onPostReceived(PostReceivedEvent event) { + onGettingInitDataResponse(event); + } + + @Subscribe + public void onRemovePost(RemovePostUIEvent event) { + clickedItemPosition = event.adapterPosition; + new PoinilaAlertDialog.Builder().setMessage(R.string.confirm_delete_post). + setPositiveBtnText(R.string.yes).setNegativeBtnText(R.string.no). + build().show(getFragmentManager(), null); + } + + @Subscribe + public void onProfilePicClickedEvent(MemberClickedUIEvent event) { + Member member = ((Post)getRecyclerViewAdapter().getItem(event.adapterPosition)).author; + PageChanger.goToProfile(getActivity(), member); + } + + @Subscribe + public void onPostClicked(PostClickedUIEvent event) { + Post post = (Post)getRecyclerViewAdapter().getItem(event.adapterPosition); + PageChanger.goToPost(getActivity(), post); + } + + @Subscribe + public void onCollectionClicked(CollectionClickedUIEvent event) { + Collection collection = ((Post)getRecyclerViewAdapter().getItem(event.adapterPosition)).collection; + PageChanger.goToCollection(getActivity(), collection); + } + + @Nullable + @OnClick(R.id.follow_button) + public void onFollowCollection(Button followButton) { + if (DataRepository.isUserAnonymous()) { + Logger.toastError(R.string.error_guest_action); + return; + } + + if (collection.followedByMe) { + PoinilaNetService.unfollowCollection(collection.getId()); + collection.followedByMe = false; + } else { + PoinilaNetService.followCollection(collection.getId()); + collection.followedByMe = true; + } + updateFollowButton(); + } + + /* @Nullable @OnClick(R.id.edit_button) public void onEditCollection(){ + DataRepository.getInstance().putTempModel(collection); + new PoinilaDialog.Builder().setTitle(R.string.edit_collection). + setPositiveText(R.string.submit). + setNegativeText(R.string.cancel). + setBody(new EditCollectionDialog(collection)). + build().show(getChildFragmentManager(), null); + }*/ + + @Nullable + @OnClick(R.id.remove_button) + public void onRemoveCollection() { + // must not happen but anyway... :) + if (DataRepository.isUserAnonymous()) { + Logger.toastError(R.string.error_guest_action); + return; + } + + DialogLauncher.launchDeleteCollection(getFragmentManager()); + } + + @Nullable + @OnClick(R.id.edit_button) + public void onEditCollection() { + // must not happen but anyway... :) + if (DataRepository.isUserAnonymous()) { + Logger.toastError(R.string.error_guest_action); + return; + } + + DialogLauncher.launchEditCollectionDialog(getFragmentManager(), collection); + } + + @Subscribe + public void onPositiveDialogButtonClicked(PositiveButtonClickedUIEvent event) { + switch (requestType) { + case REQUEST_MEMBER_POSTS: + PoinilaNetService.deletePost(((Post)getRecyclerViewAdapter().getItem(clickedItemPosition)).getId()); + getRecyclerViewAdapter().removeItem(clickedItemPosition); + //setText(itemCountView, --collection.postCount); + break; + case REQUEST_COLLECTION_POSTS: + PoinilaNetService.deleteCollection(collection); + getActivity().finish(); + break; + } + } + + public void populateIfNecessary(PostsReceivedEvent event) { + if (collection != null) { + for (Post post : event.posts) { + post.collection = collection; + //post.author = collection.owner; + } + } + } + + // response for getting collection info OR editing it. + @Subscribe + public void onCollectionInfoReceived(CollectionReceivedEvent event) { + onGettingInitDataResponse(event); + } + + @Subscribe + public void onContentReceivedEvent(final ContentReceivedEvent event) { + final int postIndex = findPostInAdapter(getRecyclerViewAdapter().getItems(), event.postID); + if (postIndex != -1) { + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + ((Post)getRecyclerViewAdapter().getItem(postIndex)).content = StringUtils.removeHtmlDirAttribute(event.content); + getRecyclerViewAdapter().notifyItemChanged(postIndex); + } + }); + } + } + + public static int findPostInAdapter(List adapter, int postID) { + for (int i = 0; i < adapter.size(); i++) { + if (adapter.get(i) instanceof Post && adapter.get(i).id == postID) // take account of ask rating item + return i; + } + return -1; + } + + /*---------------*/ + + @Override + protected boolean sendsRequestAutomatically() { + return true; + } + + @Override + protected void requestInitialData() { + // TODO: is necessary to invoke listData requests or it gets called by listener? + if (requestType == REQUEST_COLLECTION_POSTS) { + // deep links are unique with two parameters -> user name, collection name + Log.i(getClass().getName(), "collection unige name : " + (byDeepLink(mainEntityId) ? secondEntityId : null)); + DataRepository.getInstance().getCollection( + mainEntityId, + byDeepLink(mainEntityId) ? secondEntityId : null, + RequestSource.FORCE_ONLINE); + } else if (requestType == REQUEST_POST_RELATED_POSTS) { + int requestId = RandomUtils.getRandomInt(); + activeRequests.add(requestId); + DataRepository.getInstance().getPost(mainEntityId, RequestSource.FORCE_ONLINE, requestId); + setLoading(new Loading()); + } + requestForMoreData(); + } + + @Override + public boolean mustShowProgressView() { + // always show except on related post when main post is present + return !(requestType == REQUEST_POST_RELATED_POSTS && !getRecyclerViewAdapter().isEmpty() && getRecyclerViewAdapter().getItem(0) != null); + } + + @Override + public void requestForMoreData() { + int requestId = RandomUtils.getRandomInt(); + activeRequests.add(requestId); + switch (requestType) { + case REQUEST_MEMBER_FAVED_POSTS: + PoinilaNetService.getFavedPostByMember(mainEntityId, bookmark); + break; + case REQUEST_MEMBER_POSTS: + PoinilaNetService.getMemberPosts(mainEntityId, bookmark); + break; + case REQUEST_COLLECTION_POSTS: + DataRepository.getCollectionPosts( + mainEntityId, + byDeepLink(mainEntityId) ? secondEntityId : null, + bookmark, + BaseEvent.ReceiverName.PostListFragment); + break; + case REQUEST_POST_RELATED_POSTS: + // experimental + PoinilaNetService.getRelatedPosts(mainEntityId, bookmark, requestId); + break; + case REQUEST_EXPLORE: + PoinilaNetService.explore(mainEntityId, bookmark); + break; + } + } + + @Override + public RecyclerViewAdapter createAndReturnRVAdapter() { + final BaseEvent.ReceiverName receiverName = (requestType == REQUEST_POST_RELATED_POSTS) + ? BaseEvent.ReceiverName.PostRelatedPosts : BaseEvent.ReceiverName.PostListFragment; + switch (requestType) { + case REQUEST_POST_RELATED_POSTS: + return new PostAndRelatedPostAdapter(getActivity()); + case REQUEST_EXPLORE: + case REQUEST_MEMBER_FAVED_POSTS: + return new RecyclerViewAdapter(getActivity(), R.layout.post_dashboard) { + @Override + protected BaseViewHolder getProperViewHolder(View v, int viewType) { + + if(viewType == VIEW_TYPE_LOAD_PROGRESS){ + return new BaseViewHolder.EmptyViewHolder(v); + } + + return new DashboardPostViewHolder(v, receiverName); + } + + @Override + public void onBindViewHolder(BaseViewHolder holder, int position) { + if (getItemViewType(position) == VIEW_TYPE_LOAD_PROGRESS) { + StaggeredGridLayoutManager.LayoutParams layoutParams = (StaggeredGridLayoutManager.LayoutParams) holder.itemView.getLayoutParams(); + layoutParams.setFullSpan(true); + } else { + super.onBindViewHolder(holder, position); + } + } + }; + case REQUEST_MEMBER_POSTS: + if (mainEntityId.equals(DataRepository.getInstance().getMyId())) { // user posts + return new RecyclerViewAdapter(getActivity(), R.layout.post_item_removable) { + @Override + protected BaseViewHolder getProperViewHolder(View v, int viewType) { + + if(viewType == VIEW_TYPE_LOAD_PROGRESS){ + return new BaseViewHolder.EmptyViewHolder(v); + } + + return new RemovablePostViewHolder(v, receiverName); + } + + @Override + protected boolean isStaggeredGridLayoutManager() { + return true; + } + }; + } else { + return new RecyclerViewAdapter(getActivity(), R.layout.post_dashboard) { + @Override + protected BaseViewHolder getProperViewHolder(View v, int viewType) { + + if(viewType == VIEW_TYPE_LOAD_PROGRESS){ + return new BaseViewHolder.EmptyViewHolder(v); + } + + return new DashboardPostViewHolder(v, receiverName); + } + + @Override + protected boolean isStaggeredGridLayoutManager() { + return true; + } + }; + } + case ConstantsUtils.REQUEST_COLLECTION_POSTS: + return new RecyclerViewAdapter(getActivity(), R.layout.post_in_collection) { + @Override + protected BaseViewHolder getProperViewHolder(View v, int viewType) { + if(viewType == VIEW_TYPE_LOAD_PROGRESS){ + return new BaseViewHolder.EmptyViewHolder(v); + } + return new PostsOfCollectionViewHolder(v, receiverName); + } + + @Override + protected boolean isStaggeredGridLayoutManager() { + return true; + } + }; + } + return null; + } + + @Override + public void onSuccessfulListData(BaseEvent baseEvent, String newBookmark) { + super.onSuccessfulListData(baseEvent, newBookmark); + List posts = ((PostsReceivedEvent) baseEvent).posts; + + getRecyclerViewAdapter().addItems(posts); + + if(!showedHelp && !PoinilaPreferences.getHelpStatus(getClass().getName()+ ".CollectionPage") && requestType == REQUEST_COLLECTION_POSTS){ + showHelp(); + showedHelp = true; + PoinilaPreferences.putHelpStatus(getClass().getName() + ".CollectionPage", true); + } + + } + + @Override + public void onSuccessfulInitData(BaseEvent baseEvent) { + super.onSuccessfulInitData(baseEvent); + + if (baseEvent instanceof CollectionReceivedEvent) { + collection = ((CollectionReceivedEvent) baseEvent).collection; + fillCollection(collection); + } else if (baseEvent instanceof PostReceivedEvent) { + if (getRecyclerViewAdapter().isEmpty()) { // click on notification participant + getRecyclerViewAdapter().addItem(((PostReceivedEvent) baseEvent).post, 0); + } else { + //TODO: replace nemikone ba avvalin item related post ha? + getRecyclerViewAdapter().setItem(((PostReceivedEvent) baseEvent).post, 0); + } + } + /*collection.description = event.collection.description; + collection.circleIDs = event.collection.circleIDs; + collection.name = event.collection.name; + collection.topic = event.collection.topic; + collection.coverImageUrls = event.collection.coverImageUrls;*/ + + } + + @Override + protected boolean isInitDataResponseValid(BaseEvent baseEvent) { + boolean res = true; + if (baseEvent instanceof CollectionReceivedEvent) { + res = requestType == REQUEST_COLLECTION_POSTS; + } else if (baseEvent instanceof PostReceivedEvent) { + res = (requestType == REQUEST_POST_RELATED_POSTS && + activeRequests.contains(((PostReceivedEvent) baseEvent).requestId)); + } + return res && super.isInitDataResponseValid(baseEvent); + } + + @Override + protected boolean isListDataResponseValid(BaseEvent baseEvent, String responseBookmark) { + boolean res; + PostsReceivedEvent event = (PostsReceivedEvent) baseEvent; + if (requestType == REQUEST_POST_RELATED_POSTS) + res = (event.receiverName == BaseEvent.ReceiverName.PostRelatedPosts + && activeRequests.contains(event.requestId)); + else if (requestType == REQUEST_EXPLORE) + res = event.receiverName == BaseEvent.ReceiverName.ExploredTagPosts; + else + res = event.receiverName == BaseEvent.ReceiverName.PostListFragment; + /*switch (requestID) { + case ((PostsReceivedEvent) baseEvent).receiverName + }*/ + return res && super.isListDataResponseValid(baseEvent, responseBookmark); + + } + + /*----Related post stuff------*/ + // all event belong to first post in adapter which we show in full detail + + + @Subscribe + public void onPostDetailsComponentClickEvent(PostComponentClickedUIEvent event) { + if (DataRepository.isUserAnonymous() && PostComponentClickedUIEvent.Type.guestCantPerformActions.contains(event.type)) { + Logger.toastError(R.string.error_guest_action); + return; + } + + + Post post = (Post)getRecyclerViewAdapter().getItem(0); + switch (event.type) { + case FaversList: + PageChanger.goToLikersList(getActivity(), post.faveCount, post.getId()); + break; + case Fave: + favePost(post); + getRecyclerViewAdapter().notifyItemChanged(0); + break; + case Comments: + PageChanger.goToCommentList(getActivity(), post.commentCount, post.getId()); + getRecyclerViewAdapter().notifyItemChanged(0); + break; + case RepostersList: + PageChanger.goToRepostList(getActivity(), post.repostCount, post.getId()); + break; + case Repost: + DialogLauncher.launchRepostDialog(getFragmentManager(), post); + getRecyclerViewAdapter().notifyItemChanged(0); + break; + case Poster: + Member member = post.author; + PageChanger.goToProfile(getActivity(), member); + break; + case Collection: + Collection collection = post.collection; + PageChanger.goToCollection(getActivity(), collection); + break; + case OriginalCollection: + collection = post.originalCollection; + PageChanger.goToCollection(getActivity(), collection); + break; + case Reference: + PageChanger.goToInlineBrowser(getActivity(), post.originalWebpage.toLowerCase(), post.getId(), post.name); + break; + case FullImage: + PageChanger.goToFullImage(getActivity(), post.imagesUrls.properPostImage(ImageUrls.ImageSize.FULL_SIZE).url); + break; + } + } + + private void favePost(Post post) { + if (!post.favedByMe) { + PoinilaNetService.favePost(post.getId()); + post.faveCount++; + //post.favedByMe = true; + //setText(faveCount, ++post.faveCount); + //faveBtn.setSelected(true); + } else { + PoinilaNetService.unfavePost(post.getId()); + post.faveCount--; + /*post.favedByMe = false; + setText(faveCount, --post.faveCount); + faveBtn.setSelected(false);*/ + } + + post.favedByMe ^= true; + + BusProvider.getSyncUIBus().post(new PostActionSyncEvent(post)); + + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + + super.onConfigurationChanged(newConfig); + int currentOrientation = getResources().getConfiguration().orientation; + + + + } + + @Subscribe + public void onUndofaveEvent(UndoFavePostEvent event) { + ((Post)getRecyclerViewAdapter().getItem(0)).favedByMe = false; + getRecyclerViewAdapter().notifyItemChanged(0); + // TODO: ??? + //faveBtn.setSelected(false); + } + + @Subscribe + public void onUndoUnfaveEvent(UndoUnfavePostEvent event) { + ((Post)getRecyclerViewAdapter().getItem(0)).favedByMe = true; + getRecyclerViewAdapter().notifyItemChanged(0); + // TODO: ??? + //faveBtn.setSelected(true); + } + + @Subscribe + public void onUpdateUiRepostEvent(UpdateUiRepostEvent event){ + +// Logger.log(getClass().getName() + " isSuccess : " + event.isSuccess, Logger.LEVEL_INFO); +// +// if(event.isSuccess) +// ((Post)getRecyclerViewAdapter().getItem(0)).repostCount++; +// else +// ((Post)getRecyclerViewAdapter().getItem(0)).repostCount--; +// +// getRecyclerViewAdapter().notifyItemChanged(0); + } + + @Subscribe + public void onUpdateUICommentEvent(UpdateUICommentEvent event){ + + if(getRecyclerViewAdapter().isEmpty())return; + + if(event.action == UpdateUICommentEvent.INCREMENT_COMMENTS) + ((Post)getRecyclerViewAdapter().getItem(0)).commentCount++; + else + ((Post)getRecyclerViewAdapter().getItem(0)).commentCount--; + + getRecyclerViewAdapter().notifyItemChanged(0); + } + + private class PostAndRelatedPostAdapter extends RecyclerViewAdapter> { + public static final int VIEW_TYPE_POST_FULL = 1; + public static final int VIEW_TYPE_POST_ITEM = 2; + + public PostAndRelatedPostAdapter(Context context) { + super(context, -1); + } + + @Override + protected PostDetailViewHolder getProperViewHolder(View v, int viewType) { + return null; + } + + @Override + public void onBindViewHolder(BaseViewHolder holder, int position) { + if (getItemViewType(position) == VIEW_TYPE_POST_FULL) { + StaggeredGridLayoutManager.LayoutParams layoutParams = (StaggeredGridLayoutManager.LayoutParams) holder.itemView.getLayoutParams(); + layoutParams.setFullSpan(true); + } + super.onBindViewHolder(holder, position); + } + + @Override + public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + if(viewType == VIEW_TYPE_LOAD_PROGRESS){ + return new BaseViewHolder.EmptyViewHolder(mLayoutInflater.inflate(R.layout.progress, parent, false)); + }else if (viewType == VIEW_TYPE_POST_FULL) + return new PostDetailViewHolder(mLayoutInflater.inflate(R.layout.post_full_detail, parent, false)); + else + return new DashboardPostViewHolder(mLayoutInflater.inflate(R.layout.post_dashboard, parent, false), BaseEvent.ReceiverName.PostRelatedPosts); + + } + + @Override + protected boolean isStaggeredGridLayoutManager() { + return true; + } + + @Override + public int getItemViewType(int position) { + int type = super.getItemViewType(position); + + if(type == VIEW_TYPE_LOAD_PROGRESS){ + return type; + } + + return (position == 0) ? VIEW_TYPE_POST_FULL : VIEW_TYPE_POST_ITEM; + } + } + + private boolean byDeepLink(String collectionIdOrName) { + return !StringUtils.isInteger(collectionIdOrName); + } + + /*share collection and post*/ + //private ShareActionProvider mShareActionProvider; + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + menu.clear(); + if (Arrays.asList(REQUEST_COLLECTION_POSTS, REQUEST_POST_RELATED_POSTS).contains(requestType)){ + + // Inflate menu resource file. + inflater.inflate(R.menu.menu_post, menu); + + // Hidden Report Item + if(requestType != ConstantsUtils.REQUEST_POST_RELATED_POSTS) + menu.findItem(R.id.menu_item_report).setVisible(false); + + /*// Locate MenuItem with ShareActionProvider + MenuItem item = menu.findItem(R.id.menu_item_share); + // Fetch reference to the share action provider + mShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(item);*/ + + } else + super.onCreateOptionsMenu(menu, inflater); + } + + @Override + public void onStart() { + super.onStart(); + + BusProvider.getSyncUIBus().register(this); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + + // handle item selection + switch (item.getItemId()) { + case R.id.menu_item_share: + // Handle this selection + + launchShareMenu(mainPost); + return true; + case R.id.menu_item_report: + // Handle this selection + if(getRecyclerViewAdapter().getItemCount() >= 1) + DialogLauncher.launchReportDialog( + getFragmentManager(), + R.string.report_post, + mainPost.id); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + private void launchShareMenu(Post post) { + Intent shareIntent = new Intent(Intent.ACTION_SEND); + shareIntent.setType("text/plain"); + String extra = null; + switch (requestType) { + case REQUEST_COLLECTION_POSTS: + + extra = getString(R.string.checkout_this_collection) + "\n" + + getString(R.string.collection_share_url, + ConstantsUtils.POINILA_ORIGIN_ADDRESS, + Uri.encode(collection.owner.uniqueName), + Uri.encode(collection.name)) + "\n" + + getString(R.string.ponila_world_of_interest); + + break; + case REQUEST_POST_RELATED_POSTS: + extra = post.name + "\n\n" + + post.summary + "\n" + + getString(R.string.checkout_this_post) + "\n" + + getString(R.string.post_share_url, ConstantsUtils.POINILA_ORIGIN_ADDRESS, + ((Post)getRecyclerViewAdapter().getItem(0)).getId()) + "\n" + + getString(R.string.ponila_world_of_interest); + break; + default: + return; + } + shareIntent.putExtra(Intent.EXTRA_TEXT, extra); + startActivity(Intent.createChooser(shareIntent, getString(R.string.share_dialog_title))); + } + + + @Subscribe + public void onExploreTag(ExploreTagEvent event) { + PageChanger.goToExplore(getActivity(), event.text); + } + + public void showHelp(){ + Help.getInstance().showPostsOfCollectionHelp(getActivity(), followButton); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/RegisterFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/RegisterFragment.java new file mode 100755 index 0000000..8613734 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/RegisterFragment.java @@ -0,0 +1,430 @@ +package com.shaya.poinila.android.presentation.view.fragments; + + +import android.Manifest; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v13.app.FragmentCompat; +import android.text.Editable; +import android.text.Html; +import android.text.InputType; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.text.method.LinkMovementMethod; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.RadioButton; +import android.widget.RadioGroup; +import android.widget.TextView; + +import com.mobsandgeeks.saripaar.ValidationError; +import com.mobsandgeeks.saripaar.Validator; +import com.mobsandgeeks.saripaar.annotation.Checked; +import com.mobsandgeeks.saripaar.annotation.Length;; +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.SmsReceiver; +import com.shaya.poinila.android.presentation.uievent.SmsReceivedEvent; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.presentation.view.activity.BaseActivity; +import com.shaya.poinila.android.presentation.view.costom_view.ActivityResultPermissionDelegate.SimpleActivityResultPermissionDelegate; +import com.shaya.poinila.android.util.ConnectionUitls; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.utils.PonilaAccountManager; +import com.squareup.otto.Subscribe; + +import java.util.List; + +import butterknife.Bind; +import butterknife.OnClick; +import data.PoinilaNetService; +import data.event.MyInfoReceivedEvent; +import data.event.RegisterResponseEvent; +import data.event.UserNameValidityEvent; +import data.model.Gender; +import manager.DataRepository; +import uk.co.chrisjenx.calligraphy.CalligraphyConfig; +import uk.co.chrisjenx.calligraphy.CalligraphyUtils; + +public class RegisterFragment extends BusFragment implements Validator.ValidationListener, FragmentCompat.OnRequestPermissionsResultCallback { + private static final String KEY_VERIFICATION_CODE = "verification code"; + + @Bind(R.id.card_title) + TextView title; + //actually doesn't exist in graphic design. I chose a similar layout + //for reusability purposes. + @Bind(R.id.left_arrow) + ImageView arrow; + + @Bind(R.id.verification_code) + EditText verificationCode; + + @Bind(R.id.fullname_input) + EditText fullName; + + /*@Length(min = 6, max = 32, messageResId = R.string.error_username_length) + @Pattern(regex = "[a-zA-Z0-9_\\.\\u0600-\\u06FF\\uFB8A\\u067E\\u0686\\u06AF]+", messageResId = R.string.error_username_rule)*/ + @Bind(R.id.username_input) + EditText userName; + + /*@Order(value = 1) + @Email + @Bind(R.id.email_input) + EditText email;*/ + + @Length(max = 40, min = 6, messageResId = R.string.error_password_length) + @Bind(R.id.password_input) + EditText password; + + /*@Order(value = 2) + @ConfirmPassword + @Bind(R.id.confirm_password_input) + EditText confirm_password;*/ + + @Bind(R.id.gender_container) + RadioGroup genderRadioGroup; + @Bind(R.id.male) + RadioButton maleRadioBtn; + @Bind(R.id.female) + RadioButton femaleRadioBtn; + + @Checked(messageResId = R.string.error_must_agree_terms) + @Bind(R.id.terms_checkbox) + CheckBox termsCheckBox; + + @Bind(R.id.terms_textview) + TextView termsTextView; + + @Bind(R.id.signup) + Button signupButton; + + @Bind(R.id.toggle_visibility) + ImageView toggleVisibilityBtn; + + private String verificationCodeString; + private Validator validator; + private boolean passwordVisible = false; + private boolean smsPermissionDeclined; + private SimpleActivityResultPermissionDelegate permissionHandlerDelegate; + + private boolean byEmail; + private String emailOrPhone; + + public RegisterFragment() { + // Required empty public constructor + } + + + @Override + public int getLayoutID() { + return R.layout.fragment_register; + } + + public static RegisterFragment newInstance(String code, boolean byEmail, String emailOrPhone) { + Bundle args = new Bundle(); + if(code != null) + args.putString(KEY_VERIFICATION_CODE, code); + + RegisterFragment fragment = new RegisterFragment(); + + fragment.byEmail = byEmail; + fragment.emailOrPhone = emailOrPhone; + fragment.setArguments(args); + return fragment; + } + + public static RegisterFragment newInstance(String code) { + + Bundle args = new Bundle(); + args.putString(KEY_VERIFICATION_CODE, code); + RegisterFragment fragment = new RegisterFragment(); + fragment.setArguments(args); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + validator = new Validator(this); + validator.setValidationListener(this); + if (getArguments() != null) + verificationCodeString = getArguments().getString(KEY_VERIFICATION_CODE, null); + else if (savedInstanceState != null) + verificationCodeString = savedInstanceState.getString(KEY_VERIFICATION_CODE, null); + else + verificationCodeString = ""; + + permissionHandlerDelegate = new SimpleActivityResultPermissionDelegate(){ + + @Override + public void handlePermissionDenied() { + Logger.longToast(getString(R.string.permission_reason_sms)); + smsPermissionDeclined = true; + } + + @Override + public void handlePermissionGranted() { + getActivity().registerReceiver(smsReceiver, new IntentFilter("android.provider.Telephony.SMS_RECEIVED")); + } + + @Override + public void handleValidResults(int requestCode, Intent data) {}// we don't start activity for result, a better design perhaps... + }; + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString(KEY_VERIFICATION_CODE, verificationCodeString); + } + + @Override + protected void initUI() { + title.setText(getString(R.string.signup_in_poinila)); + arrow.setVisibility(View.GONE); + if (!TextUtils.isEmpty(verificationCodeString)) { + verificationCode.setText(verificationCodeString); + } + termsTextView.setText(Html.fromHtml( + "" + + getString(R.string.terms_of_condition_word) + " " + + getString(R.string.terms_of_condition_tail))); + termsTextView.setMovementMethod(LinkMovementMethod.getInstance()); + + termsCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if(!TextUtils.isEmpty(verificationCode.getText().toString())) + signupButton.setEnabled(isChecked); + } + }); + + verificationCode.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { + + } + + @Override + public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { + + } + + @Override + public void afterTextChanged(Editable editable) { + signupButton.setEnabled(true); +// signupButton.setEnabled(!TextUtils.isEmpty(verificationCode.getText().toString()) && termsCheckBox.isChecked()); + } + }); + + userName.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + String tempUserName; + if (!hasFocus && !(tempUserName = ((EditText) v).getText().toString().trim()).isEmpty()) { + PoinilaNetService.checkUserNameValidity(tempUserName); + } + } + }); + } + + @Subscribe + public void onUserNameValidityResponse(UserNameValidityEvent event) { + if (!event.success) { + String error = ""; + switch (event.error) { + case UserNameValidityEvent.DUPLICATE: + error = getString(R.string.error_already_taken_username); + break; + case UserNameValidityEvent.RESERVED: + error = getString(R.string.error_invalid_username); + break; + case UserNameValidityEvent.LENGTH: + error = getString(R.string.error_username_length); + break; + case UserNameValidityEvent.RULE: + error = getString(R.string.error_username_rule); + break; + } + if (!TextUtils.isEmpty(error)) userName.setError(error); + } + } + + @OnClick(R.id.signup) + public void onSignup() { + validator.validate(); + //onValidationSucceeded(); + } + + @Subscribe + public void onRegisterResponse(RegisterResponseEvent event) { + + if (event.successful) + DataRepository.getInstance().getMyInfo(ConnectionUitls.isNetworkOnline(), MyInfoReceivedEvent.MY_INFO_TYPE.LOAD); + else if(event.errorCode == RegisterResponseEvent.USED_VERIFICATION_CODE){ + dismissProgressDialog(); + ViewUtils.temporaryError(verificationCode, getString(R.string.error_already_used_verification_code)); + } + else if (event.errorCode == RegisterResponseEvent.DUPLICATE_USERNAME) { + dismissProgressDialog(); + ViewUtils.temporaryError(userName, getString(R.string.error_already_taken_username)); + } else { + dismissProgressDialog(); + Logger.toast(R.string.error_fail_register); + } + } + + @Subscribe + public void onUserInfoReceived(MyInfoReceivedEvent event) { + + if(event.type != MyInfoReceivedEvent.MY_INFO_TYPE.LOAD) + return; + + onGettingInitDataResponse(event); + + new AsyncTask() { + + @Override + protected Void doInBackground(Object... params) { + DataRepository.syncWithMyInfoResponse((MyInfoReceivedEvent)params[0]); + return null; + } + + @Override + protected void onPostExecute(Void o) { + PonilaAccountManager.getInstance().initUserTag(); + decideAboutNextPage(); + } + + + }.execute(event); + + } + + private void decideAboutNextPage() { + PageChanger.goToSelectInterest(getActivity(), true); + getActivity().finish(); // one must not be able to navigate back to register + dismissProgressDialog(); + } + + public void fillVerificationCode(String code) { + verificationCode.setText(code); + } + + SmsReceiver smsReceiver = new SmsReceiver(); + + @Override + public void onStart() { + super.onStart(); + permissionHandlerDelegate.askForPermission(this, + Manifest.permission.RECEIVE_SMS, + BaseActivity.MY_PERMISSIONS_REQUEST_RECEIVE_SMS); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + permissionHandlerDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + + @Override + public void onStop() { + super.onStop(); + // TODO: unregister receiver. but I don't know if it's correct. may be we should listen + // in all application life cycle + try{ + if (!smsPermissionDeclined) + getActivity().unregisterReceiver(smsReceiver); + }catch (IllegalArgumentException exception){ + // just ignore it since we don't know user granted permission or not + } + } + + + @Subscribe + public void onSmsReceived(SmsReceivedEvent event) { + fillVerificationCode(event.str); + } + + @Override + public void onValidationSucceeded() { + PoinilaNetService.register( + "", +// verificationCode.getText().toString().trim(), + fullName.getText().toString().trim(), + userName.getText().toString().trim(), + getSelectedGender(), + password.getText().toString().trim(), + byEmail, + emailOrPhone + ); + showProgressDialog(); + } + + @Override + public void onValidationFailed(List errors) { + for (ValidationError error : errors) { + View view = error.getView(); + String message = error.getCollatedErrorMessage(getActivity()); + + // Display error messages ;) + if (view instanceof EditText) { + ((EditText) view).setError(message); + } else { + Logger.toastError(message); + } + } + //Logger.toast(getString(R.string.signup_faild)); + } + + public Gender getSelectedGender() { + return genderRadioGroup.getCheckedRadioButtonId() == maleRadioBtn.getId() ? Gender.MALE : Gender.FEMALE; + } + + @OnClick(R.id.toggle_visibility) + public void onChangeVisibility() { + passwordVisible ^= true; // toggle + + int start = password.getSelectionStart(); + int end = password.getSelectionEnd(); + password.setInputType(passwordVisible ? + InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD : + InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); + CalligraphyUtils.applyFontToTextView(getActivity(), password, CalligraphyConfig.get().getFontPath()); + password.setSelection(start, end); + + toggleVisibilityBtn.setImageResource(passwordVisible ? + R.drawable.toggle_visible_nobel_32dp : + R.drawable.toggle_invisible_nobel_32dp); + } + + /*---------*/ + + @Override + protected boolean sendsRequestAutomatically() { + return false; + } + + @Override + protected void requestInitialData() { + + } + + @Override + public ViewGroup getLoadableView() { + return null; + } + + @Override + public boolean mustShowProgressView() { + return false; + } + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/ResetPasswordFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/ResetPasswordFragment.java new file mode 100755 index 0000000..e850f1e --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/ResetPasswordFragment.java @@ -0,0 +1,221 @@ +package com.shaya.poinila.android.presentation.view.fragments; + +import android.Manifest; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v13.app.FragmentCompat; +import android.text.TextUtils; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; + +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.SmsReceiver; +import com.shaya.poinila.android.presentation.uievent.SmsReceivedEvent; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.presentation.view.activity.BaseActivity; +import com.shaya.poinila.android.presentation.view.costom_view.ActivityResultPermissionDelegate.SimpleActivityResultPermissionDelegate; +import com.shaya.poinila.android.util.ConnectionUitls; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.utils.PonilaAccountManager; +import com.squareup.otto.Subscribe; + +import butterknife.Bind; +import butterknife.ButterKnife; +import butterknife.OnClick; +import data.PoinilaNetService; +import data.event.LoginFailedEvent; +import data.event.LoginSucceedEvent; +import data.event.MyInfoReceivedEvent; +import manager.DataRepository; + +/** + * Created by iran on 12/8/2015. + */ +public class ResetPasswordFragment extends BusFragment implements FragmentCompat.OnRequestPermissionsResultCallback { + + private static final String KEY_IDENTIFICATION_CODE = "identification code"; + @Bind(R.id.submit) + Button SubmitButton; + @Bind(R.id.email_or_phone_number) + EditText verificationCodeInput; + @Bind(R.id.password_input) + EditText passwordInput; + @Bind(R.id.card_title) + TextView title; + private String identificationCodeString; + private SimpleActivityResultPermissionDelegate permissionHandlerDelegate; + private boolean smsPermissionDeclined; + + @OnClick(R.id.submit) + public void onResetPass() { + + if (!TextUtils.isEmpty(passwordInput.getText().toString())) { + showProgressDialog(); + PoinilaNetService.resetPassword(passwordInput.getText().toString().trim(), verificationCodeInput.getText().toString().trim()); + + } else + Logger.toastError(R.string.error_password); + } + + @Subscribe + public void onSuccessfulResetPassword(LoginSucceedEvent event) { + DataRepository.getInstance().getMyInfo(ConnectionUitls.isNetworkOnline(), MyInfoReceivedEvent.MY_INFO_TYPE.LOAD); + } + + @Subscribe + public void onFailedResetPassword(LoginFailedEvent event) { + dismissProgressDialog(); + Logger.toast(getString(R.string.error_change_password_code)); + } + + @Subscribe + public void onUserInfoReceived(MyInfoReceivedEvent event) { + + if (event.type != MyInfoReceivedEvent.MY_INFO_TYPE.LOAD) + return; + + onGettingInitDataResponse(event); + + + new AsyncTask() { + + @Override + protected Void doInBackground(Object... params) { + DataRepository.syncWithMyInfoResponse((MyInfoReceivedEvent) params[0]); + return null; + } + + @Override + protected void onPostExecute(Void o) { + PonilaAccountManager.getInstance().initUserTag(); + PageChanger.goToDashboard(getActivity()); + dismissProgressDialog(); + } + + + }.execute(event); + + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getArguments() != null) + identificationCodeString = getArguments().getString(KEY_IDENTIFICATION_CODE, null); + else if (savedInstanceState != null) + identificationCodeString = savedInstanceState.getString(KEY_IDENTIFICATION_CODE, null); + else + identificationCodeString = ""; + + permissionHandlerDelegate = new SimpleActivityResultPermissionDelegate() { + @Override + public void handlePermissionDenied() { + Logger.longToast(getString(R.string.permission_reason_sms)); + smsPermissionDeclined = true; + } + + @Override + public void handlePermissionGranted() { + getActivity().registerReceiver(smsReceiver, new IntentFilter("android.provider.Telephony.SMS_RECEIVED")); + } + + @Override + public void handleValidResults(int requestCode, Intent data) { + }// we don't start activity for result, a better design perhaps... + }; + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString(KEY_IDENTIFICATION_CODE, identificationCodeString); + } + + public void fillIdentificationCodeInput(String code) { + verificationCodeInput.setText(code); + } + + SmsReceiver smsReceiver = new SmsReceiver(); + + @Override + public void onStart() { + super.onStart(); + permissionHandlerDelegate.askForPermission(this, + Manifest.permission.RECEIVE_SMS, + BaseActivity.MY_PERMISSIONS_REQUEST_RECEIVE_SMS); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + permissionHandlerDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + + @Override + public void onStop() { + super.onStop(); + // TODO: unregister receiver. but I don't know if it's correct. may be we should listen + // in all application life cycle + try { + if (!smsPermissionDeclined) + getActivity().unregisterReceiver(smsReceiver); + } catch (IllegalArgumentException exception) { + // just ignore it since we don't know user granted permission or not + } + } + + @Subscribe + public void onSmsReceived(SmsReceivedEvent event) { + verificationCodeInput.setText(event.str); + } + + @Override + protected boolean sendsRequestAutomatically() { + return false; + } + + @Override + protected void requestInitialData() { + + } + + @Override + public ViewGroup getLoadableView() { + return null; + } + + @Override + public boolean mustShowProgressView() { + return false; + } + + @Override + public int getLayoutID() { + return R.layout.fragment_reset_password; + } + + @Override + protected void initUI() { + ViewUtils.setText(title, getString(R.string.reset_password)); + ButterKnife.findById(rootView, R.id.left_arrow).setVisibility(View.GONE); + if (!TextUtils.isEmpty(identificationCodeString)) + fillIdentificationCodeInput(identificationCodeString); + } + + public static ResetPasswordFragment newInstance(String code) { + + Bundle args = new Bundle(); + args.putString(KEY_IDENTIFICATION_CODE, code); + ResetPasswordFragment fragment = new ResetPasswordFragment(); + fragment.setArguments(args); + return fragment; + } + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/SearchFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/SearchFragment.java new file mode 100755 index 0000000..f173efd --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/SearchFragment.java @@ -0,0 +1,731 @@ +package com.shaya.poinila.android.presentation.view.fragments; + + +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.os.Bundle; +import android.os.Handler; +import android.support.annotation.Nullable; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.StaggeredGridLayoutManager; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewPropertyAnimator; +import android.view.ViewTreeObserver; +import android.view.animation.AccelerateInterpolator; +import android.view.inputmethod.EditorInfo; +import android.widget.EditText; +import android.widget.ImageButton; +import android.widget.TextView; +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewProvider; +import com.shaya.poinila.android.presentation.uievent.CollectionClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.FriendCirclesUpdated; +import com.shaya.poinila.android.presentation.uievent.FriendshipClickEvent; +import com.shaya.poinila.android.presentation.uievent.MemberClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.NeutralDialogButtonClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.OnFollowUnfollowCollectionUIEvent; +import com.shaya.poinila.android.presentation.uievent.PositiveButtonClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.PostClickedUIEvent; +import com.shaya.poinila.android.presentation.view.dialog.DialogLauncher; +import com.shaya.poinila.android.presentation.viewholder.BaseViewHolder; +import com.shaya.poinila.android.presentation.viewholder.DashboardPostViewHolder; +import com.shaya.poinila.android.presentation.viewholder.FollowableCollectionViewHolder; +import com.shaya.poinila.android.presentation.viewholder.MemberViewHolder; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.util.ResourceUtils; +import com.squareup.otto.Subscribe; +import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration; + +import butterknife.Bind; +import butterknife.OnClick; +import data.PoinilaNetService; +import data.event.BaseEvent; +import data.event.CollectionsReceivedEvent; +import data.event.MembersReceivedEvent; +import data.event.PostsReceivedEvent; +import data.model.Collection; +import data.model.FriendRequestAnswer; +import data.model.FriendshipStatus; +import data.model.Loading; +import data.model.Member; +import data.model.Post; +import manager.DBFacade; +import manager.DataRepository; + +import static android.support.v7.widget.StaggeredGridLayoutManager.VERTICAL; +import static java.util.Collections.singletonList; + + +// fragmenta ro joda nakardam be khatere bahse moshkelate inheritance tu subscrib haye otto. +// avval recyclerview ro GONE mizaram o adapteresh nulle chon set kardane listener ina be bag mikhore. + + +public class SearchFragment extends BusFragment { + private static final String POST = "post"; + private static final String COLLECTION = "collection"; + private static final String MEMBER = "member"; + private static final String TAG = "search fragment"; + + @Bind(R.id.recycler_view) + protected RecyclerView mRecyclerView; + + private static final long POST_ANIMATION_DURATION = 400; //800; // milliseconds + private static final long COLLECTION_ANIMATION_DURATION = 400; //600; // milliseconds + private static final long MEMBER_ANIMATION_DURATION = 400;// 400; // milliseconds + + @Bind(R.id.search_btn) + ImageButton searchButton; + + @Bind(R.id.search_field) + EditText searchField; + + @Bind(R.id.post_tag) + TextView post; + + @Bind(R.id.collection_tag) + TextView collection; + + @Bind(R.id.member_tag) + TextView member; + + @Bind(R.id.search_container) + ViewGroup searchBox; + + private RecyclerViewAdapter mAdapter; + private ViewPropertyAnimator first_animation; + private ViewPropertyAnimator second_animation; + private ViewPropertyAnimator third_animation; + private String selectedCategory = ""; + private float search_container_right; + private int tag_size; + private float search_field_right; + private Handler mHandler; + private int MEMBER_DIST; + private int COLLECTION_DIST; + private int POST_DIST; + private int TAG_WIDTH; + private int VELOCITY; + private String bookmark; + private boolean requestingIsLocked = false; + private String query; + private RecyclerView.OnScrollListener recyclerViewListener; + + private boolean hasLoading = false; + + private RecyclerView.ItemDecoration mItemDecoration; + + + public SearchFragment() { + // Required empty public constructor + + } + + public static SearchFragment newInstance(){ + SearchFragment fragment = new SearchFragment(); + + return fragment; + } + + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View v = super.onCreateView(inflater, container, savedInstanceState); + + TAG_WIDTH = ResourceUtils.covertDpToPixel(R.dimen.search_tag_width); + VELOCITY = (int) (TAG_WIDTH / (0.4)); // in dp/seconds + + runJustBeforeBeingDrawn(container, findSizesRunnable); + + mItemDecoration = new HorizontalDividerItemDecoration.Builder(getActivity()) + .sizeResId(R.dimen.divider_thickness) + .marginResId(R.dimen.border_thin) + .build(); + return v; + } + + + @Override + public int getLayoutID() { + return R.layout.fragment_search; + } + + @Override + protected void initUI() { + mRecyclerView.setVisibility(View.INVISIBLE); + + searchField.setPadding(searchField.getPaddingLeft(), searchField.getPaddingTop(), + searchField.getPaddingRight() + + //(int)ResourceUtils.getDimen(R.dimen.search_tag_width) + + (int) ResourceUtils.getDimen(R.dimen.padding_lvl2), + searchField.getPaddingBottom()); + searchButton.setEnabled(false); + + searchField.setEnabled(false); + searchField.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (actionId == EditorInfo.IME_ACTION_SEARCH) { + searchButton.performClick(); + } + return false; // ?? + } + }); + } + + @OnClick(R.id.search_btn) + public void onSearchClicked() { + loadMore = false; + bookmark = null; + if (mAdapter != null) { + mAdapter.clear(); + mAdapter = null; + } + mRecyclerView.setVisibility(View.GONE); + query = getQueryFromInputField(); + if (query == null) { + Logger.toast(R.string.error_short_query); + return; + } + requestingIsLocked = true; + + if(selectedCategory.equals(MEMBER)){ + mRecyclerView.addItemDecoration(mItemDecoration); + }else { + mRecyclerView.removeItemDecoration(mItemDecoration); + } + + initData(); + } + + public void onLoadMore() { + if (!requestingIsLocked) { + requestingIsLocked = true; + requestInitialData(); + } + } + + @Override + protected void requestInitialData() { + switch (selectedCategory) { + case POST: + PoinilaNetService.searchPostWithQuery(singletonList(query), bookmark); + break; + case COLLECTION: + PoinilaNetService.searchCollectionsWithQuery(singletonList(query), bookmark); + break; + case MEMBER: + PoinilaNetService.searchMembersWithQuery(singletonList(query), bookmark); + break; + } + } + + @Override + protected boolean isInitDataResponseValid(BaseEvent baseEvent) { + return super.isInitDataResponseValid(baseEvent) && !loadMore; + } + + protected boolean isListDataResponseValid(BaseEvent baseEvent, String bookmark) { + return !bookmark.equals(this.bookmark); + } + + @Override + public void onSuccessfulInitData(BaseEvent baseEvent) { + requestingIsLocked = false; + switch (selectedCategory) { + case POST: + if (((PostsReceivedEvent) baseEvent).posts.isEmpty()) + Logger.toast(R.string.error_nothing_found); + mRecyclerView = new RecyclerViewProvider(mRecyclerView). + setAdapter(getRecyclerViewAdapter()). + setStaggeredLayoutManager(VERTICAL, getResources().getInteger(R.integer.column_count)). + bindViewToAdapter(); + getRecyclerViewAdapter().resetData(((PostsReceivedEvent) baseEvent).posts); + + if(((PostsReceivedEvent) baseEvent).posts.size() >= 25){ + setLoading(new Loading()); + } + + + break; + case COLLECTION: + if (((CollectionsReceivedEvent) baseEvent).collections.isEmpty()) + Logger.toast(R.string.error_nothing_found); + mRecyclerView = new RecyclerViewProvider(mRecyclerView). + // TODO: changed from gridview to this because of follow bar on some collections + setAdapter(getRecyclerViewAdapter()). + setStaggeredLayoutManager(VERTICAL, getResources().getInteger(R.integer.column_count)). + bindViewToAdapter(); + getRecyclerViewAdapter().resetData(((CollectionsReceivedEvent) baseEvent).collections); + + if(((CollectionsReceivedEvent) baseEvent).collections.size() >= 25){ + setLoading(new Loading()); + } + + break; + case MEMBER: + if (((MembersReceivedEvent) baseEvent).members.isEmpty()) + Logger.toast(R.string.error_nothing_found); + mRecyclerView = new RecyclerViewProvider(mRecyclerView). + setAdapter(getRecyclerViewAdapter()). + setLinearLayoutManager(VERTICAL). + bindViewToAdapter(); + getRecyclerViewAdapter().resetData(((MembersReceivedEvent) baseEvent).members); + + if(((MembersReceivedEvent) baseEvent).members.size() >= 25){ + setLoading(new Loading()); + } + + break; + } + mRecyclerView.removeOnScrollListener(lastUsedListener); + lastUsedListener = getRecyclerViewListener(); + mRecyclerView.addOnScrollListener(lastUsedListener); + + mRecyclerView.setVisibility(View.VISIBLE); + super.onSuccessfulInitData(baseEvent); + } + + public void onSuccessfulListData(BaseEvent baseEvent, String newBookmark) { + requestingIsLocked = false; + bookmark = newBookmark; + switch (selectedCategory) { + case POST: + getRecyclerViewAdapter().addItems(((PostsReceivedEvent) baseEvent).posts); + break; + case COLLECTION: + getRecyclerViewAdapter().addItems(((CollectionsReceivedEvent) baseEvent).collections); + break; + case MEMBER: + getRecyclerViewAdapter().addItems(((MembersReceivedEvent) baseEvent).members); + break; + } + } + + private void onGettingResult(BaseEvent event, String bookmark) { + if (isInitDataResponseValid(event)) { + this.bookmark = bookmark; + onSuccessfulInitData(event); + return; + } + + if (isListDataResponseValid(event, bookmark)) { + onSuccessfulListData(event, bookmark); + }else{ + onEndListData(); + } + } + + @Subscribe + public void onSearchPostResults(PostsReceivedEvent event) { + if (event.receiverName != BaseEvent.ReceiverName.SearchFragment || + !selectedCategory.equals(POST)) + return; + onGettingResult(event, event.bookmark); + } + + @Subscribe + public void onSearchCollectionResults(CollectionsReceivedEvent event) { + if (event.receiverName != BaseEvent.ReceiverName.SearchFragment || + !selectedCategory.equals(COLLECTION)) + return; + onGettingResult(event, event.bookmark); + } + + @Subscribe + public void onSearchPeopleResults(MembersReceivedEvent event) { + if (!selectedCategory.equals(MEMBER)) + return; + onGettingResult(event, event.bookmark); + } + + public void setLoading(Loading loading){ + hasLoading = true; + getRecyclerViewAdapter().setLoading(loading); + } + + public void removeLoading(){ + if(hasLoading) { + hasLoading = false; + getRecyclerViewAdapter().removeLoading(); + } + } + + private void onEndListData(){ + removeLoading(); + } + + private RecyclerViewAdapter getRecyclerViewAdapter() { + if (mAdapter == null) { + final BaseEvent.ReceiverName receiverName = BaseEvent.ReceiverName.SearchFragment; + switch (selectedCategory) { + case POST: + mAdapter = new RecyclerViewAdapter(getContext(), R.layout.post_dashboard) { + @Override + protected BaseViewHolder getProperViewHolder(View v, int viewType) { + + if(viewType == VIEW_TYPE_LOAD_PROGRESS){ + return new BaseViewHolder.EmptyViewHolder(v); + } + + return new DashboardPostViewHolder(v, receiverName); + } + + @Override + protected boolean isStaggeredGridLayoutManager() { + return true; + } + }; + break; + case COLLECTION: + mAdapter = new RecyclerViewAdapter(getContext(), R.layout.collection_followable) { + @Override + protected BaseViewHolder getProperViewHolder(View v, int viewType) { + + if(viewType == VIEW_TYPE_LOAD_PROGRESS){ + return new BaseViewHolder.EmptyViewHolder(v); + } + + return new FollowableCollectionViewHolder(v, receiverName); + } + }; + break; + case MEMBER: + mAdapter = new RecyclerViewAdapter(getContext(), R.layout.member_inlist) { + @Override + protected BaseViewHolder getProperViewHolder(View v, int viewType) { + + if(viewType == VIEW_TYPE_LOAD_PROGRESS){ + return new BaseViewHolder.EmptyViewHolder(v); + } + + return new MemberViewHolder(v, receiverName); + } + + + }; + break; + } + } + return mAdapter; + } + + @Override + public boolean mustShowProgressView() { + return true; + } + + + public ViewGroup getLoadableView() { + return mRecyclerView; + } + + @Override + protected boolean sendsRequestAutomatically() { + return false; + } + + + private String getQueryFromInputField() { + String query = searchField.getText().toString(); + //return query; + return (query.trim().length() <= 1) ? null : query; + } + + @OnClick({R.id.post_tag, R.id.collection_tag, R.id.member_tag}) + public void onTagClick(View v) { + if (selectedCategory.isEmpty()) { + switch (v.getId()) { + case R.id.post_tag: + selectedCategory = POST; + post.setBackgroundResource(R.drawable.search_tag_active); + animate(MEMBER_DIST, COLLECTION_DIST, POST_DIST - TAG_WIDTH); + break; + case R.id.collection_tag: + selectedCategory = COLLECTION; + collection.setBackgroundResource(R.drawable.search_tag_active); + animate(MEMBER_DIST, COLLECTION_DIST - TAG_WIDTH, POST_DIST); + break; + case R.id.member_tag: + selectedCategory = MEMBER; + member.setBackgroundResource(R.drawable.search_tag_active); + animate(MEMBER_DIST - TAG_WIDTH, COLLECTION_DIST, POST_DIST); + break; + } + searchField.setEnabled(true); + searchButton.setEnabled(true); + } else { + animateBack(); + switch (v.getId()) { + case R.id.post_tag: + post.setBackgroundResource(R.drawable.search_tag_inactive); + break; + case R.id.collection_tag: + collection.setBackgroundResource(R.drawable.search_tag_inactive); + break; + case R.id.member_tag: + member.setBackgroundResource(R.drawable.search_tag_inactive); + break; + } + searchField.setEnabled(false); + searchButton.setEnabled(false); + selectedCategory = ""; + searchField.setText(""); + } + } + + public void animate(int memberDist, int collectionDist, int postDist) { + ObjectAnimator oa1 = ObjectAnimator.ofFloat( + member, "x", member.getLeft() + memberDist).setDuration(MEMBER_ANIMATION_DURATION);//(memberDist * 1000/VELOCITY); + ObjectAnimator oa2 = ObjectAnimator.ofFloat( + collection, "x", collection.getLeft() + collectionDist).setDuration(COLLECTION_ANIMATION_DURATION);//(collectionDist * 1000/VELOCITY); + ObjectAnimator oa3 = ObjectAnimator.ofFloat( + post, "x", post.getLeft() + postDist).setDuration(POST_ANIMATION_DURATION);//(postDist * 1000/VELOCITY); + AnimatorSet set = new AnimatorSet(); + set.setInterpolator(new AccelerateInterpolator()); + set.playTogether(oa1, oa2, oa3); + set.start(); + } + + public void animateBack() { + ObjectAnimator oa1 = ObjectAnimator.ofFloat( + member, "x", member.getLeft()).setDuration(MEMBER_ANIMATION_DURATION);//((long)Math.abs(member.getX() - member.getLeft()) * 1000/VELOCITY); + ObjectAnimator oa2 = ObjectAnimator.ofFloat( + collection, "x", collection.getLeft()).setDuration(COLLECTION_ANIMATION_DURATION);//((long)Math.abs(collection.getX() - collection.getLeft()) * 1000/VELOCITY); + ObjectAnimator oa3 = ObjectAnimator.ofFloat( + post, "x", post.getLeft()).setDuration(POST_ANIMATION_DURATION);//((long)Math.abs(post.getX() - post.getLeft()) * 1000/VELOCITY); + AnimatorSet set = new AnimatorSet(); + set.setInterpolator(new AccelerateInterpolator()); + set.playTogether(oa1, oa2, oa3); + set.start(); + } + + private static void runJustBeforeBeingDrawn(final View view, final Runnable runnable) { + final ViewTreeObserver vto = view.getViewTreeObserver(); + final ViewTreeObserver.OnPreDrawListener preDrawListener = new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + //Log.d(App.APPLICATION_TAG, CLASS_TAG + "onpredraw"); + runnable.run(); + final ViewTreeObserver vto = view.getViewTreeObserver(); + vto.removeOnPreDrawListener(this); + return true; + } + }; + vto.addOnPreDrawListener(preDrawListener); + } + + Runnable findSizesRunnable = new Runnable() { + @Override + public void run() { + MEMBER_DIST = post.getRight() - member.getLeft(); + COLLECTION_DIST = post.getRight() - collection.getLeft(); + POST_DIST = post.getRight() - post.getLeft(); + } + }; + + @Override + public void onStop() { + super.onStop(); + searchField.setText(""); + } + + private boolean loadMore = false; + + public RecyclerView.OnScrollListener getRecyclerViewListener() { + switch (selectedCategory) { + case POST: + return getPostListListener(); + case COLLECTION: + return getCollectionListListener(); + case MEMBER: + return getMemberScrollListener(); + } + // must never happen + return new RecyclerView.OnScrollListener() { + }; + } + + private RecyclerView.OnScrollListener getPostListListener() { + return new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + int[] lastVisibleItems = ((StaggeredGridLayoutManager) recyclerView.getLayoutManager()). + findLastVisibleItemPositions(null); + int itemCount = getRecyclerViewAdapter().getItemCount() - 1; + if ((lastVisibleItems[0] == itemCount || + lastVisibleItems[1] == itemCount) && dy != 0) { + loadMore = true; + onLoadMore(); + } + } + }; + } + + private RecyclerView.OnScrollListener getCollectionListListener() { + return new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + int[] lastVisibleItems = ((StaggeredGridLayoutManager) recyclerView.getLayoutManager()). + findLastVisibleItemPositions(null); + int itemCount = getRecyclerViewAdapter().getItemCount() - 1; + if ((lastVisibleItems[0] == itemCount || + lastVisibleItems[1] == itemCount) && dy != 0) { + loadMore = true; + onLoadMore(); + } + } + }; + } + + private RecyclerView.OnScrollListener getMemberScrollListener() { + return new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + if (((LinearLayoutManager) recyclerView.getLayoutManager()).findLastVisibleItemPosition() == + getRecyclerViewAdapter().getItemCount() - 1 && dy != 0) { + loadMore = true; + onLoadMore(); + } + } + }; + } + + RecyclerView.OnScrollListener lastUsedListener; + + + + /*----------------*/ + // TODO; I think its better we replace fragments with each other for taking use of + // event handling on ListFragments like MemberListFragment + + /*--------Post----------*/ + + @Subscribe + public void onProfilePicClickedEvent(MemberClickedUIEvent event) { + if (event.receiverName != BaseEvent.ReceiverName.SearchFragment) + return; + + Object item = getRecyclerViewAdapter().getItem(event.adapterPosition); + + Member member = null; + if (item instanceof Post) { + member = ((Post) item).author; + } else if (item instanceof Collection) { + member = ((Collection) item).owner; + } else if (item instanceof Member) { + member = ((Member) item); + } + if (member == null) return; + PageChanger.goToProfile(getActivity(), member); + } + + @Subscribe + public void onPostClicked(PostClickedUIEvent event) { + if (event.receiverName != BaseEvent.ReceiverName.SearchFragment) + return; + Post post = ((Post) getRecyclerViewAdapter().getItem(event.adapterPosition)); + PageChanger.goToPost(getActivity(), post); + } + + + /*-----Collection------*/ + + + private int clickedItemPosition; + + @Subscribe + public void onPositiveDialogButton(PositiveButtonClickedUIEvent event) { + Member member = ((Member) getRecyclerViewAdapter().getItem(clickedItemPosition)); + switch (member.friendshipStatus) { + case NotFriend: + PoinilaNetService.friendRequest(member.getId(), DBFacade.getDefaultCircle().id); + member.friendshipStatus = FriendshipStatus.Pending; + break; + case WaitingForAction: // sending request + PoinilaNetService.answerFriendRequest(member.id, (FriendRequestAnswer)event.getData(), DBFacade.getDefaultCircle().id); + member.friendshipStatus = FriendshipStatus.IsFriend; + break; + case IsFriend: // removing friend + PoinilaNetService.removeFriend(member.getId()); + member.friendshipStatus = FriendshipStatus.NotFriend; + break; + case Pending: // no action yet + break; + } + getRecyclerViewAdapter().notifyItemChanged(clickedItemPosition); + } + + @Subscribe + public void onCollectionClicked(CollectionClickedUIEvent event) { + if (event.receiverName != BaseEvent.ReceiverName.SearchFragment) + return; + + Object item = getRecyclerViewAdapter().getItem(event.adapterPosition); + Collection collection = null; + if (item instanceof Post) { + collection = ((Post) item).collection; + } else if (item instanceof Collection) { + collection = ((Collection) item); + } + if (collection == null) return; + PageChanger.goToCollection(getActivity(), collection); + + } + + /*-------Member------*/ + @Subscribe + public void onNeutralDialogButton(NeutralDialogButtonClickedUIEvent event) { + Member clickedMember = ((Member) getRecyclerViewAdapter().getItem(clickedItemPosition)); + DialogLauncher.launchChangeFriendCircle(getFragmentManager(), clickedMember); + } + + @Subscribe + public void onShowFriendShipDialog(FriendshipClickEvent event) { + // handling anonymous + if (DataRepository.isUserAnonymous()) { + Logger.toastError(R.string.error_guest_action); + return; + } + + Member member = (Member) getRecyclerViewAdapter().getItem(event.adapterPosition); + DialogLauncher.launchFriendshipDialog(member, getFragmentManager()); + clickedItemPosition = event.adapterPosition; + } + + @Subscribe + public void onFollowCollection(OnFollowUnfollowCollectionUIEvent event) { + // handling anonymous + if (DataRepository.isUserAnonymous()) { + Logger.toastError(R.string.error_guest_action); + return; + } + + Collection collection = (Collection) getRecyclerViewAdapter().getItem(event.adapterPosition); + if (event.follow) { + PoinilaNetService.followCollection(collection.getId()); + } else { + PoinilaNetService.unfollowCollection(collection.getId()); + } + collection.followedByMe ^= true; // toggle boolean by xor ing with "True". + getRecyclerViewAdapter().notifyItemChanged(event.adapterPosition); + } + + @Subscribe + public void onFriendCirclesUpdated(FriendCirclesUpdated event) { + if (selectedCategory.equals(MEMBER)) { + int index = getRecyclerViewAdapter().getItems().indexOf(event.member); + if (index < 0) return; + + ((Member) getRecyclerViewAdapter().getItem(index)).circle_ids = event.selectedCirclesIDs; + } + } + +} + diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/VerificationRequestFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/VerificationRequestFragment.java new file mode 100755 index 0000000..19c966a --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/VerificationRequestFragment.java @@ -0,0 +1,134 @@ +package com.shaya.poinila.android.presentation.view.fragments; + +import android.text.InputType; +import android.util.Patterns; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.RadioGroup; +import android.widget.TextView; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.presentation.view.activity.SignUpLoginActivity; +import com.shaya.poinila.android.util.Logger; +import com.squareup.otto.Subscribe; + +import butterknife.Bind; +import butterknife.ButterKnife; +import butterknife.OnClick; +import data.PoinilaNetService; +import data.event.VerificationRequestResponse; + +/** + * Created by iran on 12/6/2015. + */ +public class VerificationRequestFragment extends BusFragment { + @Bind(R.id.card_title) + TextView titleTextView; + @Bind(R.id.radio_group) + RadioGroup optionsRadioGroup; + @Bind(R.id.input_field) + EditText inputField; + @Bind(R.id.input_filed_icon) + ImageView inputFieldIcon; + @Bind(R.id.verify_button) + Button verifyButton; + private boolean mVerificationByEmail; + + @Override + protected boolean sendsRequestAutomatically() { + return false; + } + + @Override + protected void requestInitialData() { + } + + @Override + public ViewGroup getLoadableView() { + return null; + } + + @Override + public boolean mustShowProgressView() { + return false; + } + + @Override + public int getLayoutID() { + return R.layout.fragment_request_verification_code; + } + + @Override + protected void initUI() { + //backForthButtonsBox.setBackForthListener(this); + ButterKnife.findById(rootView, R.id.left_arrow).setVisibility(View.GONE); + titleTextView.setText(R.string.verifying); + optionsRadioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(RadioGroup group, int checkedId) { + switch (checkedId) { + case R.id.email_option: + onMailOption(); + break; + case R.id.sms_option: + onSmsOption(); + break; + } + } + }); + if (mVerificationByEmail) onMailOption(); + else onSmsOption(); + } + + private void onSmsOption() { + mVerificationByEmail = false; + inputField.setInputType(InputType.TYPE_CLASS_PHONE); + inputFieldIcon.setImageResource(R.drawable.phone_48dp); + } + + private void onMailOption() { + mVerificationByEmail = true; + inputField.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS); + inputFieldIcon.setImageResource(R.drawable.email_48dp); + } + + @Subscribe + public void onVerifyResponse(VerificationRequestResponse event) { + dismissProgressDialog(); + if (event.succeed) { + ((SignUpLoginActivity) getActivity()).goToRegister(null, event.byEmail, event.emailOrPhone); + Logger.toast(mVerificationByEmail ? R.string.success_verification_mail : R.string.success_verification_sms); + } else { + ViewUtils.temporaryError(inputField, event.errorExplanation); + } + optionsRadioGroup.setEnabled(true); + } + + @OnClick(R.id.verify_button) + public void onVerificationRequest() { + showProgressDialog(); + String userText = inputField.getText().toString().trim(); + if (mVerificationByEmail && !Patterns.EMAIL_ADDRESS.matcher(userText).matches()) { + inputField.setText(""); + ViewUtils.temporaryError(inputField, getString(R.string.error_invalid_email)); + return; + } else if (!mVerificationByEmail && !Patterns.PHONE.matcher(userText).matches()) { + inputField.setText(""); + ViewUtils.temporaryError(inputField, getString(R.string.error_invalid_phone)); + return; + } + /*if (!mVerificationByEmail){ // sms + initData(); + }else{*//* + Logger.toast(R.string.success_verification_mail); + //}*/ + optionsRadioGroup.setEnabled(false); + PoinilaNetService.requestVerificationCode(mVerificationByEmail, userText); + } + + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/notification/NCollectionListFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/notification/NCollectionListFragment.java new file mode 100755 index 0000000..0f11c17 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/notification/NCollectionListFragment.java @@ -0,0 +1,89 @@ +package com.shaya.poinila.android.presentation.view.fragments.notification; + +import android.support.v7.widget.RecyclerView; +import android.util.Log; +import android.view.View; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewProvider; +import com.shaya.poinila.android.presentation.view.fragments.CollectionListFragment; +import com.shaya.poinila.android.presentation.view.fragments.ListBusFragment; +import com.shaya.poinila.android.presentation.viewholder.EditableCollectionViewHolder; +import com.shaya.poinila.android.presentation.viewholder.notification.NEditableCollectionViewHolder; + +import java.util.List; + +import data.event.BaseEvent; +import data.model.Collection; + +import static android.support.v7.widget.LinearLayoutManager.VERTICAL; +import static com.shaya.poinila.android.util.ResourceUtils.getInteger; + +/** + * Created by iran on 6/14/2016. + */ +public class NCollectionListFragment extends CollectionListFragment { + + List mData; + + @Override + protected void initUI() { +// super.initUI(); + + mRecyclerView = new RecyclerViewProvider(mRecyclerView). + setAdapter(getRecyclerViewAdapter()). + setGridLayoutManager(VERTICAL, getInteger(R.integer.column_count)). + bindViewToAdapter(); + + if(mData != null){ + getRecyclerViewAdapter().addItems(mData); + } + } + + public static NCollectionListFragment newInstance(List list){ + NCollectionListFragment fragment = new NCollectionListFragment(); + fragment.mData = list; + return fragment; + } + + @Override + protected RecyclerView.OnScrollListener getRecyclerViewListener() { + return null; + } + + @Override + public void requestForMoreData() { + + } + + @Override + public RecyclerViewAdapter createAndReturnRVAdapter() { + return new RecyclerViewAdapter(getActivity(), R.layout.collection_editable) { + @Override + protected NEditableCollectionViewHolder getProperViewHolder(View v, int viewType) { + return new NEditableCollectionViewHolder(v, BaseEvent.ReceiverName.CollectionListFragment); + } + }; + } + + @Override + protected boolean sendsRequestAutomatically() { + return false; + } + + @Override + protected void requestInitialData() { + + } + + @Override + public boolean mustShowProgressView() { + return false; + } + + @Override + public int getLayoutID() { + return R.layout.recycler_view_full; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/fragments/notification/NPostListFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/notification/NPostListFragment.java new file mode 100755 index 0000000..37f2e88 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/fragments/notification/NPostListFragment.java @@ -0,0 +1,145 @@ +package com.shaya.poinila.android.presentation.view.fragments.notification; + +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.StaggeredGridLayoutManager; +import android.view.View; + +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewAdapter; +import com.shaya.poinila.android.presentation.presenter.RecyclerViewProvider; +import com.shaya.poinila.android.presentation.uievent.CollectionClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.MemberClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.PostClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.SuggestionPosts; +import com.shaya.poinila.android.presentation.view.fragments.ListBusFragment; +import com.shaya.poinila.android.presentation.viewholder.BaseViewHolder; +import com.shaya.poinila.android.presentation.viewholder.DashboardPostViewHolder; +import com.shaya.poinila.android.presentation.viewholder.PostViewHolder; +import com.shaya.poinila.android.utils.PushNotificationUtils; +import com.squareup.otto.Subscribe; + +import java.util.List; + +import data.PoinilaNetService; +import data.event.BaseEvent; +import data.model.Collection; +import data.model.Member; +import data.model.Post; + +/** + * Created by iran on 6/15/2016. + */ +public class NPostListFragment extends ListBusFragment { + + private List mData; + private String ids; + private PushNotificationUtils.NOTIFICATION_TYPE type; + + public static NPostListFragment newInstance(List list, PushNotificationUtils.NOTIFICATION_TYPE type){ + NPostListFragment fragment = new NPostListFragment(); + fragment.mData = list; + fragment.type = type; + return fragment; + } + + public static NPostListFragment newInstance(String ids, PushNotificationUtils.NOTIFICATION_TYPE type){ + NPostListFragment fragment = new NPostListFragment(); + fragment.ids = ids; + fragment.type = type; + return fragment; + } + + @Override + protected void initUI() { +// super.initUI(); + + mRecyclerView = new RecyclerViewProvider(mRecyclerView). + setStaggeredLayoutManager(StaggeredGridLayoutManager.VERTICAL, + getResources().getInteger(R.integer.column_count)). + setAdapter(getRecyclerViewAdapter()). + bindViewToAdapter(); + + if(mData != null) + getRecyclerViewAdapter().addItems(mData); + + } + + @Override + public void onStart() { + super.onStart(); + } + + @Override + protected RecyclerView.OnScrollListener getRecyclerViewListener() { + return null; + } + + @Override + public void requestForMoreData() { + + } + + @Override + public RecyclerViewAdapter createAndReturnRVAdapter() { + + return new RecyclerViewAdapter(getActivity(), R.layout.post_dashboard) { + @Override + protected DashboardPostViewHolder getProperViewHolder(View v, int viewType) { + return new DashboardPostViewHolder(v, BaseEvent.ReceiverName.NPostListFragment); + } + }; + + } + + + + @Override + protected boolean sendsRequestAutomatically() { + return true; + } + + @Override + protected void requestInitialData() { + + if(!type.equals(PushNotificationUtils.NOTIFICATION_TYPE.POST_SUGGESTION)) return; + + PoinilaNetService.getSuggestedPosts(ids); + + } + + @Override + public boolean mustShowProgressView() { + return false; + } + + @Override + public int getLayoutID() { + return R.layout.recycler_view_full; + } + + @Subscribe + public void onPostClickedEvent(PostClickedUIEvent event) { + if (event.receiverName != BaseEvent.ReceiverName.NPostListFragment) return; + PageChanger.goToPost(getActivity(), getRecyclerViewAdapter().getItem(event.adapterPosition)); + } + + @Subscribe + public void onProfilePicClickedEvent(MemberClickedUIEvent event) { + if (event.receiverName != BaseEvent.ReceiverName.NPostListFragment) return; + Member member =getRecyclerViewAdapter().getItem(event.adapterPosition).author; + PageChanger.goToProfile(getActivity(), member); + } + + @Subscribe + public void onPostCollectionClickedEvent(CollectionClickedUIEvent event) { + if (event.receiverName != BaseEvent.ReceiverName.NPostListFragment) return; + Collection collection = getRecyclerViewAdapter().getItem(event.adapterPosition).collection; + PageChanger.goToCollection(getActivity(), collection); + } + + @Subscribe + public void onSuggestionPosts(SuggestionPosts event){ + getRecyclerViewAdapter().addItems(event.posts); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/help/Help.java b/src/main/java/com/shaya/poinila/android/presentation/view/help/Help.java new file mode 100755 index 0000000..fda793b --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/help/Help.java @@ -0,0 +1,119 @@ +package com.shaya.poinila.android.presentation.view.help; + +import android.app.Activity; +import android.app.Dialog; +import android.graphics.Rect; +import android.support.v7.app.AlertDialog; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.fragments.BaseFragment; +import com.shaya.poinila.android.presentation.view.help.masks.BaseMaskView; +import com.shaya.poinila.android.presentation.view.help.masks.CollectionMaskView; +import com.shaya.poinila.android.presentation.view.help.masks.CreateMaskView; +import com.shaya.poinila.android.presentation.view.help.masks.DashboardMaskView; +import com.shaya.poinila.android.presentation.view.help.masks.PostRelatedPostMaskView; +import com.shaya.poinila.android.presentation.view.help.masks.PostsOfCollectionMaskView; +import com.shaya.poinila.android.presentation.view.help.masks.ProfileMaskView; + +/** + * Created by iran on 5/23/2016. + */ +public class Help { + private static Help instance = null; + private boolean showingHelp = false; + private BaseFragment mPage; + + private Dialog help; + + private Help(){ + + } + + public static Help getInstance(){ + if(instance == null){ + instance = new Help(); + } + return instance; + } + + public void showDashboardHelp(Activity activity, View itemView){ + + DashboardMaskView helpView = new DashboardMaskView(activity, itemView); + initDialog(activity, helpView); + + } + + public void showPostRelatedPostsHelp(Activity activity, View itemView){ + + PostRelatedPostMaskView helpView = new PostRelatedPostMaskView(activity, itemView); + initDialog(activity, helpView); + + } + + public void showFollowedCollectionHelp(Activity activity, View itemView){ + + CollectionMaskView helpView = new CollectionMaskView(activity, itemView); + initDialog(activity, helpView); + } + + public void showProfileHelp(Activity activity, View itemView){ + + ProfileMaskView helpView = new ProfileMaskView(activity, itemView); + initDialog(activity, helpView); + + } + + public void showNewPostHelp(Activity activity, View itemView){ + + CreateMaskView helpView = new CreateMaskView(activity, itemView); + initDialog(activity, helpView); + } + + public void showPostsOfCollectionHelp(Activity activity, View itemView){ + + PostsOfCollectionMaskView helpView = new PostsOfCollectionMaskView(activity, itemView); + initDialog(activity, helpView); + } + + private void initDialog(Activity activity, BaseMaskView helpView){ + if(!isShowingHelp()){ + + helpView.setStatusBarHeight(getStatusBarHeight(activity.getWindow())); + + help = new Dialog(activity, android.R.style.Theme_Translucent_NoTitleBar); + help.requestWindowFeature(Window.FEATURE_NO_TITLE); + help.setContentView(helpView); + help.getWindow().setBackgroundDrawableResource(android.R.color.transparent); + help.getWindow().setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT); + help.show(); + + showingHelp = true; + + helpView.setOnNextBtnListener(new OnNextButtonListener() { + @Override + public void onClick(View view) { + help.dismiss(); + showingHelp = false; + } + }); + } + + } + + public boolean isShowingHelp(){ + return showingHelp; + } + + private int getStatusBarHeight(Window window){ + Rect rectangle = new Rect(); + window.getDecorView().getWindowVisibleDisplayFrame(rectangle); + return rectangle.top; + } + + + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/help/OnNextButtonListener.java b/src/main/java/com/shaya/poinila/android/presentation/view/help/OnNextButtonListener.java new file mode 100755 index 0000000..a67a5e2 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/help/OnNextButtonListener.java @@ -0,0 +1,11 @@ +package com.shaya.poinila.android.presentation.view.help; + +import android.app.Dialog; +import android.view.View; + +/** + * Created by iran on 5/24/2016. + */ +public interface OnNextButtonListener { + public void onClick(View view); +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/help/fragments/CreateHelpFragment.java b/src/main/java/com/shaya/poinila/android/presentation/view/help/fragments/CreateHelpFragment.java new file mode 100755 index 0000000..7d1ce31 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/help/fragments/CreateHelpFragment.java @@ -0,0 +1,57 @@ +package com.shaya.poinila.android.presentation.view.help.fragments; + +import android.app.DialogFragment; +import android.app.FragmentManager; +import android.app.FragmentTransaction; +import android.graphics.drawable.ColorDrawable; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.help.masks.CreateMaskView; + +/** + * Created by iran on 5/24/2016. + */ +public class CreateHelpFragment extends DialogFragment { + + private View view; + + public CreateHelpFragment(){ + + } + + public static CreateHelpFragment newInstance(View view){ + CreateHelpFragment dialogFragment = new CreateHelpFragment(); + dialogFragment.view = view; + return dialogFragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE); + getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(getResources().getColor(R.color.gray_transparent))); + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return view; + } + + public void show(FragmentManager manager) { +// mDismissed = false; +// mShownByMe = true; + FragmentTransaction ft = manager.beginTransaction(); + ft.add(this, getClass().getName()); + ft.commit(); + } + + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/help/masks/BaseMaskView.java b/src/main/java/com/shaya/poinila/android/presentation/view/help/masks/BaseMaskView.java new file mode 100755 index 0000000..bfa5a29 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/help/masks/BaseMaskView.java @@ -0,0 +1,113 @@ +package com.shaya.poinila.android.presentation.view.help.masks; + +import android.app.Dialog; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.RectF; +import android.view.View; +import android.view.Window; +import android.widget.Button; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.help.OnNextButtonListener; + +/** + * Created by iran on 5/24/2016. + */ +public abstract class BaseMaskView extends RelativeLayout implements View.OnClickListener { + + protected Button nextBtn; + protected TextView descView; + + private int location[] = new int[2]; + private boolean hasLocation = false; + + protected OnNextButtonListener btnListener; + + protected Paint transparentPaint; + private View itemView; + + private int statusBarHeight = 0; + + public BaseMaskView(Context context, View itemView) { + super(context); + + inflate(context, getLayoutResource(), this); + + descView = (TextView)findViewById(getDescViewId()); + nextBtn = (Button)findViewById(getNextBtnId()); + nextBtn.setOnClickListener(this); + init(); + + this.itemView = itemView; + + transparentPaint = new Paint(); + transparentPaint.setColor(getResources().getColor(android.R.color.transparent)); + transparentPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + + } + + protected abstract void init(); + protected abstract int getLayoutResource(); + protected abstract int getDescViewId(); + protected abstract int getNextBtnId(); + + + public BaseMaskView setOnNextBtnListener(OnNextButtonListener btnListener) { + this.btnListener = btnListener; + return this; + } + + @Override + protected void dispatchDraw(Canvas canvas) { + + if(location == null || itemView == null) return; + + Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); + Canvas osCanvas = new Canvas(bitmap); + + RectF outerRectangle = new RectF(0, 0, getWidth(), getHeight()); + + Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + paint.setColor(getResources().getColor(R.color.help_bg)); + osCanvas.drawRect(outerRectangle, paint); + + paint.setColor(Color.TRANSPARENT); + paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT)); +// float centerX = getWidth() / 2; +// float centerY = getHeight() / 2; +// float radius = 100; + + + RectF itemRect = new RectF(location[0], location[1] - statusBarHeight, location[0] + itemView.getWidth(), (location[1] + itemView.getHeight()) - statusBarHeight); + osCanvas.drawRect(itemRect, paint); + canvas.drawBitmap(bitmap, 0, 0, null); + + super.dispatchDraw(canvas); + + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + if(!hasLocation && itemView != null){ + itemView.getLocationInWindow(location); + hasLocation = true; + } + + } + + public BaseMaskView setStatusBarHeight(int statusBarHeight) { + this.statusBarHeight = statusBarHeight; + return this; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/help/masks/CollectionMaskView.java b/src/main/java/com/shaya/poinila/android/presentation/view/help/masks/CollectionMaskView.java new file mode 100755 index 0000000..7120c91 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/help/masks/CollectionMaskView.java @@ -0,0 +1,51 @@ +package com.shaya.poinila.android.presentation.view.help.masks; + +import android.content.Context; +import android.view.View; + +import com.shaya.poinila.android.presentation.R; + +/** + * Created by iran on 5/24/2016. + */ +public class CollectionMaskView extends BaseMaskView implements View.OnClickListener { + + + protected int level = 1; + + + public CollectionMaskView(Context context, View itemView) { + super(context, itemView); + } + + @Override + protected void init() { + // TODO + } + + @Override + protected int getLayoutResource() { + return R.layout.mask_followed_collections; + } + + @Override + protected int getDescViewId() { + return R.id.mask_followed_collections_description; + } + + @Override + protected int getNextBtnId() { + return R.id.mask_followed_collections_btn; + } + + @Override + public void onClick(View v) { + level++; + switch (level){ + default: + btnListener.onClick(nextBtn); + } + + } + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/help/masks/CreateMaskView.java b/src/main/java/com/shaya/poinila/android/presentation/view/help/masks/CreateMaskView.java new file mode 100755 index 0000000..bbf2f13 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/help/masks/CreateMaskView.java @@ -0,0 +1,54 @@ +package com.shaya.poinila.android.presentation.view.help.masks; + +import android.content.Context; +import android.view.View; + +import com.shaya.poinila.android.presentation.R; + +/** + * Created by iran on 5/24/2016. + */ +public class CreateMaskView extends BaseMaskView{ + protected int level = 1; + + + public CreateMaskView(Context context, View itemView) { + super(context, itemView); + } + + @Override + protected void init() { + descView.setText(R.string.help_create_description); + } + + @Override + protected int getLayoutResource() { + return R.layout.mask_create; + } + + @Override + protected int getDescViewId() { + return R.id.mask_create_description; + } + + @Override + protected int getNextBtnId() { + return R.id.mask_create_btn; + } + + @Override + public void onClick(View v) { + + level++; + + switch (level){ + default: + if(btnListener != null) + btnListener.onClick(nextBtn); + } + + + } + + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/help/masks/DashboardMaskView.java b/src/main/java/com/shaya/poinila/android/presentation/view/help/masks/DashboardMaskView.java new file mode 100755 index 0000000..0678a41 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/help/masks/DashboardMaskView.java @@ -0,0 +1,60 @@ +package com.shaya.poinila.android.presentation.view.help.masks; + +import android.content.Context; +import android.view.View; + +import com.shaya.poinila.android.presentation.R; + +/** + * Created by iran on 5/24/2016. + */ +public class DashboardMaskView extends BaseMaskView{ + + protected int level = 1; + + public DashboardMaskView(Context context, View itemView) { + super(context, itemView); + } + + @Override + protected void init() { + //TODO + } + + @Override + protected int getLayoutResource() { + return R.layout.mask_dashboard; + } + + @Override + protected int getDescViewId() { + return R.id.mask_dashboard_description; + } + + @Override + protected int getNextBtnId() { + return R.id.mask_dashboard_btn; + } + + @Override + public void onWindowFocusChanged(boolean hasWindowFocus) { + super.onWindowFocusChanged(hasWindowFocus); + } + + + @Override + public void onClick(View v) { + + level++; + + switch (level){ + case 2: + descView.setText(R.string.help_dashboard_level_two_description); + break; + default: + btnListener.onClick(nextBtn); + } + + + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/help/masks/PostRelatedPostMaskView.java b/src/main/java/com/shaya/poinila/android/presentation/view/help/masks/PostRelatedPostMaskView.java new file mode 100755 index 0000000..982af3a --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/help/masks/PostRelatedPostMaskView.java @@ -0,0 +1,53 @@ +package com.shaya.poinila.android.presentation.view.help.masks; + +import android.content.Context; +import android.view.View; + +import com.shaya.poinila.android.presentation.R; + +/** + * Created by iran on 5/24/2016. + */ +public class PostRelatedPostMaskView extends BaseMaskView { + + protected int level = 1; + + public PostRelatedPostMaskView(Context context, View itemView) { + super(context, itemView); + } + + @Override + protected void init() { + // TODO + } + + @Override + protected int getLayoutResource() { + return R.layout.mask_post_related_posts; + } + + @Override + protected int getDescViewId() { + return R.id.mask_post_related_posts_description; + } + + @Override + protected int getNextBtnId() { + return R.id.mask_post_related_posts_btn; + } + + + @Override + public void onClick(View v) { + + level++; + + switch (level){ + default: + btnListener.onClick(nextBtn); + } + + + } + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/help/masks/PostsOfCollectionMaskView.java b/src/main/java/com/shaya/poinila/android/presentation/view/help/masks/PostsOfCollectionMaskView.java new file mode 100755 index 0000000..e558ac3 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/help/masks/PostsOfCollectionMaskView.java @@ -0,0 +1,48 @@ +package com.shaya.poinila.android.presentation.view.help.masks; + +import android.content.Context; +import android.view.View; + +import com.shaya.poinila.android.presentation.R; + +/** + * Created by iran on 5/29/2016. + */ +public class PostsOfCollectionMaskView extends BaseMaskView { + + protected int level = 1; + + public PostsOfCollectionMaskView(Context context, View itemView) { + super(context, itemView); + } + + @Override + protected void init() { + // TODO + } + + @Override + protected int getLayoutResource() { + return R.layout.mask_posts_of_collection; + } + + @Override + protected int getDescViewId() { + return R.id.mask_posts_of_collection_description; + } + + @Override + protected int getNextBtnId() { + return R.id.mask_posts_of_collection_btn; + } + + @Override + public void onClick(View v) { + level++; + switch (level){ + default: + btnListener.onClick(nextBtn); + } + + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/help/masks/ProfileMaskView.java b/src/main/java/com/shaya/poinila/android/presentation/view/help/masks/ProfileMaskView.java new file mode 100755 index 0000000..25a6598 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/help/masks/ProfileMaskView.java @@ -0,0 +1,52 @@ +package com.shaya.poinila.android.presentation.view.help.masks; + +import android.content.Context; +import android.view.View; + +import com.shaya.poinila.android.presentation.R; + +/** + * Created by iran on 7/11/2016. + */ +public class ProfileMaskView extends BaseMaskView { + protected int level = 1; + + + public ProfileMaskView(Context context, View itemView) { + super(context, itemView); + } + + @Override + protected void init() { + descView.setText(R.string.help_profile_description); + } + + @Override + protected int getLayoutResource() { + return R.layout.mask_profile; + } + + @Override + protected int getDescViewId() { + return R.id.mask_create_description; + } + + @Override + protected int getNextBtnId() { + return R.id.mask_create_btn; + } + + @Override + public void onClick(View v) { + + level++; + + switch (level){ + default: + if(btnListener != null) + btnListener.onClick(nextBtn); + } + + + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/video/PonilaMediaController.java b/src/main/java/com/shaya/poinila/android/presentation/view/video/PonilaMediaController.java new file mode 100755 index 0000000..c46ce29 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/video/PonilaMediaController.java @@ -0,0 +1,640 @@ +package com.shaya.poinila.android.presentation.view.video; + +import android.content.Context; +import android.os.Handler; +import android.os.Message; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageButton; +import android.widget.ProgressBar; +import android.widget.SeekBar; +import android.widget.TextView; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.utils.Utils; + +import java.lang.ref.WeakReference; +import java.util.Formatter; +import java.util.Locale; + +/** + * Created by iran on 8/11/2016. + */ +public class PonilaMediaController extends FrameLayout { + private static final String TAG = "PonilaMediaController"; + + private MediaPlayerControl mPlayer; + private Context mContext; + private ViewGroup mAnchor; + private View mRoot; + private ProgressBar mProgress; + private TextView mEndTime, mCurrentTime; + private boolean mShowing; + private boolean mDragging; + private static final int sDefaultTimeout = 3000; + private static final int FADE_OUT = 1; + private static final int SHOW_PROGRESS = 2; + private boolean mUseFastForward; + private boolean mFromXml; + private boolean mListenersSet; + private OnClickListener mNextListener, mPrevListener; + StringBuilder mFormatBuilder; + Formatter mFormatter; + private ImageButton mPauseButton; +// private ImageButton mFfwdButton; +// private ImageButton mRewButton; +// private ImageButton mNextButton; +// private ImageButton mPrevButton; + private ImageButton mFullscreenButton; + private Handler mHandler = new MessageHandler(this); + + public PonilaMediaController(Context context, AttributeSet attrs) { + super(context, attrs); + mRoot = null; + mContext = context; + mUseFastForward = true; + mFromXml = true; + + Log.i(TAG, TAG); + + setBackgroundDrawable(null); + } + + public PonilaMediaController(Context context, boolean useFastForward) { + super(context); + mContext = context; + mUseFastForward = useFastForward; + + Log.i(TAG, TAG); + setBackgroundDrawable(null); + + } + + public PonilaMediaController(Context context) { + this(context, true); + + Log.i(TAG, TAG); + setBackgroundDrawable(null); + + } + + @Override + public void onFinishInflate() { +// super.onFinishInflate(); + if (mRoot != null) + initControllerView(mRoot); + } + + public void setMediaPlayer(MediaPlayerControl player) { + mPlayer = player; + updatePausePlay(); + updateFullScreen(); + } + + /** + * Set the view that acts as the anchor for the control view. + * This can for example be a VideoView, or your Activity's main view. + * @param view The view to which to anchor the controller when it is visible. + */ + public void setAnchorView(ViewGroup view) { + mAnchor = view; + + LayoutParams frameParams = new LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ); + + removeAllViews(); + View v = makeControllerView(); + addView(v, frameParams); + } + + /** + * Create the view that holds the widgets that control playback. + * Derived classes can override this to create their own. + * @return The controller view. + * @hide This doesn't work as advertised + */ + protected View makeControllerView() { + LayoutInflater inflate = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mRoot = inflate.inflate(R.layout.media_controller, null); + + initControllerView(mRoot); + + return mRoot; + } + + private void initControllerView(View v) { + mPauseButton = (ImageButton) v.findViewById(R.id.pause); + if (mPauseButton != null) { + mPauseButton.requestFocus(); + mPauseButton.setOnClickListener(mPauseListener); + } + + mFullscreenButton = (ImageButton) v.findViewById(R.id.fullscreen); + if (mFullscreenButton != null) { + mFullscreenButton.requestFocus(); + mFullscreenButton.setOnClickListener(mFullscreenListener); + } + +// mFfwdButton = (ImageButton) v.findViewById(R.id.ffwd); +// if (mFfwdButton != null) { +// mFfwdButton.setOnClickListener(mFfwdListener); +// if (!mFromXml) { +// mFfwdButton.setVisibility(mUseFastForward ? View.VISIBLE : View.GONE); +// } +// } +// +// mRewButton = (ImageButton) v.findViewById(R.id.rew); +// if (mRewButton != null) { +// mRewButton.setOnClickListener(mRewListener); +// if (!mFromXml) { +// mRewButton.setVisibility(mUseFastForward ? View.VISIBLE : View.GONE); +// } +// } +// +// // By default these are hidden. They will be enabled when setPrevNextListeners() is called +// mNextButton = (ImageButton) v.findViewById(R.id.next); +// if (mNextButton != null && !mFromXml && !mListenersSet) { +// mNextButton.setVisibility(View.GONE); +// } +// mPrevButton = (ImageButton) v.findViewById(R.id.prev); +// if (mPrevButton != null && !mFromXml && !mListenersSet) { +// mPrevButton.setVisibility(View.GONE); +// } + + mProgress = (SeekBar) v.findViewById(R.id.mediacontroller_progress); + if (mProgress != null) { + if (mProgress instanceof SeekBar) { + SeekBar seeker = (SeekBar) mProgress; + seeker.setOnSeekBarChangeListener(mSeekListener); + } + mProgress.setMax(1000); + } + + mEndTime = (TextView) v.findViewById(R.id.time); + mCurrentTime = (TextView) v.findViewById(R.id.time_current); + mFormatBuilder = new StringBuilder(); + mFormatter = new Formatter(mFormatBuilder, Locale.getDefault()); + + installPrevNextListeners(); + } + + /** + * Show the controller on screen. It will go away + * automatically after 3 seconds of inactivity. + */ + public void show() { + show(sDefaultTimeout); + } + + /** + * Disable pause or seek buttons if the stream cannot be paused or seeked. + * This requires the control interface to be a MediaPlayerControlExt + */ + private void disableUnsupportedButtons() { + if (mPlayer == null) { + return; + } + + try { + if (mPauseButton != null && !mPlayer.canPause()) { + mPauseButton.setEnabled(false); + } +// if (mRewButton != null && !mPlayer.canSeekBackward()) { +// mRewButton.setEnabled(false); +// } +// if (mFfwdButton != null && !mPlayer.canSeekForward()) { +// mFfwdButton.setEnabled(false); +// } + } catch (IncompatibleClassChangeError ex) { + // We were given an old version of the interface, that doesn't have + // the canPause/canSeekXYZ methods. This is OK, it just means we + // assume the media can be paused and seeked, and so we don't disable + // the buttons. + } + } + + /** + * Show the controller on screen. It will go away + * automatically after 'timeout' milliseconds of inactivity. + * @param timeout The timeout in milliseconds. Use 0 to show + * the controller until hide() is called. + */ + public void show(int timeout) { + if (!mShowing && mAnchor != null) { + setProgress(); + if (mPauseButton != null) { + mPauseButton.requestFocus(); + } + disableUnsupportedButtons(); + + LayoutParams tlp = new LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT, + Gravity.BOTTOM + ); + + mAnchor.addView(this, tlp); + mShowing = true; + } + updatePausePlay(); + updateFullScreen(); + + // cause the progress bar to be updated even if mShowing + // was already true. This happens, for example, if we're + // paused with the progress bar showing the user hits play. + mHandler.sendEmptyMessage(SHOW_PROGRESS); + + Message msg = mHandler.obtainMessage(FADE_OUT); + if (timeout != 0) { + mHandler.removeMessages(FADE_OUT); + mHandler.sendMessageDelayed(msg, timeout); + } + } + + public boolean isShowing() { + return mShowing; + } + + /** + * Remove the controller from the screen. + */ + public void hide() { + if (mAnchor == null) { + return; + } + + try { + mAnchor.removeView(this); + mHandler.removeMessages(SHOW_PROGRESS); + } catch (IllegalArgumentException ex) { + Log.w("MediaController", "already removed"); + } + mShowing = false; + } + + private String stringForTime(int timeMs) { + int totalSeconds = timeMs / 1000; + + int seconds = totalSeconds % 60; + int minutes = (totalSeconds / 60) % 60; + int hours = totalSeconds / 3600; + + mFormatBuilder.setLength(0); + if (hours > 0) { + return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString(); + } else { + return mFormatter.format("%02d:%02d", minutes, seconds).toString(); + } + } + + private int setProgress() { + if (mPlayer == null || mDragging) { + return 0; + } + + int position = mPlayer.getCurrentPosition(); + int duration = mPlayer.getDuration(); + if (mProgress != null) { + if (duration > 0) { + // use long to avoid overflow + long pos = 1000L * position / duration; + mProgress.setProgress( (int) pos); + } + int percent = mPlayer.getBufferPercentage(); + mProgress.setSecondaryProgress(percent * 10); + } + + if (mEndTime != null) + mEndTime.setText(stringForTime(duration)); + if (mCurrentTime != null) + mCurrentTime.setText(stringForTime(position)); + + return position; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + show(sDefaultTimeout); + return true; + } + + @Override + public boolean onTrackballEvent(MotionEvent ev) { + show(sDefaultTimeout); + return false; + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + if (mPlayer == null) { + return true; + } + + int keyCode = event.getKeyCode(); + final boolean uniqueDown = event.getRepeatCount() == 0 + && event.getAction() == KeyEvent.ACTION_DOWN; + if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK + || keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE + || keyCode == KeyEvent.KEYCODE_SPACE) { + if (uniqueDown) { + doPauseResume(); + show(sDefaultTimeout); + if (mPauseButton != null) { + mPauseButton.requestFocus(); + } + } + return true; + } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) { + if (uniqueDown && !mPlayer.isPlaying()) { + mPlayer.start(); + updatePausePlay(); + show(sDefaultTimeout); + } + return true; + } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP + || keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) { + if (uniqueDown && mPlayer.isPlaying()) { + mPlayer.pause(); + updatePausePlay(); + show(sDefaultTimeout); + } + return true; + } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN + || keyCode == KeyEvent.KEYCODE_VOLUME_UP + || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) { + // don't show the controls for volume adjustment + return super.dispatchKeyEvent(event); + } else if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU) { + if (uniqueDown) { + hide(); + } + return true; + } + + show(sDefaultTimeout); + return super.dispatchKeyEvent(event); + } + + private OnClickListener mPauseListener = new OnClickListener() { + public void onClick(View v) { + doPauseResume(); + show(sDefaultTimeout); + } + }; + + private OnClickListener mFullscreenListener = new OnClickListener() { + public void onClick(View v) { + doToggleFullscreen(); + if(Utils.isEnabledAutoRotate()) + show(sDefaultTimeout); + } + }; + + public void updatePausePlay() { + if (mRoot == null || mPauseButton == null || mPlayer == null) { + return; + } + + if (mPlayer.isPlaying()) { + mPauseButton.setImageResource(R.drawable.ic_media_pause); + } else { + mPauseButton.setImageResource(R.drawable.ic_media_play); + } + } + + public void updateFullScreen() { + if (mRoot == null || mFullscreenButton == null || mPlayer == null) { + return; + } + + if (mPlayer.isFullScreen()) { + mFullscreenButton.setImageResource(R.drawable.ic_media_fullscreen_shrink); + } + else { + mFullscreenButton.setImageResource(R.drawable.ic_media_fullscreen_stretch); + } + } + + private void doPauseResume() { + if (mPlayer == null) { + return; + } + + if (mPlayer.isPlaying()) { + mPlayer.pause(); + } else { + mPlayer.start(); + } + updatePausePlay(); + } + + private void doToggleFullscreen() { + if (mPlayer == null) { + return; + } + + mPlayer.toggleFullScreen(); + } + + // There are two scenarios that can trigger the seekbar listener to trigger: + // + // The first is the user using the touchpad to adjust the posititon of the + // seekbar's thumb. In this case onStartTrackingTouch is called followed by + // a number of onProgressChanged notifications, concluded by onStopTrackingTouch. + // We're setting the field "mDragging" to true for the duration of the dragging + // session to avoid jumps in the position in case of ongoing playback. + // + // The second scenario involves the user operating the scroll ball, in this + // case there WON'T BE onStartTrackingTouch/onStopTrackingTouch notifications, + // we will simply apply the updated position without suspending regular updates. + private SeekBar.OnSeekBarChangeListener mSeekListener = new SeekBar.OnSeekBarChangeListener() { + public void onStartTrackingTouch(SeekBar bar) { + show(3600000); + + mDragging = true; + + // By removing these pending progress messages we make sure + // that a) we won't update the progress while the user adjusts + // the seekbar and b) once the user is done dragging the thumb + // we will post one of these messages to the queue again and + // this ensures that there will be exactly one message queued up. + mHandler.removeMessages(SHOW_PROGRESS); + } + + public void onProgressChanged(SeekBar bar, int progress, boolean fromuser) { + if (mPlayer == null) { + return; + } + + if (!fromuser) { + // We're not interested in programmatically generated changes to + // the progress bar's position. + return; + } + + long duration = mPlayer.getDuration(); + long newposition = (duration * progress) / 1000L; + mPlayer.seekTo( (int) newposition); + if (mCurrentTime != null) + mCurrentTime.setText(stringForTime( (int) newposition)); + } + + public void onStopTrackingTouch(SeekBar bar) { + mDragging = false; + setProgress(); + updatePausePlay(); + show(sDefaultTimeout); + + // Ensure that progress is properly updated in the future, + // the call to show() does not guarantee this because it is a + // no-op if we are already showing. + mHandler.sendEmptyMessage(SHOW_PROGRESS); + } + }; + + @Override + public void setEnabled(boolean enabled) { + if (mPauseButton != null) { + mPauseButton.setEnabled(enabled); + } +// if (mFfwdButton != null) { +// mFfwdButton.setEnabled(enabled); +// } +// if (mRewButton != null) { +// mRewButton.setEnabled(enabled); +// } +// if (mNextButton != null) { +// mNextButton.setEnabled(enabled && mNextListener != null); +// } +// if (mPrevButton != null) { +// mPrevButton.setEnabled(enabled && mPrevListener != null); +// } + if (mProgress != null) { + mProgress.setEnabled(enabled); + } + disableUnsupportedButtons(); + super.setEnabled(enabled); + } + + public void setSecondaryProgress(int precent){ + mProgress.setSecondaryProgress(precent); + } + + public int getSecondaryProgress(){ + return mProgress.getSecondaryProgress(); + } + + private OnClickListener mRewListener = new OnClickListener() { + public void onClick(View v) { + + if (mPlayer == null) { + return; + } + + int pos = mPlayer.getCurrentPosition(); + pos -= 5000; // milliseconds + mPlayer.seekTo(pos); + setProgress(); + + show(sDefaultTimeout); + } + }; + + private OnClickListener mFfwdListener = new OnClickListener() { + public void onClick(View v) { + if (mPlayer == null) { + return; + } + + int pos = mPlayer.getCurrentPosition(); + pos += 15000; // milliseconds + mPlayer.seekTo(pos); + setProgress(); + + show(sDefaultTimeout); + } + }; + + private void installPrevNextListeners() { +// if (mNextButton != null) { +// mNextButton.setOnClickListener(mNextListener); +// mNextButton.setEnabled(mNextListener != null); +// } +// +// if (mPrevButton != null) { +// mPrevButton.setOnClickListener(mPrevListener); +// mPrevButton.setEnabled(mPrevListener != null); +// } + } + + public void setPrevNextListeners(OnClickListener next, OnClickListener prev) { + mNextListener = next; + mPrevListener = prev; + mListenersSet = true; + + if (mRoot != null) { + installPrevNextListeners(); + +// if (mNextButton != null && !mFromXml) { +// mNextButton.setVisibility(View.VISIBLE); +// } +// if (mPrevButton != null && !mFromXml) { +// mPrevButton.setVisibility(View.VISIBLE); +// } + } + } + + public interface MediaPlayerControl { + void start(); + void pause(); + int getDuration(); + int getCurrentPosition(); + void seekTo(int pos); + boolean isPlaying(); + int getBufferPercentage(); + boolean canPause(); + boolean canSeekBackward(); + boolean canSeekForward(); + boolean isFullScreen(); + void toggleFullScreen(); + } + + private static class MessageHandler extends Handler { + private final WeakReference mView; + + MessageHandler(PonilaMediaController view) { + mView = new WeakReference(view); + } + + @Override + public void handleMessage(Message msg) { + PonilaMediaController view = mView.get(); + if (view == null || view.mPlayer == null) { + return; + } + + int pos; + switch (msg.what) { + case FADE_OUT: + view.hide(); + break; + case SHOW_PROGRESS: + pos = view.setProgress(); + if (!view.mDragging && view.mShowing && view.mPlayer.isPlaying()) { + msg = obtainMessage(SHOW_PROGRESS); + sendMessageDelayed(msg, 1000 - (pos % 1000)); + } + break; + } + } + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/view/video/PonilaVideoView.java b/src/main/java/com/shaya/poinila/android/presentation/view/video/PonilaVideoView.java new file mode 100755 index 0000000..c4edd75 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/view/video/PonilaVideoView.java @@ -0,0 +1,380 @@ +package com.shaya.poinila.android.presentation.view.video; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.PixelFormat; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.media.AudioManager; +import android.media.MediaPlayer; +import android.net.Uri; +import android.os.Bundle; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.presentation.view.costom_view.AspectRatioImageView; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.utils.Utils; +import com.squareup.picasso.Picasso; +import com.squareup.picasso.Target; + +import java.io.IOException; + +import data.model.ImageUrls; + +/** + * Created by iran on 8/11/2016. + */ +public class PonilaVideoView extends LinearLayout + implements + PonilaMediaController.MediaPlayerControl, + SurfaceHolder.Callback, + MediaPlayer.OnPreparedListener, + MediaPlayer.OnErrorListener, + MediaPlayer.OnCompletionListener, + View.OnClickListener{ + + // all possible internal states + private static final int STATE_ERROR = -1; + private static final int STATE_IDLE = 0; + private static final int STATE_PREPARING = 1; + private static final int STATE_PREPARED = 2; + private static final int STATE_PLAYING = 3; + private static final int STATE_PAUSED = 4; + private static final int STATE_PLAYBACK_COMPLETED = 5; + + private int mCurrentState = STATE_IDLE; + private int mTargetState = STATE_IDLE; + + private MediaPlayer mMediaPlayer; + private PonilaMediaController mMediaController; + + private SurfaceView videoSurface; + private SurfaceHolder mVideoHolder; + private Uri videoUri; + private boolean fullScreen = false; + private OnFullScreenListener onFullScreenListener; + private AspectRatioImageView previewView; + private ImageButton largePlayBtn; + private View loading; + private ViewGroup videoOverlay; + private int mCurrentBufferPercentage; + + private MediaPlayer.OnBufferingUpdateListener mBufferingUpdateListener = new MediaPlayer.OnBufferingUpdateListener() { + @Override + public void onBufferingUpdate(MediaPlayer mediaPlayer, int percent) { + mCurrentBufferPercentage = percent; + if(mCurrentBufferPercentage > mMediaController.getSecondaryProgress()) + mMediaController.setSecondaryProgress(mCurrentBufferPercentage); + } + }; + + public PonilaVideoView(Context context) { + super(context); + init(); + } + + public PonilaVideoView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public PonilaVideoView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + private void init(){ + + inflate(getContext(), R.layout.ponila_video_view, this); + + videoSurface = (SurfaceView) findViewById(R.id.videoSurface); + + previewView = (AspectRatioImageView)findViewById(R.id.video_preview); + largePlayBtn = (ImageButton)findViewById(R.id.video_play_btn); + loading = findViewById(R.id.progress_view); + videoOverlay = (ViewGroup) findViewById(R.id.video_overlay); + loading.setVisibility(GONE); + + largePlayBtn.setOnClickListener(this); + + videoSurface.getHolder().addCallback(this); + videoSurface.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); +// videoSurface.setZOrderOnTop(true); + + mMediaController = new PonilaMediaController(getContext()); + + mCurrentState = STATE_IDLE; + mTargetState = STATE_IDLE; + } + + private void openVideo(){ + + if(videoUri == null || mVideoHolder == null)return; + + release(false); + try { + mMediaPlayer = new MediaPlayer(); + mMediaPlayer.setDataSource(getContext(), videoUri); + mMediaPlayer.setDisplay(mVideoHolder); + mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); + mMediaPlayer.setOnPreparedListener(this); + mMediaPlayer.setOnErrorListener(this); + mMediaPlayer.setScreenOnWhilePlaying(true); + mMediaPlayer.setOnCompletionListener(this); + mMediaPlayer.setOnBufferingUpdateListener(mBufferingUpdateListener); + mCurrentBufferPercentage = 0; + mMediaPlayer.prepareAsync(); + mCurrentState = STATE_PREPARING; + attachMediaController(); + } catch (IOException e) { + e.printStackTrace(); + mCurrentState = STATE_ERROR; + mTargetState = STATE_ERROR; + } catch (IllegalStateException e){ + e.printStackTrace(); + mCurrentState = STATE_ERROR; + mTargetState = STATE_ERROR; + } + + } + + private void attachMediaController() { + if (mMediaPlayer != null && mMediaController != null) { + mMediaController.setMediaPlayer(this); + mMediaController.setAnchorView((FrameLayout)findViewById(R.id.videoSurfaceContainer)); +// mMediaController.setEnabled(isInPlaybackState()); + } + } + + public void setVideoPreview(ImageUrls imageUrls){ + ViewUtils.setImage( + previewView, + imageUrls, + ImageUrls.ImageType.POST, + ImageUrls.ImageSize.BIG); + + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if(isInPlaybackState()){ + mMediaController.show(); + } + return false; + } + + // Implement SurfaceHolder.Callback + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + if(mMediaPlayer != null && mTargetState == STATE_PLAYING) + start(); + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + mVideoHolder = holder; +// openVideo(); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + mVideoHolder = null; +// if (mMediaController != null) mMediaController.hide(); + release(true); + } + + // End SurfaceHolder.Callback + + // Implement MediaPlayer.OnPreparedListener + + public void onPrepared(MediaPlayer mp) { + mCurrentState = STATE_PREPARED; + if (mTargetState == STATE_PLAYING) { +// videoSurface.setZOrderOnTop(false); + start(); + loading.setVisibility(GONE); + previewView.setVisibility(GONE); + } + } + // End MediaPlayer.OnPreparedListener + + // Implement VideoMediaController.MediaPlayerControl + @Override + public boolean canPause() { + return true; + } + + @Override + public boolean canSeekBackward() { + return true; + } + + @Override + public boolean canSeekForward() { + return true; + } + + @Override + public boolean isFullScreen() { + return fullScreen; + } + + @Override + public void toggleFullScreen() { + if(!Utils.isEnabledAutoRotate()){ + Logger.toastError(R.string.change_orientation_error); + return; + } + if(onFullScreenListener != null){ + fullScreen = !fullScreen; + onFullScreenListener.stateChanged(); + } + } + + @Override + public int getBufferPercentage() { + if(mMediaPlayer != null){ + return mCurrentBufferPercentage; + } + return 0; + } + + @Override + public int getCurrentPosition() { + return mMediaPlayer.getCurrentPosition(); + } + + @Override + public int getDuration() { + return mMediaPlayer.getDuration(); + } + + @Override + public boolean isPlaying() { + return mMediaPlayer != null && mMediaPlayer.isPlaying(); + } + + @Override + public void pause() { + if (isInPlaybackState()) { + if (mMediaPlayer.isPlaying()) { + mMediaPlayer.pause(); + mCurrentState = STATE_PAUSED; + } + } + mTargetState = STATE_PAUSED; + } + + public void suspend(){ + release(false); + } + + public void resume(){ + openVideo(); + } + + @Override + public void seekTo(int i) { + mMediaPlayer.seekTo(i); + } + + @Override + public void start() { + if (isInPlaybackState()) { + mMediaPlayer.start(); + mCurrentState = STATE_PLAYING; + } + mTargetState = STATE_PLAYING; + } + + private boolean isInPlaybackState() { + return +// mMediaPlayer != null && + mCurrentState != STATE_ERROR && + mCurrentState != STATE_IDLE && + mCurrentState != STATE_PREPARING; + } + + public void setVideoPath(String path){ + videoUri = Uri.parse(path); + requestLayout(); + invalidate(); + } + + private void release(boolean cleartargetstate) { + if (mMediaPlayer != null) { + mMediaPlayer.reset(); + mMediaPlayer.release(); + mMediaPlayer = null; +// mPendingSubtitleTracks.clear(); + mCurrentState = STATE_IDLE; + if (cleartargetstate) { + mTargetState = STATE_IDLE; + } + } + } + + public void setFullScreenMode(boolean status){ + fullScreen = status; + } + + @Override + public boolean onError(MediaPlayer mediaPlayer, int framework_err, int impl_err) { + mCurrentState = STATE_ERROR; + mTargetState = STATE_ERROR; + if(mMediaController != null) + mMediaController.hide(); + Logger.toastError(R.string.play_video_error); + + largePlayBtn.setVisibility(VISIBLE); + loading.setVisibility(GONE); + videoOverlay.setVisibility(VISIBLE); + + return false; + } + + public void setOnFullScreenListener(OnFullScreenListener onFullScreenListener) { + this.onFullScreenListener = onFullScreenListener; + } + + @Override + public void onClick(View view) { + openVideo(); + view.setVisibility(GONE); + loading.setVisibility(VISIBLE); + videoOverlay.setVisibility(GONE); + start(); + } + + @Override + public void onCompletion(MediaPlayer mediaPlayer) { + + mCurrentState = STATE_PLAYBACK_COMPLETED; + mTargetState = STATE_PLAYBACK_COMPLETED; + + if(mMediaController != null) + mMediaController.hide(); + previewView.setVisibility(VISIBLE); + largePlayBtn.setVisibility(VISIBLE); + loading.setVisibility(GONE); + videoOverlay.setVisibility(VISIBLE); + + } + + public interface OnFullScreenListener{ + void stateChanged(); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/AskUserLikesPonilaViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/AskUserLikesPonilaViewHolder.java new file mode 100755 index 0000000..8cf8bcc --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/AskUserLikesPonilaViewHolder.java @@ -0,0 +1,33 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.view.View; +import android.widget.Button; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.fragments.DashboardFragment.DashboardRecyclerViewAdapter.AskIfUserLikesPonila; + +import butterknife.Bind; + +/** + * Created by iran on 1/20/2016. + */ +public class AskUserLikesPonilaViewHolder extends BaseViewHolder { + @Bind(R.id.positive_button) + public Button positiveButton; + @Bind(R.id.negative_button) + public Button negativeButton; + @Bind(R.id.dont_know_button) + public Button dontKnowButton; + + public AskUserLikesPonilaViewHolder(View view) { + super(view); + } + + + @Override + public void fill(AskIfUserLikesPonila askIfUserLikesPonila) { + + } + + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/BaseViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/BaseViewHolder.java new file mode 100755 index 0000000..b9095d4 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/BaseViewHolder.java @@ -0,0 +1,36 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.support.v7.widget.RecyclerView; +import android.view.View; + +import butterknife.ButterKnife; + +/** + * @author Alireza Farahani + * Created by iran on 2015-06-10. + */ +public abstract class BaseViewHolder extends RecyclerView.ViewHolder{ // with butterknife no need + // to implement click listerners + // implements View.OnClickListener{ + protected View rootView; + public BaseViewHolder(View view) { + super(view); + this.rootView = view; + ButterKnife.bind(this, view); + } + + public abstract void fill(T t); + + public static class EmptyViewHolder extends BaseViewHolder{ + + public EmptyViewHolder(View view) { + super(view); + } + + @Override + public void fill(Object o) { + + } + } +} + diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/CheckedCircleViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/CheckedCircleViewHolder.java new file mode 100755 index 0000000..4cf7a12 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/CheckedCircleViewHolder.java @@ -0,0 +1,50 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.view.View; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.TextView; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.CheckBoxClickUIEvent; +import com.shaya.poinila.android.util.BusProvider; + +import butterknife.Bind; +import data.model.Circle; + +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; + +/** + * Created by AlirezaF on 11/16/2015. + */ +public class CheckedCircleViewHolder extends BaseViewHolder { + @Bind(R.id.text) + public TextView textView; + + @Bind(R.id.checkbox) + public CheckBox checkBox; + + public CheckedCircleViewHolder(View view) { + super(view); + } + + private CompoundButton.OnCheckedChangeListener getCheckBoxListener() { + return new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + BusProvider.getBus().post( + new CheckBoxClickUIEvent(isChecked, getAdapterPosition())); + } + }; + } + + public void fill(Circle circle){ + setText(textView, circle.name); + //checkBox.setChecked(interest.selected); + checkBox.setOnCheckedChangeListener(null); + checkBox.setChecked(circle.selected); + itemView.setBackgroundResource(circle.selected ? + R.drawable.bordered_rounded_rect_checked : R.drawable.bordered_rounded_rect); + checkBox.setOnCheckedChangeListener(getCheckBoxListener()); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/CheckedTextViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/CheckedTextViewHolder.java new file mode 100755 index 0000000..a022127 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/CheckedTextViewHolder.java @@ -0,0 +1,56 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.view.View; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.TextView; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.CheckBoxClickUIEvent; +import com.shaya.poinila.android.util.BusProvider; + +import butterknife.Bind; +import data.model.Tag; + +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; + +/** + * Created by iran on 2015-07-11. + */ +public class CheckedTextViewHolder extends BaseViewHolder{ + @Bind(R.id.text) + public TextView textView; + + @Bind(R.id.checkbox) + public CheckBox checkBox; + + public CheckedTextViewHolder(View view) { + super(view); + } + + private CompoundButton.OnCheckedChangeListener getCheckBoxListener(){ + return new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + BusProvider.getBus().post( + new CheckBoxClickUIEvent(isChecked, getAdapterPosition())); + } + }; + } + + public void fill(Tag interest) { + setText(textView, interest.name); + //checkBox.setChecked(interest.selected); + checkBox.setOnCheckedChangeListener(null); + checkBox.setChecked(interest.selected); +// itemView.setBackgroundResource(interest.selected ? +// R.drawable.bordered_rounded_rect_checked : R.drawable.bordered_rounded_rect); + checkBox.setOnCheckedChangeListener(getCheckBoxListener()); + rootView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + BusProvider.getBus().post( + new CheckBoxClickUIEvent(!checkBox.isChecked(), getAdapterPosition())); + } + }); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/CircleEditViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/CircleEditViewHolder.java new file mode 100755 index 0000000..84f7455 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/CircleEditViewHolder.java @@ -0,0 +1,50 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.view.View; +import android.widget.ImageButton; +import android.widget.TextView; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.DeleteCircleUIEvent; +import com.shaya.poinila.android.presentation.uievent.EditCircleNameUIEvent; +import com.shaya.poinila.android.presentation.uievent.ViewCircleMembersUIEvent; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.util.BusProvider; + +import butterknife.Bind; +import butterknife.OnClick; +import data.model.Circle; + +/** + * Created by iran on 2015-07-28. + */ +public class CircleEditViewHolder extends BaseViewHolder{ + @Bind(R.id.view_members) + public ImageButton viewMembersBtn; + @Bind(R.id.edit_name) + public ImageButton editCircleNameBtn; + @Bind(R.id.delete) + public ImageButton deleteCircleBtn; + @Bind(R.id.name) + public TextView circleNameView; + + public CircleEditViewHolder(View view) { + super(view); + } + + @Override + public void fill(Circle circle) { + ViewUtils.setText(circleNameView, circle.name); + } + + @OnClick(R.id.view_members) public void onViewMembers(){ + BusProvider.getBus().post(new ViewCircleMembersUIEvent(getAdapterPosition())); + } + + @OnClick(R.id.edit_name) public void onEditCircleName(){ + BusProvider.getBus().post(new EditCircleNameUIEvent(getAdapterPosition())); + } + + @OnClick(R.id.delete) public void onDeleteCircle(){ + BusProvider.getBus().post(new DeleteCircleUIEvent(getAdapterPosition())); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/CircleMemberViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/CircleMemberViewHolder.java new file mode 100755 index 0000000..685012c --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/CircleMemberViewHolder.java @@ -0,0 +1,47 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.view.View; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.TextView; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.MemberCircleToggledEvent; +import com.shaya.poinila.android.util.BusProvider; + +import butterknife.Bind; +import butterknife.OnClick; +import data.model.ImageUrls; +import data.model.Member; + +import static com.shaya.poinila.android.presentation.view.ViewUtils.setImage; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; + +/** + * Created by iran on 2015-07-26. + */ +public class CircleMemberViewHolder extends BaseViewHolder{ + @Bind(R.id.image) public ImageView avatarImage; + @Bind(R.id.title) public TextView nameView; + @Bind(R.id.subtitle) public TextView subTitleView; + @Bind(R.id.icon) public ImageButton addRemoveIcon; + @Bind(R.id.icon_caption) public TextView iconCaptionView; + + public CircleMemberViewHolder(View view) { + super(view); + iconCaptionView.setVisibility(View.GONE); + } + + @OnClick(R.id.icon) public void onAddOrRemove(){ + BusProvider.getBus().post(new MemberCircleToggledEvent(getAdapterPosition())); + } + + public void fill(Member member) { + setImage(avatarImage, member.imageUrls, ImageUrls.ImageType.MEMBER, ImageUrls.ImageSize.AVATAR); + setText(nameView, member.fullName); + setText(subTitleView, member.uniqueName);//(member.aboutMe); + addRemoveIcon.setBackgroundResource(R.drawable.add_remove_checkbox_selector); + addRemoveIcon.setSelected(member.selected); + } + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/CollectionNotifViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/CollectionNotifViewHolder.java new file mode 100755 index 0000000..d41f3e1 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/CollectionNotifViewHolder.java @@ -0,0 +1,34 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.view.View; + +import com.shaya.poinila.android.presentation.uievent.NotifActorClickedUIEvent; +import com.shaya.poinila.android.util.BusProvider; + +import data.model.ImageUrls; +import data.model.Notification; + +import static com.shaya.poinila.android.presentation.view.ViewUtils.setImage; + +/** + * Created by iran on 2015-08-15. + */ +public class CollectionNotifViewHolder extends NotificationViewHolder{ + + public CollectionNotifViewHolder(View inflatedView) { + super(inflatedView); + } + + @Override + public void fill(Notification notification) { + super.fill(notification); + setImage(image, notification.mainActor.imageUrls, ImageUrls.ImageType.COLLECTION, ImageUrls.ImageSize.AVATAR); + + rootView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new NotifActorClickedUIEvent(getAdapterPosition())); + } + }); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/CollectionViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/CollectionViewHolder.java new file mode 100755 index 0000000..9651d0e --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/CollectionViewHolder.java @@ -0,0 +1,77 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import com.shaya.poinila.android.presentation.PoinilaApplication; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.CollectionClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.MemberClickedUIEvent; +import com.shaya.poinila.android.util.BusProvider; + +import butterknife.Bind; +import butterknife.OnClick; +import data.event.BaseEvent; +import data.model.Collection; +import data.model.ImageUrls; + +import static com.shaya.poinila.android.presentation.view.ViewUtils.setFont; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setImage; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; + +/** + * Created by iran on 2015-06-22. + */ +public abstract class CollectionViewHolder extends BaseViewHolder{ + + public final BaseEvent.ReceiverName receiverTag; + + public CollectionViewHolder(View view, BaseEvent.ReceiverName receiverTag) { + super(view); + this.receiverTag = receiverTag; + } + + + @Bind(R.id.collection_author) + protected ViewGroup collectionAuthorView; + + protected @Bind(R.id.text_top) TextView topTag; + protected @Bind(R.id.text_bottom) TextView bottomTag; + protected @Bind(R.id.image_big) ImageView cover; + protected @Bind(R.id.image_small_top) ImageView post1Image; + protected @Bind(R.id.image_small_middle) ImageView post2Image; + protected @Bind(R.id.image_small_bottom) ImageView post3Image; + + /** + * Always call super.fill when overriding + * @param collection + */ + public void fill(Collection collection) { + setImage((ImageView) collectionAuthorView.findViewById(R.id.image), + collection.owner.imageUrls, ImageUrls.ImageType.MEMBER, ImageUrls.ImageSize.AVATAR); + setText((TextView) collectionAuthorView.findViewById(R.id.title), collection.name); + setFont((TextView) collectionAuthorView.findViewById(R.id.title), + PoinilaApplication.getAppContext().getString(R.string.default_bold_font_path)); + setText((TextView) collectionAuthorView.findViewById(R.id.subtitle), collection.owner.fullName); + + /*Transformation transformation = new RoundedTransformationBuilder() + .cornerRadiusDp(ResourceUtils.getDimen(R.dimen.corner_columned_lvlhalf)) + .oval(false) + .build();*/ + + setImage(cover, collection.coverImageUrls, ImageUrls.ImageType.COLLECTION, ImageUrls.ImageSize.BIG); + setImage(post1Image, collection.image1Url, ImageUrls.ImageType.COLLECTION, ImageUrls.ImageSize.AVATAR); + setImage(post2Image, collection.image2Url, ImageUrls.ImageType.COLLECTION, ImageUrls.ImageSize.AVATAR); + setImage(post3Image, collection.image3Url, ImageUrls.ImageType.COLLECTION, ImageUrls.ImageSize.AVATAR); + } + + @OnClick(R.id.collection_author) protected void onGoingToProfile(){ + BusProvider.getBus().post(new MemberClickedUIEvent(getAdapterPosition(), receiverTag)); + } + + @OnClick(R.id.collection) protected void onGoingToCollection(){ + BusProvider.getBus().post(new CollectionClickedUIEvent(getAdapterPosition(), receiverTag)); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/CommentViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/CommentViewHolder.java new file mode 100755 index 0000000..d4955a4 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/CommentViewHolder.java @@ -0,0 +1,58 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.util.Log; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.CommentLongClickUIEvent; +import com.shaya.poinila.android.presentation.uievent.MemberClickedUIEvent; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.TimeUtils; + +import butterknife.Bind; +import butterknife.OnClick; +import data.model.Comment; +import data.model.ImageUrls; +import manager.DataRepository; + +import static com.shaya.poinila.android.presentation.view.ViewUtils.setImage; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; + +/** + * Created by iran on 2015-06-25. + */ +public class CommentViewHolder extends BaseViewHolder{ + public CommentViewHolder(View view) { + super(view); + } + + @Bind(R.id.image) ImageView image; + + @Bind(R.id.title) TextView title; + + @Bind(R.id.subtitle) TextView subtitle; + + @Bind(R.id.date_created) TextView dateCreated; + + public void fill(Comment comment) { + itemView.setLongClickable(comment.deletable); + if (itemView.isLongClickable()) + itemView.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + BusProvider.getBus().post(new CommentLongClickUIEvent(getAdapterPosition())); + return true; + } + }); + else itemView.setOnLongClickListener(null); + setImage(image, comment.commenter.imageUrls, ImageUrls.ImageType.MEMBER, ImageUrls.ImageSize.AVATAR); + setText(title, comment.commenter.fullName); + setText(subtitle, comment.content); + setText(dateCreated, TimeUtils.getTimeString(comment.creationDate, DataRepository.getInstance().getServerTimeDifference())); + } + + @OnClick(R.id.image) public void onImageClicked(){ + BusProvider.getBus().post(new MemberClickedUIEvent(getAdapterPosition(), null)); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/DashboardPostViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/DashboardPostViewHolder.java new file mode 100755 index 0000000..b262db8 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/DashboardPostViewHolder.java @@ -0,0 +1,69 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.view.View; +import android.widget.ImageView; + +import com.shaya.poinila.android.presentation.R; + +import butterknife.Bind; +import butterknife.ButterKnife; +import data.event.BaseEvent; +import data.model.Post; +import data.model.PostType; +import data.model.SuggestionReason; + +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; +import static com.shaya.poinila.android.util.ResourceUtils.getString; + +/** + * Created by iran on 2015-08-09. + */ +public class DashboardPostViewHolder extends PostViewHolder{ + + @Bind(R.id.video_type_icon) + ImageView videoType; + + public DashboardPostViewHolder(View view, BaseEvent.ReceiverName receiverTag) { + super(view, receiverTag); + + avatar = ButterKnife.findById(postAuthor, R.id.image); + createdByTextView = ButterKnife.findById(postAuthor, R.id.title); + authorName = ButterKnife.findById(postAuthor, R.id.subtitle); + + + collectionImage = ButterKnife.findById(postCollection, R.id.image); + collectionStatusView = ButterKnife.findById(postCollection, R.id.title); + collectionName = ButterKnife.findById(postCollection, R.id.subtitle); + } + + @Override + public void fill(Post post) { + super.fill(post); + + if(post.type.equals(PostType.VIDEO)) + videoType.setVisibility(View.VISIBLE); + else + videoType.setVisibility(View.GONE); + + String reason = null; + + if (post.reason == SuggestionReason.PickedForYou) + reason = getString(R.string.picked_for_you); + else if (post.reason == SuggestionReason.FoundInInterest) + reason = getString(R.string.found_in_interest); + else if (post.reason == SuggestionReason.Following) + reason = getString(R.string.found_in_followed_collections); + else + reason = getString(R.string.collected_in); + setText(collectionStatusView, reason); + } + + /* @OnClick @Nullable (R.actorID.post_collection) protected void onGoingToCollection(){ + BusProvider.getBus().post(new CollectionClickedUIEvent(getAdapterPosition(), receiverTag)); + } + + + @OnClick @Nullable (R.actorID.post_author) protected void onGoingToProfile(){ + BusProvider.getBus().post(new MemberClickedUIEvent(getAdapterPosition(), receiverTag)); + }*/ +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/DateHeaderViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/DateHeaderViewHolder.java new file mode 100755 index 0000000..9d0c3fb --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/DateHeaderViewHolder.java @@ -0,0 +1,25 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.view.View; +import android.widget.TextView; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.ViewUtils; + +import butterknife.Bind; + +/** + * Created by iran on 2015-08-15. + */ +public class DateHeaderViewHolder extends BaseViewHolder { + @Bind(R.id.title) + TextView dayNameView; + + public DateHeaderViewHolder(View inflatedView) { + super(inflatedView); + } + + public void fill(String dayName){ + ViewUtils.setText(dayNameView, dayName); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/EditableCollectionViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/EditableCollectionViewHolder.java new file mode 100755 index 0000000..8bd0414 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/EditableCollectionViewHolder.java @@ -0,0 +1,44 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.view.View; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.EditItemUIEvent; +import com.shaya.poinila.android.presentation.uievent.RemoveItemUIEvent; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.ResourceUtils; + +import butterknife.OnClick; +import data.event.BaseEvent; +import data.model.Collection; + +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; + +/** + * Created by iran on 2015-08-17. + */ +public class EditableCollectionViewHolder extends CollectionViewHolder{ + + + public final BaseEvent.ReceiverName receiverTag; + + public EditableCollectionViewHolder(View view, BaseEvent.ReceiverName receiverTag) { + super(view, receiverTag); + this.receiverTag = receiverTag; + } + + @Override + public void fill(Collection collection) { + super.fill(collection); + topTag.setVisibility(View.GONE); + setText(bottomTag, ResourceUtils.getStringFormatted(R.string.posts_formatted, collection.postCount)); + } + + @OnClick(R.id.remove_collection) public void onRemoveCollection(){ + BusProvider.getBus().post(new RemoveItemUIEvent(getAdapterPosition())); + } + + @OnClick(R.id.edit_collection) public void onEditCollection(){ + BusProvider.getBus().post(new EditItemUIEvent(getAdapterPosition())); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/FollowableCollectionViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/FollowableCollectionViewHolder.java new file mode 100755 index 0000000..29a39db --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/FollowableCollectionViewHolder.java @@ -0,0 +1,67 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.OnFollowUnfollowCollectionUIEvent; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.PoinilaPreferences; +import com.shaya.poinila.android.util.ResourceUtils; + +import butterknife.Bind; +import data.event.BaseEvent; +import data.model.Collection; + +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; +import static com.shaya.poinila.android.util.ResourceUtils.getColor; +import static com.shaya.poinila.android.util.ResourceUtils.getString; + +/** + * Created by iran on 2015-08-05. + */ +public class FollowableCollectionViewHolder extends CollectionViewHolder { + @Bind(R.id.collection_follow_text) + TextView followText; + + @Bind(R.id.collection_follow_icon) + ImageView followIcon; + + @Bind(R.id.follow_collection_bar) View followBar; + + public FollowableCollectionViewHolder(View view, BaseEvent.ReceiverName receiverTag) { + super(view, receiverTag); + } + + @Override + public void fill(final Collection collection) { + super.fill(collection); + + topTag.setVisibility(View.GONE); + setText(bottomTag, ResourceUtils.getStringFormatted(R.string.posts_formatted, collection.postCount)); + + followBar.setVisibility( + collection.owner.id == Integer.parseInt(PoinilaPreferences.getMyId()) + ? View.GONE : View.VISIBLE); + + // Important note: just filling views. In fact, we are in a not known state here. + updateFollowIcon(collection.followedByMe); + + followBar.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new OnFollowUnfollowCollectionUIEvent(getAdapterPosition(), !collection.followedByMe)); + } + }); + } + + /*@OnClick(R.actorID.follow_collection_bar) public void onGoingToProfile(){ + BusProvider.getBus().post(new OnFollowUnfollowCollectionUIEvent(getAdapterPosition(), )); + }*/ + + public void updateFollowIcon(boolean isFollowed){ // action appearance is opposite to collection state + followIcon.setSelected(isFollowed); + followText.setTextColor(getColor(isFollowed ? R.color.sea_buckthorn : R.color.tundora)); + setText(followText, getString(isFollowed ? R.string.unfollow_item : R.string.follow_item)); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/FrameCollectionViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/FrameCollectionViewHolder.java new file mode 100755 index 0000000..98f7422 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/FrameCollectionViewHolder.java @@ -0,0 +1,49 @@ +package com.shaya.poinila.android.presentation.viewholder; + +/** + * Created by iran on 2015-09-05. + */ + +import android.view.View; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.TextView; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.CollectionFrameToggledEvent; +import com.shaya.poinila.android.util.BusProvider; + +import butterknife.Bind; +import butterknife.OnClick; +import data.model.Collection; +import data.model.ImageUrls; + +import static com.shaya.poinila.android.presentation.view.ViewUtils.setImage; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; + + +/** + * Created by iran on 2015-07-26. + */ +public class FrameCollectionViewHolder extends BaseViewHolder{ + @Bind(R.id.image) public ImageView avatarImage; + @Bind(R.id.title) public TextView nameView; + @Bind(R.id.subtitle) public TextView subTitleView; + @Bind(R.id.icon) public ImageButton addRemoveIcon; + @Bind(R.id.icon_caption) public TextView iconCaptionView; + + public FrameCollectionViewHolder(View view) { + super(view); + iconCaptionView.setVisibility(View.GONE); + } + + @OnClick(R.id.icon) public void onAddOrRemove(){ + BusProvider.getBus().post(new CollectionFrameToggledEvent(getAdapterPosition())); + } + + public void fill(Collection collection){ + setImage(avatarImage, collection.coverImageUrls, ImageUrls.ImageType.COLLECTION, ImageUrls.ImageSize.AVATAR); + setText(nameView, collection.name); + addRemoveIcon.setBackgroundResource(R.drawable.add_remove_checkbox_selector); + addRemoveIcon.setSelected(collection.selected); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/FrameEditViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/FrameEditViewHolder.java new file mode 100755 index 0000000..7b99f42 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/FrameEditViewHolder.java @@ -0,0 +1,53 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.view.View; +import android.widget.ImageButton; +import android.widget.TextView; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.DeleteFrameUIEvent; +import com.shaya.poinila.android.presentation.uievent.EditFrameNameUIEvent; +import com.shaya.poinila.android.presentation.uievent.ViewFrameMembersUIEvent; +import com.shaya.poinila.android.util.BusProvider; + +import butterknife.Bind; +import butterknife.OnClick; +import data.model.Frame; + +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; + +/** + * Created by iran on 2015-07-28. + */ +public class FrameEditViewHolder extends BaseViewHolder{ + @Bind(R.id.view_members) + public ImageButton viewFrameCollectionsBtn; + @Bind(R.id.edit_name) + public ImageButton editCircleNameBtn; + @Bind(R.id.delete) + public ImageButton deleteCircleBtn; + @Bind(R.id.name) + public TextView frameNameView; + + public FrameEditViewHolder(View view) { + super(view); + viewFrameCollectionsBtn.setImageResource(R.drawable.collection_white); + } + + @Override + public void fill(Frame frame) { + setText(frameNameView, frame.name); + } + + @OnClick(R.id.view_members) public void onViewCollections(){ + BusProvider.getBus().post(new ViewFrameMembersUIEvent(getAdapterPosition())); + } + + @OnClick(R.id.edit_name) public void onEditFrameName(){ + BusProvider.getBus().post(new EditFrameNameUIEvent(getAdapterPosition())); + } + + @OnClick(R.id.delete) public void onDeleteFrame(){ + BusProvider.getBus().post(new DeleteFrameUIEvent(getAdapterPosition())); + } +} \ No newline at end of file diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/InviteNotifViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/InviteNotifViewHolder.java new file mode 100755 index 0000000..85e85fc --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/InviteNotifViewHolder.java @@ -0,0 +1,67 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.AnswerFriendshipUIEvent; +import com.shaya.poinila.android.presentation.uievent.NotifActorClickedUIEvent; +import com.shaya.poinila.android.util.BusProvider; + +import butterknife.Bind; +import butterknife.OnClick; +import data.model.FriendRequestAnswer; +import data.model.ImageUrls; +import data.model.InvitationNotif; + +import static com.shaya.poinila.android.presentation.view.ViewUtils.setImage; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; +import static com.shaya.poinila.android.util.ResourceUtils.getString; + +/** + * Created by iran on 2015-08-15. + */ +public class InviteNotifViewHolder extends BaseViewHolder { + + @Bind(R.id.image) ImageView image; + @Bind(R.id.title) TextView title; + @Bind(R.id.subtitle) TextView subtitle; + @Bind(R.id.agree) TextView agreeButton; + @Bind(R.id.ignore) TextView ignoreButton; + + //private int adapterPosition = -1; + + public InviteNotifViewHolder(View inflatedView) { + super(inflatedView); + } + +/* + used in showing summary of friendship request in notification page when we doesn't create + ViewHolder through adapter so haven't adapter position consequently. + @param adapterPosition + */ + +/* public InviteNotifViewHolder(View inflatedView, int adapterPosition){ + this(inflatedView); + this.adapterPosition = adapterPosition; + }*/ + + @OnClick(R.id.image) public void onAvatarClicked(){ + BusProvider.getBus().post(new NotifActorClickedUIEvent(getAdapterPosition())); + } + + @OnClick({R.id.ignore, R.id.agree}) public void onIgnore(View view){ + //if (adapterPosition == -1) adapterPosition = getAdapterPosition(); + if (view.getId() == R.id.agree) + BusProvider.getBus().post(new AnswerFriendshipUIEvent(getAdapterPosition(), FriendRequestAnswer.ACCEPT)); + else + BusProvider.getBus().post(new AnswerFriendshipUIEvent(getAdapterPosition(), FriendRequestAnswer.REJECT)); + } + + public void fill(InvitationNotif invitationNotif){ + setImage(image, invitationNotif.member.imageUrls, ImageUrls.ImageType.MEMBER, ImageUrls.ImageSize.AVATAR); + setText(title, invitationNotif.member.fullName); + setText(subtitle, getString(R.string.requested_to_be_your_friend)); + } + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/LoadingViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/LoadingViewHolder.java new file mode 100755 index 0000000..062f547 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/LoadingViewHolder.java @@ -0,0 +1,21 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.view.View; + +import data.model.Loading; + + +/** + * Created by iran on 5/10/2016. + */ +public class LoadingViewHolder extends BaseViewHolder { + + public LoadingViewHolder(View view) { + super(view); + } + + @Override + public void fill(Loading loading) { + + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/MemberNotifViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/MemberNotifViewHolder.java new file mode 100755 index 0000000..ea67e6e --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/MemberNotifViewHolder.java @@ -0,0 +1,43 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.util.Log; +import android.view.View; + +import com.shaya.poinila.android.presentation.uievent.MemberClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.NotifActorClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.NotifParticipantClickedUIEvent; +import com.shaya.poinila.android.util.BusProvider; + +import data.model.ImageUrls; +import data.model.Notification; + +import static com.shaya.poinila.android.presentation.view.ViewUtils.setImage; + +/** + * Created by iran on 2015-08-15. + */ +public class MemberNotifViewHolder extends NotificationViewHolder { + + public final ImageUrls.ImageType participantImageType; + + public MemberNotifViewHolder(View inflatedView, ImageUrls.ImageType participantImageType) { + super(inflatedView); + this.participantImageType = participantImageType; + } + + public void fill( final Notification notification){ + super.fill(notification); + if (notification.type != Notification.NotificationType.FRIENDSHIP_ACCEPTED) { + setImage(image, notification.mainActor.imageUrls, ImageUrls.ImageType.MEMBER, ImageUrls.ImageSize.AVATAR); + } + + rootView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post( + new NotifParticipantClickedUIEvent(notification.participants.get(0), notification.getParticipantImageType())); + + } + }); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/MemberViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/MemberViewHolder.java new file mode 100755 index 0000000..d3fd326 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/MemberViewHolder.java @@ -0,0 +1,109 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.FriendshipClickEvent; +import com.shaya.poinila.android.presentation.uievent.MemberClickedUIEvent; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.ResourceUtils; + +import butterknife.Bind; +import butterknife.OnClick; +import data.event.BaseEvent; +import data.model.ImageUrls; +import data.model.Member; +import manager.DataRepository; + +import static com.shaya.poinila.android.presentation.view.ViewUtils.setFont; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setImage; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; + +/** + * Created by iran on 2015-07-07. + */ +public class MemberViewHolder extends BaseViewHolder{ + + BaseEvent.ReceiverName receiverTag; + + public MemberViewHolder(View view, final BaseEvent.ReceiverName receiverTag) { + super(view); + this.receiverTag = receiverTag; + + view.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new MemberClickedUIEvent(getAdapterPosition(), receiverTag)); + } + }); + } + + @Bind(R.id.image) + ImageView avatar; + + @Bind(R.id.title) + TextView name; + + @Bind(R.id.subtitle) + TextView subtitle; + + @Bind(R.id.icon) + ImageView icon; + + @Bind(R.id.icon_caption) + TextView iconCaption; + + public void fill(Member member){ + setImage(avatar, member.imageUrls, ImageUrls.ImageType.MEMBER, ImageUrls.ImageSize.AVATAR); + setText(name, member.fullName); + + setFont(name, ResourceUtils.getString(R.string.default_bold_font_path)); + + String subtitleText = (member.url == null) ? member.uniqueName : member.url; + setText(subtitle, subtitleText); + + setFont(subtitle, ResourceUtils.getString(R.string.default_font_path)); + + if (DataRepository.getInstance().isMe(member.id)) { + icon.setVisibility(View.GONE); + return; + } + else icon.setVisibility(View.VISIBLE); + + //icon.setBackgroundResource(0); // to clear old background + if (DataRepository.getInstance().isMe(member.id)) { + // TODO: this is terrible! must use the profile fragment instead. + icon.setVisibility(View.GONE); + } else if (member.friendshipStatus == null){ + icon.setVisibility(View.INVISIBLE); + } + else{ + icon.setVisibility(View.VISIBLE); + switch (member.friendshipStatus) { + case NotFriend: + icon.setImageResource(R.drawable.add_friend); + break; + case WaitingForAction: + //TODO this is temporary drawable. must replace with new drawable + icon.setImageResource(R.drawable.add_friend); + break; + case IsFriend: + icon.setImageResource(R.drawable.friends); + break; + case Pending: + icon.setImageResource(R.drawable.pending_friendship_request); + break; + } + } + } + + /* @OnClick(R.actorID.image) public void onAvatarClick(){ + BusProvider.getBus().post(new MemberClickedUIEvent(getAdapterPosition())); + }*/ + + @OnClick(R.id.icon) public void onFriendshipClick(){ + BusProvider.getBus().post(new FriendshipClickEvent(getAdapterPosition())); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/MyFollowedCollectionViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/MyFollowedCollectionViewHolder.java new file mode 100755 index 0000000..ddbbfda --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/MyFollowedCollectionViewHolder.java @@ -0,0 +1,29 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.view.View; +import com.shaya.poinila.android.util.TimeUtils; + +import data.event.BaseEvent; +import data.model.Collection; +import manager.DataRepository; + +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; + +/** + * Created by iran on 2015-08-05. + */ +public class MyFollowedCollectionViewHolder extends CollectionViewHolder { + public MyFollowedCollectionViewHolder(View view, BaseEvent.ReceiverName receiverTag) { + super(view, receiverTag); + } + + @Override + public void fill(Collection collection) { + super.fill(collection); + topTag.setVisibility(View.GONE); + setText(bottomTag, TimeUtils.getTimeString(collection.lastPostCreationTime, + DataRepository.getInstance().getServerTimeDifference())); + /*setText(bottomTag, StringUtils.getStringWithPersianNumber( + ResourceUtils.getString(R.string.new_posts_formatted), collection.unseenPostsCount));*/ + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/NotificationViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/NotificationViewHolder.java new file mode 100755 index 0000000..72896f5 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/NotificationViewHolder.java @@ -0,0 +1,106 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.NotifActorClickedUIEvent; +import com.shaya.poinila.android.util.BusProvider; + +import butterknife.Bind; +import data.model.Notification; + +import static com.shaya.poinila.android.presentation.view.ViewUtils.setNotificationImages; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setNotificationTitle; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; +import static com.shaya.poinila.android.util.ResourceUtils.getString; +import static com.shaya.poinila.android.util.ResourceUtils.getStringFormatted; + +/** + * Created by iran on 2015-08-15. + */ +public abstract class NotificationViewHolder extends BaseViewHolder { + protected @Bind(R.id.image) ImageView image; + protected @Bind(R.id.title) TextView title; + protected @Bind(R.id.subtitle) TextView subtitle; + protected @Bind(R.id.notif_image_container) ViewGroup imageContainer; + + public NotificationViewHolder(View itemView) { + super(itemView); + } + + public void fill(final Notification notification){ + setNotificationTitle(title, notification); + setText(subtitle, getSubtitleText(notification)); + setNotificationImages(imageContainer, notification.participants, notification.getParticipantImageType()); + + image.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new NotifActorClickedUIEvent(getAdapterPosition())); + } + }); + + if (!notification.seen) + rootView.setBackgroundResource(R.color.wild_sand); + } + + protected String getSubtitleText(Notification notification){ + switch (notification.type){ + case MY_POST_LIKED: + return (notification.participants.size() == 1) ? + getString(R.string.notif_my_post_liked_singular) : + getString(R.string.notif_my_post_liked_plural); + + case MY_POST_REPOSTED: + return (notification.participants.size() == 1) ? + getString(R.string.notif_my_post_reposted_singular) : + getString(R.string.notif_my_post_reposted_plural); + + case COMMENT_AFTER_YOUR_COMMENT: + return (notification.participants.size() == 1) ? + getString(R.string.notif_comment_after_you_singular) : + getString(R.string.notif_comment_after_you_plural); + + case COMMENT_MY_POST: + return (notification.participants.size() == 1) ? + getString(R.string.notif_comment_my_post_singular) : + getString(R.string.notif_comment_my_post_plural); + + case FRIENDS_LIKED_POSTS: + /*return (notification.participants.size() == 1) ? + getString(R.string.notif_friend_liked_posts_singular) : + getStringFormatted(R.string.notif_friend_liked_posts_plural);*/ + return getStringFormatted(R.string.notif_friend_liked_posts_singular, notification.participants.size()); + /*getStringFormatted(R.string.notif_friend_liked_posts_singular, notification.mainActor.title): + getStringFormatted(R.string.notif_friend_liked_posts_plural, notification.mainActor.title);*/ + + + case FRIENDS_FOLLOWED_COLLECTIONS: + return getStringFormatted(R.string.notif_friend_followed_collections_singular, notification.participants.size()); + /* return (notification.participants.size() == 1) ? + getStringFormatted(R.string.notif_friend_followed_collections_singular, notification.participants.size()): + getStringFormatted(R.string.notif_friend_followed_collections_plural, notification.participants.size());*/ + + case MY_COLLECTION_FOLLOWED: + return (notification.participants.size() == 1) ? + getString(R.string.notif_my_collection_followed_singular) : + getString(R.string.notif_my_collection_followed_plural); + + case FRIENDS_CREATED_COLLECTIONS: + return getStringFormatted(R.string.notif_friend_created_collections_singular, notification.participants.size()); + /*return (notification.participants.size() == 1) ? + getStringFormatted(R.string.notif_friend_created_collections_singular, notification.mainActor.uniqueName): + getStringFormatted(R.string.notif_friend_created_collections_plural, + notification.participants.size(), notification.mainActor.uniqueName);*/ + case FRIENDSHIP_ACCEPTED: + return (notification.participants.size() == 1) ? + getString(R.string.notif_friendship_accepted_singular): + getString(R.string.notif_friendship_accepted_plural); + default: + return null; + } + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/PostDetailViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/PostDetailViewHolder.java new file mode 100755 index 0000000..8df3581 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/PostDetailViewHolder.java @@ -0,0 +1,252 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.text.Html; +import android.text.TextUtils; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.TextView; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.PostComponentClickedUIEvent; +import com.shaya.poinila.android.presentation.view.ViewInflater; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.TimeUtils; + +import org.apmem.tools.layouts.FlowLayout; + +import butterknife.Bind; +import butterknife.ButterKnife; +import butterknife.OnClick; +import data.model.ImageUrls; +import data.model.Post; +import data.model.PostType; +import data.model.PrivacyType; +import data.model.Tag; +import manager.DataRepository; + +import static com.shaya.poinila.android.presentation.uievent.PostComponentClickedUIEvent.Type.Collection; +import static com.shaya.poinila.android.presentation.uievent.PostComponentClickedUIEvent.Type.Comments; +import static com.shaya.poinila.android.presentation.uievent.PostComponentClickedUIEvent.Type.Fave; +import static com.shaya.poinila.android.presentation.uievent.PostComponentClickedUIEvent.Type.FaversList; +import static com.shaya.poinila.android.presentation.uievent.PostComponentClickedUIEvent.Type.FullImage; +import static com.shaya.poinila.android.presentation.uievent.PostComponentClickedUIEvent.Type.OriginalCollection; +import static com.shaya.poinila.android.presentation.uievent.PostComponentClickedUIEvent.Type.Poster; +import static com.shaya.poinila.android.presentation.uievent.PostComponentClickedUIEvent.Type.Reference; +import static com.shaya.poinila.android.presentation.uievent.PostComponentClickedUIEvent.Type.Repost; +import static com.shaya.poinila.android.presentation.uievent.PostComponentClickedUIEvent.Type.RepostersList; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setImage; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; + +/** + * Created by iran on 2015-11-18. + */ +public class PostDetailViewHolder extends BaseViewHolder{ + @Bind(R.id.post_title) + ViewGroup postTitle; + /* TextView postName; + ImageView faveIcon; + TextView websiteName; + TextView creationTime;*/ + @Bind(R.id.post_image) + ImageView postImage; + + @Bind(R.id.content) + TextView postContent; + + @Bind(R.id.website) TextView website; + @Bind(R.id.reference_container) ViewGroup postReferenceContainer; + + @Bind(R.id.collection_info) View collectionInfo; + @Bind(R.id.author_info) View authorInfo; + + @Bind(R.id.tags_divider) View tagsDivider; + @Bind(R.id.tags_container) + FlowLayout tagsContainer; + + @Bind(R.id.comment_container) ViewGroup commentsContainer; + + @Bind(R.id.stats) ViewGroup postStats; + ImageButton commentBtn, repostBtn, faveBtn; + TextView faveCount, commentCount, repostCount; + + @Bind(R.id.original_collection) ViewGroup originalCollection; + + + public PostDetailViewHolder(View view) { + super(view); + faveCount = ButterKnife.findById(postStats, R.id.fave_num); + faveBtn = ButterKnife.findById(postStats, R.id.fave_icon); + faveCount.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new PostComponentClickedUIEvent(FaversList)); + } + }); + faveBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new PostComponentClickedUIEvent(Fave)); + } + }); + + commentCount = ButterKnife.findById(postStats, R.id.comment_num); + commentBtn= ButterKnife.findById(postStats, R.id.comment_icon); + commentCount.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new PostComponentClickedUIEvent(Comments)); + } + }); + commentBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new PostComponentClickedUIEvent(Comments)); + } + }); + + repostCount = ButterKnife.findById(postStats, R.id.repost_num); + repostBtn = ButterKnife.findById(postStats, R.id.repost_icon); + repostCount.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new PostComponentClickedUIEvent(RepostersList)); + } + }); + repostBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new PostComponentClickedUIEvent(Repost)); + } + }); + + authorInfo.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new PostComponentClickedUIEvent(Poster)); + } + }); + collectionInfo.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new PostComponentClickedUIEvent(Collection)); + } + }); + originalCollection.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new PostComponentClickedUIEvent(OriginalCollection)); + } + }); + + } + + + @Override + public void fill(final Post post) { + + /*------actual fill--------*/ + + ((TextView)postTitle.findViewById(R.id.title)).setText(post.name); + //((TextView)postTitle.findViewById(R.id.subtitle)).setText(post.author.urlName); + //((TextView)postTitle).findViewById(R.actorID.image)) + ((TextView)postTitle.findViewById(R.id.date_created)). + setText(TimeUtils.getTimeString(post.creationTime, DataRepository.getInstance().getServerTimeDifference())); + + if (post.type == PostType.IMAGE) { + postImage.setVisibility(View.VISIBLE); + setImage(postImage, post.imagesUrls, ImageUrls.ImageType.POST, ImageUrls.ImageSize.BIG); + setText(postContent, post.summary); + + postImage.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (TextUtils.isEmpty(post.originalWebpage)) + BusProvider.getBus().post(new PostComponentClickedUIEvent(FullImage)); + else + BusProvider.getBus().post(new PostComponentClickedUIEvent(Reference)); + } + }); + } else{ + //DataRepository.getInstance().getPostContent(post.contentUrl.url, postContent); + postImage.setVisibility(View.GONE); + if (!TextUtils.isEmpty(post.contentUrl)){ + if (TextUtils.isEmpty(post.content)) + DataRepository.getInstance().getPostContent(post.contentUrl, post.id); + else + setText(postContent, Html.fromHtml(post.content)); + } + } + + setImage((ImageView) authorInfo.findViewById(R.id.image), + post.author.imageUrls, ImageUrls.ImageType.MEMBER, ImageUrls.ImageSize.AVATAR); + ((TextView)authorInfo.findViewById(R.id.title)).setText(post.author.fullName); + + setImage((ImageView) collectionInfo.findViewById(R.id.image), + post.collection.coverImageUrls, ImageUrls.ImageType.COLLECTION, ImageUrls.ImageSize.AVATAR); + ((TextView)collectionInfo.findViewById(R.id.title)).setText(post.collection.name); + + if (TextUtils.isEmpty(post.originalWebpage)) + postReferenceContainer.setVisibility(View.GONE); + else { + setText(website, post.originalWebpage); + postReferenceContainer.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new PostComponentClickedUIEvent(Reference)); + } + }); + } + + if (post.tags == null || post.tags.isEmpty()){ + tagsContainer.setVisibility(View.GONE); + tagsDivider.setVisibility(View.GONE); + } + else{ + tagsContainer.removeAllViews(); + for (Tag tag : post.tags){ + //tagsContainer.addView(ViewInflater.inflateNormalTag(tag, getActivity())); + // TODO: difference between tag in post and interest in member may rise some issues + ViewInflater.addTagToContainer(tagsContainer, tag); + } + } + + /*---Comments----*/ + if (post.comments == null || post.comments.isEmpty()){ + commentsContainer.setVisibility(View.GONE); + //??? findviewbyid + rootView.findViewById(R.id.comment_container_divider).setVisibility(View.GONE); + }else{ + commentsContainer.removeAllViews(); + for (int i = 0; i < 3 && i < post.comments.size(); i++){ + commentsContainer.addView(ViewInflater.inflateComment(post.comments.get(i), rootView.getContext())); // ???getActivity + } + } + + /*----stats----*/ + if (post.privacy == PrivacyType.PRIVATE){ + repostBtn.setVisibility(View.INVISIBLE); + repostCount.setVisibility(View.INVISIBLE); + }else { + setText(repostCount, post.repostCount); + } + setText(faveCount, post.faveCount); + faveBtn.setSelected(post.favedByMe); + setText(commentCount, post.commentCount); + + if (post.originalCollection != null) { + setImage((ImageView) originalCollection.findViewById(R.id.image), post.originalCollection.coverImageUrls, + ImageUrls.ImageType.COLLECTION, ImageUrls.ImageSize.AVATAR); + ((TextView) originalCollection.findViewById(R.id.subtitle)). + setText(String.valueOf(post.originalCollection.name)); + ((TextView) originalCollection.findViewById(R.id.fave_num)). + setText(String.valueOf(post.originalCollection.totalLikeCount)); + ((TextView) originalCollection.findViewById(R.id.comment_num)). + setText(String.valueOf(post.originalCollection.totalCommentCount)); + ((TextView) originalCollection.findViewById(R.id.repost_num)). + setText(String.valueOf(post.originalCollection.totalRepostCount)); + }else{ + originalCollection.setVisibility(View.GONE); + } + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/PostNotifViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/PostNotifViewHolder.java new file mode 100755 index 0000000..7f6f209 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/PostNotifViewHolder.java @@ -0,0 +1,36 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.view.View; + +import com.shaya.poinila.android.presentation.uievent.NotifActorClickedUIEvent; +import com.shaya.poinila.android.util.BusProvider; + +import data.model.ImageUrls; +import data.model.Notification; + +import static com.shaya.poinila.android.presentation.view.ViewUtils.setImage; + +/** + * Created by iran on 2015-08-15. + */ +public class PostNotifViewHolder extends NotificationViewHolder { + + public PostNotifViewHolder(View inflatedView) { + super(inflatedView); + } + + @Override + public void fill(Notification notification) { + super.fill(notification); + setImage(image, notification.mainActor.imageUrls, ImageUrls.ImageType.POST, ImageUrls.ImageSize.AVATAR); + rootView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new NotifActorClickedUIEvent(getAdapterPosition())); + } + }); + + } + + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/PostViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/PostViewHolder.java new file mode 100755 index 0000000..2bd61c3 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/PostViewHolder.java @@ -0,0 +1,203 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.support.annotation.Nullable; +import android.text.Html; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import com.makeramen.roundedimageview.RoundedTransformationBuilder; +import com.shaya.poinila.android.presentation.PoinilaApplication; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.CollectionClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.MemberClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.PostClickedUIEvent; +import com.shaya.poinila.android.presentation.uievent.RemovePostUIEvent; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.Logger; + +import butterknife.Bind; +import butterknife.ButterKnife; +import butterknife.OnClick; +import data.event.BaseEvent; +import data.model.ImageUrls; +import data.model.Post; +import data.model.PostType; +import data.model.PrivacyType; +import manager.DataRepository; + +import static com.shaya.poinila.android.presentation.view.ViewUtils.setFont; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setImage; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; +import static com.shaya.poinila.android.util.ResourceUtils.getString; + +/** + * Created by iran on 2015-06-22. + */ +public class PostViewHolder extends BaseViewHolder{ + public final BaseEvent.ReceiverName receiverTag; + + /*@Bind(R.actorID.post_title) + public ViewGroup postTitle;*/ + + @Bind(R.id.post_image) + public ImageView postImage; + + @Bind(R.id.post_content) + public TextView postSummary; + + @Bind(R.id.post_stats) + public ViewGroup postStats; + + @Bind(R.id.post_title) + public TextView postName; + //public TextView postWebsiteName; + + public ImageView favoriteBtn; + public TextView favoriteCount; + public ImageView commentBtn; + public TextView commentCount; + public ImageView repostBtn; + public TextView repostCount; + + + @Nullable @Bind(R.id.post_collection) + public ViewGroup postCollection; + public ImageView collectionImage; + public TextView collectionStatusView; + public TextView collectionName; + + @Nullable @Bind(R.id.post_author) + public ViewGroup postAuthor; + public ImageView avatar; + public TextView authorName; + public TextView createdByTextView; + + + public PostViewHolder(View view, final BaseEvent.ReceiverName receiverTag) { + super(view); + this.receiverTag = receiverTag; + + favoriteBtn = ButterKnife.findById(postStats, R.id.fave_icon); + favoriteCount = ButterKnife.findById(postStats, R.id.fave_num); + commentBtn = ButterKnife.findById(postStats, R.id.comment_icon); + commentCount = ButterKnife.findById(postStats, R.id.comment_num); + repostBtn = ButterKnife.findById(postStats, R.id.repost_icon); + repostCount = ButterKnife.findById(postStats, R.id.repost_num); + + view.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new PostClickedUIEvent(getAdapterPosition(), receiverTag)); + } + }); + } + + public void fill(Post post) { + switch (post.type){ + case TEXT: + postSummary.setVisibility(View.VISIBLE); + postImage.setVisibility(View.GONE); + postName.setVisibility(View.VISIBLE); + break; + case IMAGE: + postSummary.setVisibility(TextUtils.isEmpty(post.summary) ? View.GONE : View.VISIBLE); + postImage.setVisibility(View.VISIBLE); + postName.setVisibility(!TextUtils.isEmpty(post.name) ? View.VISIBLE : View.GONE); + break; + case VIDEO: + postSummary.setVisibility(TextUtils.isEmpty(post.summary) ? View.GONE : View.VISIBLE); + postImage.setVisibility(View.VISIBLE); + postName.setVisibility(!TextUtils.isEmpty(post.name) ? View.VISIBLE : View.GONE); + break; + } + + if (post.type == PostType.IMAGE) { + + /*if (TextUtils.isEmpty(post.summary)) + postContent.setVisibility(View.GONE);*/ + }else{ //post.type == PostType.TEXT + + } + + favoriteBtn.setSelected(post.favedByMe); + + // name and site + setText(postName, post.name); + //setText(postWebsiteName, post.originalWebpage); + + // set Bold Font + setFont(postName, getString(R.string.default_bold_font_path)); + + + + float radius = PoinilaApplication.getAppContext().getResources().getDimension(R.dimen.cardview_compat_inset_shadow); + // image + setImage(postImage, post.imagesUrls, ImageUrls.ImageType.POST, ImageUrls.ImageSize.MEDIUM , new RoundedTransformationBuilder().cornerRadiusDp(radius).build()); + + // content + if (post.type == PostType.TEXT){ + if (!TextUtils.isEmpty(post.summary)) + setText(postSummary, post.summary); + else if (!TextUtils.isEmpty(post.contentUrl)){ + if (TextUtils.isEmpty(post.content)) + DataRepository.getInstance().getPostContent(post.contentUrl, post.id); + else + setText(postSummary, Html.fromHtml(post.content)); + } + }else { + setText(postSummary, post.summary); + } + // stats + if (post.privacy == PrivacyType.PRIVATE){ + repostBtn.setVisibility(View.INVISIBLE); + repostCount.setVisibility(View.INVISIBLE); + }else { + setText(repostCount, post.repostCount); + } + setText(favoriteCount, post.faveCount); + setText(commentCount, post.commentCount); + + // owner + setImage(avatar, post.author.imageUrls, ImageUrls.ImageType.MEMBER, ImageUrls.ImageSize.AVATAR); + String createdBy = post.isRepost ? getString(R.string.repost_by) : getString(R.string.created_by); + setText(createdByTextView, createdBy); + setText(authorName, post.author.fullName); + + // collection + setImage(collectionImage, post.collection.coverImageUrls, ImageUrls.ImageType.COLLECTION, ImageUrls.ImageSize.AVATAR); + setText(collectionStatusView, getString(R.string.collected_in)); + setText(collectionName, post.collection.name); + + if (postAuthor != null ){//&& DataRepository.getInstance().isMe(post.author.id)) { + postAuthor.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + BusProvider.getBus().post(new MemberClickedUIEvent(getAdapterPosition(), receiverTag)); + } + }); + } + //else postAuthor.setOnClickListener(null); + } + + @OnClick({R.id.post_image, R.id.post_content}) protected void onGoingToPost(){ + BusProvider.getBus().post(new PostClickedUIEvent(getAdapterPosition(), receiverTag)); + } + + @Nullable @OnClick(R.id.post_collection) protected void onGoingToCollection(){ + BusProvider.getBus().post(new CollectionClickedUIEvent(getAdapterPosition(), receiverTag)); + } + + + /*@Nullable @OnClick (R.actorID.post_author) protected void onGoingToProfile(){ + BusProvider.getBus().post(new MemberClickedUIEvent(getAdapterPosition(), receiverTag)); + }*/ + + @Nullable @OnClick(R.id.bottom_bar_remove) public void onRemovePost(){ + BusProvider.getBus().post(new RemovePostUIEvent(getAdapterPosition())); + } +} + diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/PostsOfCollectionViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/PostsOfCollectionViewHolder.java new file mode 100755 index 0000000..a135a87 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/PostsOfCollectionViewHolder.java @@ -0,0 +1,21 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.view.View; +import com.shaya.poinila.android.presentation.R; + +import butterknife.ButterKnife; +import data.event.BaseEvent; + +/** + * Created by iran on 2015-08-10. + */ +public class PostsOfCollectionViewHolder extends PostViewHolder{ + + public PostsOfCollectionViewHolder(View view, BaseEvent.ReceiverName receiverTag) { + super(view, receiverTag); + + avatar = ButterKnife.findById(postAuthor, R.id.image); + createdByTextView = ButterKnife.findById(postAuthor, R.id.title); + authorName = ButterKnife.findById(postAuthor, R.id.subtitle); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/RatePonilaViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/RatePonilaViewHolder.java new file mode 100755 index 0000000..c5320e9 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/RatePonilaViewHolder.java @@ -0,0 +1,30 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.view.View; +import android.widget.Button; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.fragments.DashboardFragment.DashboardRecyclerViewAdapter.AskIfUserRatesPonila; + +import butterknife.Bind; + +/** + * Created by iran on 1/20/2016. + */ +public class RatePonilaViewHolder extends BaseViewHolder{ + @Bind(R.id.positive_button) + public Button positiveButton; + @Bind(R.id.negative_button) + public Button negativeButton; + @Bind(R.id.dont_know_button) + public Button notNowButton; + + public RatePonilaViewHolder(View view) { + super(view); + } + + @Override + public void fill(AskIfUserRatesPonila askIfUserRatesPonila) { + + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/RemovableInterestViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/RemovableInterestViewHolder.java new file mode 100755 index 0000000..ede2e65 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/RemovableInterestViewHolder.java @@ -0,0 +1,49 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.view.View; +import android.widget.ImageView; + +import com.makeramen.roundedimageview.Corner; +import com.makeramen.roundedimageview.RoundedTransformationBuilder; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.util.ResourceUtils; +import com.squareup.picasso.Transformation; + +import butterknife.Bind; +import data.model.ImageTag; +import data.model.ImageUrls; + +import static com.shaya.poinila.android.presentation.view.ViewUtils.setImage; +import static com.shaya.poinila.android.presentation.view.ViewUtils.setText; + +/** + * Created by iran on 2015-11-04. + */ +public class RemovableInterestViewHolder extends RemovableTagViewHolder{ + + @Bind(R.id.image) public ImageView mImageView; + + public RemovableInterestViewHolder(View view) { + super(view); + } + + public void fill(ImageTag interest) { + setText(textView, interest.name); +// textView.setTextColor(ResourceUtils.getColor(invalidImage(interest) ? R.color.black : R.color.white)); + mImageView.setVisibility(invalidImage(interest) ? View.INVISIBLE : View.VISIBLE); +// removeBtn.setImageResource(invalidImage(interest) ? R.drawable.remove_boulder_36dp : R.drawable.remove_white_36dp); + + Transformation transformation = new RoundedTransformationBuilder() + .cornerRadiusDp(Corner.TOP_LEFT, ResourceUtils.getDimen(R.dimen.cardview_compat_inset_shadow)) + .cornerRadiusDp(Corner.TOP_RIGHT, ResourceUtils.getDimen(R.dimen.cardview_compat_inset_shadow)) + .oval(false) + .build(); + + + setImage(mImageView, interest.imageUrls, ImageUrls.ImageType.INTEREST, null, transformation); + } + + private boolean invalidImage(ImageTag imageInterest){ + return imageInterest.imageUrls == null || imageInterest.imageUrls.interest == null; + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/RemovablePostViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/RemovablePostViewHolder.java new file mode 100755 index 0000000..eee4fe4 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/RemovablePostViewHolder.java @@ -0,0 +1,22 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.view.View; +import com.shaya.poinila.android.presentation.R; + +import butterknife.Bind; +import butterknife.ButterKnife; +import data.event.BaseEvent; + +/** + * Created by iran on 2015-08-16. + */ +public class RemovablePostViewHolder extends PostViewHolder{ + + public RemovablePostViewHolder(View view, BaseEvent.ReceiverName receiverTag) { + super(view, receiverTag); + + collectionImage = ButterKnife.findById(postCollection, R.id.image); + collectionStatusView = ButterKnife.findById(postCollection, R.id.title); + collectionName = ButterKnife.findById(postCollection, R.id.subtitle); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/RemovableTagViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/RemovableTagViewHolder.java new file mode 100755 index 0000000..774d847 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/RemovableTagViewHolder.java @@ -0,0 +1,36 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.TextView; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.RemoveTagEvent; +import com.shaya.poinila.android.util.BusProvider; + +import butterknife.Bind; +import butterknife.OnClick; + +/** + * Created by iran on 2015-07-25. + */ +public class RemovableTagViewHolder extends BaseViewHolder{ + @Bind(R.id.removeButton) public ImageView removeBtn; + @Bind(R.id.tag) public TextView textView; + + public RemovableTagViewHolder(View view) { + super(view); + } + + @Override + public void fill(T t) { + + } + + @OnClick(R.id.removeButton) public void onRemove(){ + if(getAdapterPosition() != RecyclerView.NO_POSITION) + BusProvider.getBus().post(new RemoveTagEvent(getAdapterPosition())); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/SelectableInterestViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/SelectableInterestViewHolder.java new file mode 100755 index 0000000..b7dfe55 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/SelectableInterestViewHolder.java @@ -0,0 +1,48 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.view.View; +import android.widget.ImageView; + +import com.makeramen.roundedimageview.Corner; +import com.makeramen.roundedimageview.RoundedTransformationBuilder; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.util.ResourceUtils; +import com.squareup.picasso.Transformation; + +import butterknife.Bind; +import data.model.ImageTag; +import data.model.ImageUrls; +import data.model.Tag; + +/** + * Created by iran on 2015-11-03. + */ +public class SelectableInterestViewHolder extends CheckedTextViewHolder{ + @Bind(R.id.image) public ImageView mImageView; + + public SelectableInterestViewHolder(View view) { + super(view); + } + + @Override + public void fill(Tag interest) { + super.fill(interest); + ImageTag imageInterest = ((ImageTag) interest); +// textView.setTextColor(ResourceUtils.getColor(invalidImage(imageInterest) ? R.color.black : R.color.white)); + mImageView.setVisibility(invalidImage(imageInterest) ? View.INVISIBLE : View.VISIBLE); + + Transformation transformation = new RoundedTransformationBuilder() + .cornerRadiusDp(Corner.TOP_LEFT, ResourceUtils.getDimen(R.dimen.cardview_compat_inset_shadow)) + .cornerRadiusDp(Corner.TOP_RIGHT, ResourceUtils.getDimen(R.dimen.cardview_compat_inset_shadow)) + .oval(false) + .build(); + + ViewUtils.setImage(mImageView, ((ImageTag) interest).imageUrls, ImageUrls.ImageType.INTEREST, null, transformation); + } + + private boolean invalidImage(ImageTag imageInterest){ + return imageInterest.imageUrls == null || imageInterest.imageUrls.interest == null; + } + +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/SimpleTextViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/SimpleTextViewHolder.java new file mode 100755 index 0000000..3a5ad00 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/SimpleTextViewHolder.java @@ -0,0 +1,20 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.view.View; +import android.widget.TextView; + +import com.shaya.poinila.android.presentation.R; + +import butterknife.Bind; + +/** + * Created by iran on 2015-08-19. + */ +public abstract class SimpleTextViewHolder extends BaseViewHolder{ + @Bind(R.id.text) + public TextView textView; + + public SimpleTextViewHolder(View view) { + super(view); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/SingleImageViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/SingleImageViewHolder.java new file mode 100755 index 0000000..7af1cd6 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/SingleImageViewHolder.java @@ -0,0 +1,26 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.view.View; +import android.widget.ImageView; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.ImageClickedUIEvent; +import com.shaya.poinila.android.util.BusProvider; + +import butterknife.Bind; +import butterknife.OnClick; + +/** + * Created by iran on 2015-07-15. + */ +public abstract class SingleImageViewHolder extends BaseViewHolder{ + @Bind(R.id.image) + public ImageView imageView; + public SingleImageViewHolder(View view) { + super(view); + } + + @OnClick(R.id.image) public void onImageClick(){ + BusProvider.getBus().post(new ImageClickedUIEvent(getAdapterPosition())); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/SwitchTextViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/SwitchTextViewHolder.java new file mode 100755 index 0000000..a2590f7 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/SwitchTextViewHolder.java @@ -0,0 +1,41 @@ +package com.shaya.poinila.android.presentation.viewholder; + +import android.support.v7.widget.SwitchCompat; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CompoundButton; +import android.widget.TextView; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.OnOffSettingToggledUIEvent; +import com.shaya.poinila.android.presentation.view.ViewUtils; +import com.shaya.poinila.android.util.BusProvider; + +import butterknife.Bind; +import data.model.OnOffSetting; + +/** + * Created by iran on 2015-09-07. + */ +public class SwitchTextViewHolder extends BaseViewHolder{ + @Bind(R.id.switch_btn) SwitchCompat switchBtn; + @Bind(R.id.label) TextView label; + ViewGroup viewgroup; + + public SwitchTextViewHolder(View view) { + super(view); + viewgroup = (ViewGroup) view; + switchBtn.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + BusProvider.getBus().post(new OnOffSettingToggledUIEvent(getAdapterPosition(), isChecked)); + } + }); + } + + public void fill(OnOffSetting setting){ + switchBtn.setChecked(setting.value == OnOffSetting.ON); + ViewUtils.setText(label, setting.name); + ViewUtils.enableLayoutChildes(viewgroup, setting.enabled); + } +} diff --git a/src/main/java/com/shaya/poinila/android/presentation/viewholder/notification/NEditableCollectionViewHolder.java b/src/main/java/com/shaya/poinila/android/presentation/viewholder/notification/NEditableCollectionViewHolder.java new file mode 100755 index 0000000..37a26dd --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/presentation/viewholder/notification/NEditableCollectionViewHolder.java @@ -0,0 +1,17 @@ +package com.shaya.poinila.android.presentation.viewholder.notification; + +import android.view.View; + +import com.shaya.poinila.android.presentation.viewholder.EditableCollectionViewHolder; + +import data.event.BaseEvent; + +/** + * Created by iran on 6/22/2016. + */ +public class NEditableCollectionViewHolder extends EditableCollectionViewHolder { + public NEditableCollectionViewHolder(View view, BaseEvent.ReceiverName receiverTag) { + super(view, receiverTag); + bottomTag.setVisibility(View.GONE); + } +} diff --git a/src/main/java/com/shaya/poinila/android/utils/NotificationQueue.java b/src/main/java/com/shaya/poinila/android/utils/NotificationQueue.java new file mode 100755 index 0000000..90fa495 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/utils/NotificationQueue.java @@ -0,0 +1,36 @@ +package com.shaya.poinila.android.utils; + +import org.json.JSONArray; + +import java.util.HashMap; +import java.util.LinkedList; + +/** + * Created by iran on 6/20/2016. + */ +public class NotificationQueue { + + private static NotificationQueue instance; + HashMap maps; + + private NotificationQueue(){ + maps = new HashMap<>(); + } + + public static NotificationQueue getInstance(){ + + if(instance == null){ + instance = new NotificationQueue(); + } + + return instance; + } + + public void put(String group, JSONArray jSon){ + maps.put(group, jSon); + } + + public JSONArray get(String group){ + return maps.get(group); + } +} diff --git a/src/main/java/com/shaya/poinila/android/utils/PonilaAccountManager.java b/src/main/java/com/shaya/poinila/android/utils/PonilaAccountManager.java new file mode 100755 index 0000000..88761c2 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/utils/PonilaAccountManager.java @@ -0,0 +1,400 @@ +package com.shaya.poinila.android.utils; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AccountManagerCallback; +import android.accounts.AccountManagerFuture; +import android.accounts.AuthenticatorException; +import android.accounts.OperationCanceledException; +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.ActivityCompat; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.text.TextUtils; +import android.util.Log; + +import com.google.android.gms.auth.api.Auth; +import com.google.android.gms.auth.api.signin.GoogleSignInAccount; +import com.google.android.gms.auth.api.signin.GoogleSignInOptions; +import com.google.android.gms.auth.api.signin.GoogleSignInResult; +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.ResultCallback; +import com.onesignal.OneSignal; +import com.shaya.poinila.android.presentation.PoinilaApplication; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.util.PoinilaPreferences; + +import java.io.IOException; + +import data.model.Member; +import manager.DBFacade; +import ru.noties.debug.Debug; + +/** + * Created by iran on 5/29/2016. + */ +public class PonilaAccountManager { + + private AccountManager accountManager; // Android class + private static PonilaAccountManager instance = null; // Singleton instance + private String ponilaAccountType; + private GoogleApiClient mGoogleApiClient; + private GoogleSignInOptions gso; + public final static int GOOGLE_SIGN_IN_REQUEST_CODE = 10; + //================================================================================ + // Methods + //================================================================================ + + /** + * Private constructor + */ + private PonilaAccountManager(){ + accountManager = AccountManager.get(PoinilaApplication.getAppContext()); + ponilaAccountType = PoinilaApplication.getAppContext().getString(R.string.account_type); + + // Configure sign-in to request the user's ID, email address, and basic + // profile. ID and basic profile are included in DEFAULT_SIGN_IN. + gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) + .requestIdToken(PoinilaApplication.getAppContext().getString(R.string.server_client_id)) + .requestEmail() + .build(); + } + + /** + * Get the singleton instance + * @return PonilaAccountManager instance + */ + public static PonilaAccountManager getInstance(){ + if(instance == null){ + instance = new PonilaAccountManager(); + } + + return instance; + } + + /** + *

+ * Get the first account of the given type + * @param accountType Account type + * @return Account (if not found -> null) + */ + public Account getFirstAccount(String accountType) { + Account[] accounts = accountManager.getAccountsByType(accountType); + if (accounts.length > 0) { + return accounts[0]; + } else { + return null; + } + } + + /** + * Check whether at least a Goftalk account exists + * @return True|False + */ + public boolean ponilaAccountExists() { + return getPonilaAccountsNum() > 0; + } + + /** + * Get the account token of the first found account + * of the given type
+ * @param accountType Type of account + * @return Account token (if not found = "") + */ + public String getFirstAccountToken(String accountType) { + Account[] accounts = accountManager.getAccountsByType(accountType); + if (accounts.length > 0) { + return accountManager.peekAuthToken( + accounts[0], + accountType); + } else { + return ""; + } + } + + public void initGoogleAPIClient(FragmentActivity activity, GoogleApiClient.OnConnectionFailedListener onConnectionFailedListener){ + + if(isConnectedGoogleApiClient()) return; + if(mGoogleApiClient != null) mGoogleApiClient.stopAutoManage(activity); + // Build a GoogleApiClient with access to the Google Sign-In API and the + // options specified by gso. + mGoogleApiClient = new GoogleApiClient.Builder(activity) + .enableAutoManage(activity, 0, onConnectionFailedListener) + .addApi(Auth.GOOGLE_SIGN_IN_API, gso) + .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() { + @Override + public void onConnected(@Nullable Bundle bundle) { + } + + @Override + public void onConnectionSuspended(int i) { + + } + }).build(); + + } + + public void connectGoogleApiClient(){ + if(mGoogleApiClient != null) + mGoogleApiClient.connect(); + } + + public void signInWithGoogleAPI(Fragment fragment){ + Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient); + fragment.startActivityForResult(signInIntent, GOOGLE_SIGN_IN_REQUEST_CODE); + } + + /** + * Get Object Of Google Account + * @param data + * @param onGoogleSignInResult + * @return + */ + public GoogleSignInAccount getGoogleSignInAccount(Intent data, OnGoogleSignInResult onGoogleSignInResult){ + // Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...); + GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data); + GoogleSignInAccount acct = result.getSignInAccount(); + Log.d(getClass().getName(), "result = " + result.isSuccess()); + if (result.isSuccess()) { + // Signed in successfully, show authenticated UI. + onGoogleSignInResult.onSuccessGoogleSignIn(acct); + } else { + // Signed out, show unauthenticated UI. + onGoogleSignInResult.onFailureGoogleSignIn(acct); + } + + return acct; + } + + /** + * Sign Out From Google Account + * @param resultCallback + */ + public void signOutWithGoogleAPI(ResultCallback resultCallback){ +// revokeAccessGoogleAPI(resultCallback); + Auth.GoogleSignInApi.signOut(mGoogleApiClient).setResultCallback(resultCallback); + + } + + public void stopAutoManageGoogleApiClient(FragmentActivity activity){ + if(isConnectedGoogleApiClient()) mGoogleApiClient.stopAutoManage(activity); + } + + public void disconnectGoogleApiClient() { + if(isConnectedGoogleApiClient()) mGoogleApiClient.disconnect(); + } + + public boolean isConnectedGoogleApiClient(){ + return mGoogleApiClient != null && mGoogleApiClient.isConnected(); + } + + /** + * Disconnect Ponila From Google Account + * @param resultCallback + */ + public void revokeAccessGoogleAPI(ResultCallback resultCallback){ + Auth.GoogleSignInApi.revokeAccess(mGoogleApiClient).setResultCallback(resultCallback); + } + + public interface OnGoogleSignInResult{ + public void onSuccessGoogleSignIn(GoogleSignInAccount acct); + public void onFailureGoogleSignIn(GoogleSignInAccount acct); + } + + public void setGoogle(){ + setPonilaAccountData("sign_in_with_google", "yes"); + } + + public boolean isSignInWithGoogle(){ + return !TextUtils.isEmpty(getPonilaAccountData("sign_in_with_google")); + } + + /** + * Get the Ponila account token + * @return Ponila account token (if not found => "") + */ + public String getPonilaAccountToken() { + return getFirstAccountToken(ponilaAccountType); + } + + /** + * Get username of the Goftalk account + * @return Ponil username (if not found => "") + */ + public String getPonilaAccountUsername() { + Account gAccunt = getFirstAccount(ponilaAccountType); + if (gAccunt != null) return gAccunt.name; + else return ""; + } + + + /** + * set extra data + * @param key + * @param value + */ + public void setPonilaAccountData(String key, String value) { + + if(ponilaAccountExists()){ + // Get account (we know there exist at least one account of this type) + Account PonilaAccount = accountManager.getAccountsByType(ponilaAccountType)[0]; + // Set the data + accountManager.setUserData(PonilaAccount, key, value); + } + + } + + /** + * get extra data + * @param key + * @return + */ + public String getPonilaAccountData(String key) { + + // If no account is found, return "" + if (!ponilaAccountExists()) { + return ""; + } else { + // Get account + Account account = AccountManager.get(PoinilaApplication.getAppContext()).getAccountsByType(ponilaAccountType)[0]; + + // Return value + String value = ""; + try { + value = AccountManager.get(PoinilaApplication.getAppContext()).getUserData(account, key); + }catch(SecurityException e){ + e.printStackTrace(); + } + + if (value == null) return ""; + return value; + } + } + + /** + * Get the number of Ponila accounts + * @return Number of Ponila accounts + */ + private int getPonilaAccountsNum() { + return AccountManager + .get(PoinilaApplication.getAppContext()) + .getAccountsByType(ponilaAccountType) + .length; + } + + /** +// * Remove the Ponila account from phone +// */ + public void removePonilaAccount() { + Debug.i("RemovePonilaAccounts"); + // Callback of removing operation + AccountManagerCallback remCallback = new AccountManagerCallback() { + @Override + public void run(AccountManagerFuture future) { + if (future.isDone()) { + // If there's still an account, call remove again + if (getPonilaAccountsNum() > 0) removePonilaAccount(); + } + } + }; + + // Get all the Ponila accounts + Account[] gaccounts = AccountManager + .get(PoinilaApplication.getAppContext()) + .getAccountsByType(ponilaAccountType); + + // Remove all accounts + for (Account gaccount : gaccounts) { + AccountManager + .get(PoinilaApplication.getAppContext()) + .removeAccount(gaccount, remCallback, null); + } + + } + + /** + * Add a Ponila account + * @param username Username + * @param password Password + * @param authToken Access Token + */ + public void addPonilaAccount(String username, String password, String authToken) { + final Account account = new Account(username, ponilaAccountType); + + PoinilaPreferences.putAuthToken(authToken); + + accountManager.addAccountExplicitly(account, password, null); + accountManager.setAuthToken(account, ponilaAccountType, authToken); + } + + public void addPonilaAccountFromGoogle(String authToken) { + final Account account = new Account(getGoogleAccount().name, ponilaAccountType); + + PoinilaPreferences.putAuthToken(authToken); + + accountManager.addAccountExplicitly(account, "google_login", null); + accountManager.setAuthToken(account, ponilaAccountType, authToken); + } + + public void updatePonilaAccount(String password, String authToken) { + Account[] accounts = accountManager.getAccountsByType(ponilaAccountType); + + if(accounts.length > 0 && password != null) + accountManager.setPassword(accounts[0], password); + + if(accounts.length > 0 && authToken != null){ + PoinilaPreferences.putAuthToken(authToken); + accountManager.setAuthToken(accounts[0], ponilaAccountType, authToken); + } + } + + + + public void initUserTag(){ + Member member = DBFacade.getCachedMyInfo(); + if(member != null){ + OneSignal.sendTag("member_id", member.getId()); + } + } + + public void removeUserTag(){ + OneSignal.deleteTag("member_id"); + } + + public String getPonilaAuthToken(){ + Account[] accounts = accountManager.getAccountsByType(ponilaAccountType); + + if(accounts.length > 0) + try { + return accountManager.getAuthToken(accounts[0], ponilaAccountType, new Bundle(), true, null, null) + .getResult() + .getString(AccountManager.KEY_AUTHTOKEN); + } catch (OperationCanceledException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (AuthenticatorException e) { + e.printStackTrace(); + } + return ""; + } + + + public Account getGoogleAccount(){ + Account[] accounts = accountManager.getAccounts(); + + for(Account account : accounts){ + if(account.type.equals("com.google")) return account; + } + + return null; + } + + + +} diff --git a/src/main/java/com/shaya/poinila/android/utils/PonilaJsonParser.java b/src/main/java/com/shaya/poinila/android/utils/PonilaJsonParser.java new file mode 100755 index 0000000..74016af --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/utils/PonilaJsonParser.java @@ -0,0 +1,92 @@ +package com.shaya.poinila.android.utils; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; + +import com.google.gson.JsonElement; +import com.google.gson.JsonIOException; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSyntaxException; +import com.google.gson.internal.Streams; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.MalformedJsonException; + +/** + * A parser to parse Json into a parse tree of {@link JsonElement}s + * + * @author Inderjeet Singh + * @author Joel Leitch + * @since 1.3 + */ +public final class PonilaJsonParser { + + /** + * Parses the specified JSON string into a parse tree + * + * @param json JSON text + * @return a parse tree of {@link JsonElement}s corresponding to the specified JSON + * @throws JsonParseException if the specified text is not valid JSON + * @since 1.3 + */ + public JsonElement parse(String json) throws JsonSyntaxException { + return parse(new StringReader(json)); + } + + /** + * Parses the specified JSON string into a parse tree + * + * @param json JSON text + * @return a parse tree of {@link JsonElement}s corresponding to the specified JSON + * @throws JsonParseException if the specified text is not valid JSON + * @since 1.3 + */ + public JsonElement parse(Reader json) throws JsonIOException, JsonSyntaxException { + try { + JsonReader jsonReader = new JsonReader(json); + JsonElement element = parse(jsonReader); + if (!element.isJsonNull() && jsonReader.peek() != JsonToken.END_DOCUMENT) { + throw new JsonSyntaxException("Did not consume the entire document."); + } + return element; + } catch (MalformedJsonException e) { + throw new JsonSyntaxException(e); + } catch (IOException e) { + throw new JsonIOException(e); + } catch (NumberFormatException e) { + throw new JsonSyntaxException(e); + } + } + + /** + * Returns the next value from the JSON stream as a parse tree. + * + * @throws JsonParseException if there is an IOException or if the specified + * text is not valid JSON + * @since 1.6 + */ + public JsonElement parse(JsonReader json) throws JsonIOException, JsonSyntaxException { + + json.setLenient(true); + + try { + return Streams.parse(json); + } catch (StackOverflowError e) { + throw new JsonParseException("Failed parsing JSON source: " + json + " to Json", e); + } catch (OutOfMemoryError e) { + throw new JsonParseException("Failed parsing JSON source: " + json + " to Json", e); + } + +// boolean lenient = json.isLenient(); +// try { +// return Streams.parse(json); +// } catch (StackOverflowError e) { +// throw new JsonParseException("Failed parsing JSON source: " + json + " to Json", e); +// } catch (OutOfMemoryError e) { +// throw new JsonParseException("Failed parsing JSON source: " + json + " to Json", e); +// } finally { +// json.setLenient(lenient); +// } + } +} diff --git a/src/main/java/com/shaya/poinila/android/utils/PonilaQueue.java b/src/main/java/com/shaya/poinila/android/utils/PonilaQueue.java new file mode 100755 index 0000000..7747096 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/utils/PonilaQueue.java @@ -0,0 +1,38 @@ +package com.shaya.poinila.android.utils; + +import java.util.LinkedList; +import java.util.Queue; + +/** + * Created by iran on 6/14/2016. + */ +public class PonilaQueue { + + private static PonilaQueue instance; + private LinkedList queue; + + private PonilaQueue(){ + queue = new LinkedList(); + } + + public static PonilaQueue getInstance(){ + + if(instance == null){ + instance = new PonilaQueue(); + } + + return instance; + } + + public void push(Object object){ + queue.add(object); + } + + public boolean isEmpty(){ + return queue.isEmpty(); + } + + public Object pop(){ + return queue.remove(); + } +} diff --git a/src/main/java/com/shaya/poinila/android/utils/PonilaSnackbarManager.java b/src/main/java/com/shaya/poinila/android/utils/PonilaSnackbarManager.java new file mode 100755 index 0000000..6d9d68c --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/utils/PonilaSnackbarManager.java @@ -0,0 +1,125 @@ +package com.shaya.poinila.android.utils; + +import android.content.Context; +import android.support.design.widget.Snackbar; +import android.support.v4.app.FragmentActivity; +import android.support.v4.content.ContextCompat; +import android.util.Log; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.view.dialog.DialogLauncher; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.Logger; + +import static com.shaya.poinila.android.util.ResourceUtils.*; + +/** + * Created by iran on 7/17/2016. + */ +public class PonilaSnackbarManager { + + private static PonilaSnackbarManager instance; + + public static final int PONILA_SNACKBAR_DEFAULT = 1; + public static final int PONILA_SNACKBAR_WITH_BUTTON = 2; + + private PonilaSnackbarManager(){ + + } + + public static PonilaSnackbarManager getInstance(){ + if(instance == null) + instance = new PonilaSnackbarManager(); + + return instance; + } + + public void showVerifySnackbar(View parentView, final FragmentActivity activity){ + + Snackbar snackbar = Snackbar + .make(parentView, R.string.snackbar_verify_message, Snackbar.LENGTH_INDEFINITE) + .setActionTextColor(activity.getResources().getColor(R.color.white)) + .setAction(R.string.ok, new View.OnClickListener() { + @Override + public void onClick(View view) { + DialogLauncher.launchInputVerificationCodeDialog(activity.getSupportFragmentManager(), "", false); + } + }); + + Snackbar.SnackbarLayout rootView = (Snackbar.SnackbarLayout)snackbar.getView(); + + rootView.setGravity(Gravity.CENTER); + + Button button = (Button)rootView.findViewById(android.support.design.R.id.snackbar_action); + + button.setBackgroundResource(R.drawable.snackbar_action); + + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + (int)activity.getResources().getDimension(R.dimen.snackbar_action_height)); + + button.setLayoutParams(layoutParams); + button.setPadding(0, 0, 0, 0); + + snackbar.show(); + + } + + public void showChangeUserPassSnackBar(View parentView, final FragmentActivity activity){ + + Snackbar snackbar = Snackbar + .make(parentView, R.string.change_user_pass_title, Snackbar.LENGTH_INDEFINITE) + .setActionTextColor(activity.getResources().getColor(R.color.white)) + .setAction(R.string.ok, new View.OnClickListener() { + @Override + public void onClick(View view) { + DialogLauncher.launchSetUsernamePasswordDialog(activity.getSupportFragmentManager()); } + }); + + Snackbar.SnackbarLayout rootView = (Snackbar.SnackbarLayout)snackbar.getView(); + + rootView.setGravity(Gravity.CENTER); + + Button button = (Button)rootView.findViewById(android.support.design.R.id.snackbar_action); + + button.setBackgroundResource(R.drawable.snackbar_action); + + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + (int)activity.getResources().getDimension(R.dimen.snackbar_action_height)); + + button.setLayoutParams(layoutParams); + button.setPadding(0, 0, 0, 0); + + snackbar.show(); + } +// +// private View customSnackBar(Context context, int type, int message, int btnText){ +// LinearLayout snackBarView = (LinearLayout)LayoutInflater.from(context).inflate(R.layout.ponila_snackbar, null); +// Button btn = (Button)snackBarView.findViewById(R.id.ponila_snackbar_btn); +// TextView textView = (TextView)snackBarView.findViewById(R.id.ponila_snackbar_text); +// +// switch (type){ +// case PONILA_SNACKBAR_DEFAULT: +// btn.setVisibility(View.GONE); +// break; +// case PONILA_SNACKBAR_WITH_BUTTON: +// btn.setVisibility(View.VISIBLE); +// break; +// } +// +// btn.setText(btnText != ConstantsUtils.NO_RESOURCE ? getString(btnText) : ""); +// textView.setText(message != ConstantsUtils.NO_RESOURCE ? getString(message) : ""); +// +// +// return snackBarView; +// } + +} diff --git a/src/main/java/com/shaya/poinila/android/utils/PushNotificationUtils.java b/src/main/java/com/shaya/poinila/android/utils/PushNotificationUtils.java new file mode 100755 index 0000000..427be76 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/utils/PushNotificationUtils.java @@ -0,0 +1,142 @@ +package com.shaya.poinila.android.utils; + +import android.content.Intent; +import android.util.Log; + +import com.shaya.poinila.android.presentation.PageChanger; +import com.shaya.poinila.android.presentation.PoinilaApplication; +import com.shaya.poinila.android.presentation.view.activity.OthersProfileActivity; +import com.shaya.poinila.android.util.NavigationUtils; + +import org.json.JSONArray; +import org.json.JSONObject; + +import data.PoinilaNetService; +import data.model.FriendRequestAnswer; +import manager.DataRepository; + +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_MEMBER_ID; + +/** + * Created by iran on 6/12/2016. + */ +public class PushNotificationUtils { + + // Notification Type + public enum NOTIFICATION_TYPE{ + POST_SUGGESTION, FRIENDSHIP_REQUEST, FRIENDSHIP_ANSWER, LIKE, FOLLOW, COMMENT + } + + // Notification Key + public static final String NOTIF_TYPE = "notif_type"; + public static final String MEMBERS = "members"; + public static final String NUMBER = "number"; + public static final String FULL_NAME = "member_full_name"; + public static final String MEMBER = "member"; + public static final String CIRCLE_ID = "circle_id"; + public static final String MEMBER_ID = "member_id"; + public static final String MEMBER_REQUEST = "member_request"; + public static final String MEMBER_ANSWER = "member_answer"; + public static final String COLLECTIONS = "collections"; + public static final String POSTS = "posts"; + + public static NOTIFICATION_TYPE getNotificationType(JSONObject notificationData){ + String notif_type = notificationData.optString(NOTIF_TYPE); + return NOTIFICATION_TYPE.valueOf(notif_type.toUpperCase()); + } + + private static void log(String str){ + Log.i(PushNotificationUtils.class.getName(), str); + } + + + public static void fireNotificationAction(JSONObject additionalData){ + + String actionSelected = additionalData.optString("actionSelected"); + switch (actionSelected){ + case "accept": + FriendshipAccept(additionalData); +// NotificationOpenedFriendship(additionalData); + break; + case "decline": + FriendshipDecline(additionalData); +// NotificationOpenedFriendship(additionalData); + break; + default: + fireNotificationOpened(additionalData); + + } + } + + public static void fireNotificationOpened(JSONObject additionalData){ + switch (getNotificationType(additionalData)){ + case POST_SUGGESTION: + notificationOpenedSuggestion(additionalData); + break; + case FRIENDSHIP_REQUEST: + NotificationOpenedFriendship(additionalData); + break; + case FRIENDSHIP_ANSWER: + NotificationOpenedFriendship(additionalData); + break; + case LIKE: + notificationOpenedLike(additionalData); + break; + case FOLLOW: + notificationOpenedFollow(additionalData); + break; + case COMMENT: + notificationOpenedComment(additionalData); + break; + } + } + + public static void notificationOpenedFollow(JSONObject additionalData){ + JSONArray collectionListJs = NotificationQueue.getInstance().get(NOTIFICATION_TYPE.FOLLOW.toString().toLowerCase()); + PageChanger.gotToNotificationActivity(NOTIFICATION_TYPE.FOLLOW, collectionListJs.toString()); + } + + public static void notificationOpenedLike(JSONObject additionalData){ + JSONArray postListJs = NotificationQueue.getInstance().get(NOTIFICATION_TYPE.LIKE.toString().toLowerCase()); + PageChanger.gotToNotificationActivity(NOTIFICATION_TYPE.LIKE, postListJs.toString()); + } + + public static void NotificationOpenedFriendship(JSONObject additionalData){ + String memberId = String.valueOf(additionalData.optInt("object")); + DataRepository.getInstance().putTempModel(null); + NavigationUtils.goToActivity(OthersProfileActivity.class, + PoinilaApplication.getAppContext(), KEY_MEMBER_ID, memberId, Intent.FLAG_ACTIVITY_NEW_TASK); + } + + public static void notificationOpenedComment(JSONObject additionalData){ + JSONArray postListJs = NotificationQueue.getInstance().get(NOTIFICATION_TYPE.COMMENT.toString().toLowerCase()); + PageChanger.gotToNotificationActivity(NOTIFICATION_TYPE.COMMENT, postListJs.toString()); + } + + public static void notificationOpenedSuggestion(JSONObject additionalData){ + + PageChanger.gotToNotificationActivity(NOTIFICATION_TYPE.POST_SUGGESTION, additionalData.optJSONArray("object").toString()); + } + + /** + * Friendship Accept + * @param additionalData + */ + public static void FriendshipAccept(JSONObject additionalData){ + int member_id = additionalData.optInt("object"); +// int circle_id = additionalData.optInt(""); + PoinilaNetService.answerFriendRequest(member_id, FriendRequestAnswer.ACCEPT, 0); + + } + + /** + * Friendship Decline + * @param additionalData + */ + public static void FriendshipDecline(JSONObject additionalData){ + int member_id = additionalData.optInt("object"); +// int circle_id = additionalData.optInt(CIRCLE_ID); + PoinilaNetService.answerFriendRequest(member_id, FriendRequestAnswer.REJECT, 0); + } + +} diff --git a/src/main/java/com/shaya/poinila/android/utils/TypefaceUtil.java b/src/main/java/com/shaya/poinila/android/utils/TypefaceUtil.java new file mode 100755 index 0000000..bca6f27 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/utils/TypefaceUtil.java @@ -0,0 +1,30 @@ +package com.shaya.poinila.android.utils; + +import android.content.Context; +import android.graphics.Typeface; + +import com.shaya.poinila.android.presentation.PoinilaApplication; + +import java.lang.reflect.Field; + +/** + * Created by iran on 2015-06-03. + * @author Alireza Farahani + * + * Change the entire Application by editting {@link Typeface} class using + * reflection. + */ +public class TypefaceUtil { + + private static Typeface iransans; + + static { + iransans = Typeface.createFromAsset(PoinilaApplication.getAppContext().getAssets(), "fonts/iransans.ttf"); + } + + public static Typeface getIranSansFont(){ + return iransans; + } + + +} diff --git a/src/main/java/com/shaya/poinila/android/utils/Utils.java b/src/main/java/com/shaya/poinila/android/utils/Utils.java new file mode 100755 index 0000000..edd591b --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/utils/Utils.java @@ -0,0 +1,39 @@ +package com.shaya.poinila.android.utils; + +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.net.Uri; +import android.provider.Settings; + +import com.shaya.poinila.android.presentation.PoinilaApplication; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by iran on 6/19/2016. + */ +public class Utils { + + public static String getBrowserAvailablePackageName(){ + Intent activityIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.example.com")); + PackageManager pm = PoinilaApplication.getAppContext().getPackageManager(); + List resolvedActivityList = pm.queryIntentActivities( + activityIntent, PackageManager.MATCH_ALL); + List packagesSupportingCustomTabs = new ArrayList<>(); + for (ResolveInfo info : resolvedActivityList) { + Intent serviceIntent = new Intent(); + serviceIntent.setAction("android.support.customtabs.action.CustomTabsService"); + serviceIntent.setPackage(info.activityInfo.packageName); + if (pm.resolveService(serviceIntent, 0) != null) { + packagesSupportingCustomTabs.add(info.activityInfo.packageName); + } + } + return packagesSupportingCustomTabs.size() > 0 ? packagesSupportingCustomTabs.get(0) : null; + } + + public static boolean isEnabledAutoRotate(){ + return android.provider.Settings.System.getInt(PoinilaApplication.getAppContext().getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0) == 1; + } +} diff --git a/src/main/java/com/shaya/poinila/android/utils/uisynchronize/UISynchronizeBus.java b/src/main/java/com/shaya/poinila/android/utils/uisynchronize/UISynchronizeBus.java new file mode 100755 index 0000000..831f625 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/utils/uisynchronize/UISynchronizeBus.java @@ -0,0 +1,73 @@ +package com.shaya.poinila.android.utils.uisynchronize; + +import android.content.Intent; +import android.content.IntentFilter; +import android.support.v4.content.LocalBroadcastManager; + +import com.shaya.poinila.android.presentation.PoinilaApplication; + +import java.io.Serializable; + +/** + * Created by iran on 7/3/2016. + */ +public class UISynchronizeBus { + + private static UISynchronizeBus instance; + private final String INTENT_FILTER_NAME = "com.shaya.poinila.ui.synchronize"; + private IntentFilter intentFilter; + private Intent intent; + private LocalBroadcastManager localBroadcastManager; + private UISynchronizeReceiver uiSynchronizeReceiver; + + public enum UI_SYNCHRONIZE_ACTION{ + OFF, + ALL, + UPDATE_DASHBOARD_POST + } + + + private UISynchronizeBus(){ + + uiSynchronizeReceiver = new UISynchronizeReceiver(); + + localBroadcastManager = LocalBroadcastManager.getInstance(PoinilaApplication.getAppContext()); + + intentFilter = new IntentFilter(INTENT_FILTER_NAME); + + for (UI_SYNCHRONIZE_ACTION action : UI_SYNCHRONIZE_ACTION.values()) { + intentFilter.addAction(action.toString()); + } + + intent = new Intent(INTENT_FILTER_NAME); + + + localBroadcastManager.registerReceiver( + uiSynchronizeReceiver, + intentFilter + ); + + } + + public UISynchronizeReceiver getReceiver(){ + return uiSynchronizeReceiver; + } + + public static UISynchronizeBus getInstance(){ + + if(instance == null){ + instance = new UISynchronizeBus(); + } + + return instance; + } + + public void sendData(UI_SYNCHRONIZE_ACTION action, Serializable data){ + intent.setAction(action.toString()); + intent.putExtra("data", data); + localBroadcastManager.sendBroadcast(intent); + } + + + +} diff --git a/src/main/java/com/shaya/poinila/android/utils/uisynchronize/UISynchronizeReceiver.java b/src/main/java/com/shaya/poinila/android/utils/uisynchronize/UISynchronizeReceiver.java new file mode 100755 index 0000000..0401b53 --- /dev/null +++ b/src/main/java/com/shaya/poinila/android/utils/uisynchronize/UISynchronizeReceiver.java @@ -0,0 +1,35 @@ +package com.shaya.poinila.android.utils.uisynchronize; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import java.io.Serializable; + +/** + * Created by iran on 7/3/2016. + */ +public class UISynchronizeReceiver extends BroadcastReceiver { + + private UISynchronizeBus.UI_SYNCHRONIZE_ACTION action; + private OnLoadDataSynchronizeListener onLoadDataSynchronizeListener; + + + @Override + public void onReceive(Context context, Intent intent) { + Serializable data = intent.getSerializableExtra("data"); + UISynchronizeBus.UI_SYNCHRONIZE_ACTION action = UISynchronizeBus.UI_SYNCHRONIZE_ACTION.valueOf(intent.getAction()); + if(action == onLoadDataSynchronizeListener.getSynchronizeAction() || action == UISynchronizeBus.UI_SYNCHRONIZE_ACTION.ALL) + onLoadDataSynchronizeListener.loadDataForSynchronize(data, action); + } + + public UISynchronizeReceiver setOnLoadDataSynchronizeListener(OnLoadDataSynchronizeListener onLoadDataSynchronizeListener) { + this.onLoadDataSynchronizeListener = onLoadDataSynchronizeListener; + return this; + } + + public interface OnLoadDataSynchronizeListener { + public UISynchronizeBus.UI_SYNCHRONIZE_ACTION getSynchronizeAction(); + public void loadDataForSynchronize(Serializable data, UISynchronizeBus.UI_SYNCHRONIZE_ACTION action); + } +} diff --git a/src/main/java/data/FriendRemovedEvent.java b/src/main/java/data/FriendRemovedEvent.java new file mode 100755 index 0000000..d1ae48d --- /dev/null +++ b/src/main/java/data/FriendRemovedEvent.java @@ -0,0 +1,13 @@ +package data; + + +import data.event.BaseEvent; + +/** + * Created by iran on 2015-11-14. + */ +public class FriendRemovedEvent extends BaseEvent { + public FriendRemovedEvent(int targetId) { + this.requestType = targetId; + } +} diff --git a/src/main/java/data/FriendRequestSentEvent.java b/src/main/java/data/FriendRequestSentEvent.java new file mode 100755 index 0000000..fa0c955 --- /dev/null +++ b/src/main/java/data/FriendRequestSentEvent.java @@ -0,0 +1,12 @@ +package data; + +import data.event.BaseEvent; + +/** + * Created by iran on 2015-11-14. + */ +public class FriendRequestSentEvent extends BaseEvent { + public FriendRequestSentEvent(int targetId) { + this.requestType = targetId; + } +} diff --git a/src/main/java/data/JsonRequestBodyMaker.java b/src/main/java/data/JsonRequestBodyMaker.java new file mode 100755 index 0000000..be97a0e --- /dev/null +++ b/src/main/java/data/JsonRequestBodyMaker.java @@ -0,0 +1,323 @@ +package data; + +import android.text.TextUtils; + +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.DeviceInfoUtils; + +import org.json.JSONArray; +import org.json.JSONException; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import data.model.Collection; +import data.model.FriendRequestAnswer; +import data.model.Member; +import data.model.Post; +import data.model.Tag; + +import static com.shaya.poinila.android.util.ConstantsUtils.OS_TYPE_ANDROID; +import static com.shaya.poinila.android.util.ConstantsUtils.PHONE; +import static com.shaya.poinila.android.util.ConstantsUtils.TABLET; + + +/** + * Created by iran on 2015-07-20. + */ +public class JsonRequestBodyMaker { + + public static Creator commentOnPost(String comment){ + return new Creator().put("comment", comment);//.toRequestPacketJsonObject(); + } + + public static Creator changePostColleciton(int destinationCollectionID){ + return new Creator().put("destination_collection_id", destinationCollectionID);//.toRequestPacketJsonObject(); + } + + public static Creator updateFriendCircles(List circleIDs, int friendID){ + return new Creator().put("circle_ids", circleIDs). + put("friend_member_id", friendID); + } + + public static Creator answerFriendRequest( + FriendRequestAnswer answer, int requesterID, int circleID){ + Creator creator = new Creator(). + put("requester_member_id", requesterID). + put("answer", answer.getAnswer());//.toRequestPacketJsonObject(); + if (circleID != -1) creator.put("circle_id", circleID); + return creator; + } + + /* public static HashMap uploadImage(String fileAddress){ + return new Creator().put("image", fileAddress).toRequestPacketJsonObject(); + } + + public static HashMap cropPostImage(int postID, int left, int top, int right, int down){ + return new Creator().put("post_id", postID).child(). + put("left", left).put("top", top).put("right", right).put("down", down) + .insertInParent("coordinates").toRequestPacketJsonObject(); + + }*/ + + /** + * Note that to-be-friend id will be send via url parameters + * @param circleIDs circle the user has set the friend in it. + * @return HashMap to send by post method via retrofit + */ + public static Creator friendRequest(List circleIDs){ + return new Creator().put("circle_ids", circleIDs); + } + + // TODO: you can use json ignore to send a post object instead + /** + * If any field is unchanged, send the Original one + * @param postID + * @param caption + * @param tags + * @return + */ + public static Creator repost(int postID, String caption, List tags){ + return new Creator().put("post_id", postID).put("caption", caption).put("tags", tags); + //toRequestPacketJsonObject(); + } + + public static Creator websitePost(Post post, String siteAddress, String imageAddress, String videoAddress){ + return new Creator() + .put("title", post.name) + .put("caption", post.summary) + .put("url", siteAddress) + .put("image_url", imageAddress) + .put("tags", post.tags) + .put("video_url", videoAddress) + .put("content", post.content);//.toRequestPacketJsonObject(); + } + + public static Creator createCircle(String name){ + return new Creator().put("name", name);//.toRequestPacketJsonObject(); + } + + public static Creator updateProfile(Member profile){ + /*String firstName, String lastName, String password, + String oldPassword, String email, Boolean isActive){*/ + return new Creator(). + put("full_name", profile.fullName). + put("email", profile.email). + put("description", profile.aboutMe). + put("mobile_number", profile.mobileNumber). + //put("is_active", profile.isActive). + put("url", profile.url). + put("url_name", profile.urlName) + .put("type", "people"); + //.toRequestPacketJsonObject(); + } + + public static Creator collectionID(int collectionID) { + return new Creator().put("collection_id", collectionID);//.toRequestPacketJsonObject(); + } + + public static Creator loginParams(String uniqueName, String email, String password) { + return new Creator(). + put("unique_name", uniqueName). + put("email", email). + put("password", password). + put("device_os_type", OS_TYPE_ANDROID). + put("device_type", DeviceInfoUtils.isTablet() ? TABLET : PHONE). + put("device_model", DeviceInfoUtils.MODEL). + put("device_unique_identifier", DeviceInfoUtils.ANDROID_ID). + put("device_api_version", ConstantsUtils.PONILA_API_VERSION). + put("device_os_version", DeviceInfoUtils.SDK_INT). + put("device_brand", DeviceInfoUtils.MANUFACTURER). + put("client_version", DeviceInfoUtils.CLIENT_VERSION_CODE). + put("is_browser", false); + //toRequestPacketJsonObject(); + } + + public static Creator loginByGoogleParams(String tokenId) { + return new Creator(). + put("token_id", tokenId). + put("device_os_type", OS_TYPE_ANDROID). + put("device_type", DeviceInfoUtils.isTablet() ? TABLET : PHONE). + put("device_model", DeviceInfoUtils.MODEL). + put("device_unique_identifier", DeviceInfoUtils.ANDROID_ID). + put("device_api_version", ConstantsUtils.PONILA_API_VERSION). + put("device_os_version", DeviceInfoUtils.SDK_INT). + put("device_brand", DeviceInfoUtils.MANUFACTURER). + put("client_version", DeviceInfoUtils.CLIENT_VERSION_CODE). + put("is_browser", false); + //toRequestPacketJsonObject(); + } + + public static Creator setUsernamePassword(String uniqueName, String password, String gToken){ + return new Creator() + .put("unique_name", uniqueName) + .put("password", password) + .put("google_token", gToken); // same auth token + } + + public static Creator OnOffSetting(int typeID, int value) { + return new Creator().put("type_id", typeID).put("value", value); + } + + public static Creator emptyPacket() { + return new Creator(); + } + + public static Creator userInterests(List selectedInterests) { + return new Creator().putArrayAsData(selectedInterests); + } + + public static Creator createTextPost(Post post) { + return new Creator().putObjectAsData(post); + } + + public static Creator createCollection(Collection collection) { + return new Creator().putObjectAsData(collection); + } + + public static Creator inviteToPoinila(String email, String message) { + return new Creator().put("email", email).put("message", message).put("suppress_warning", true); + } + + public static Creator contactUs(String type, String title, String content) { + return new Creator().put("type", type).put("title", title).put("content", content); + } + + public static Creator email(String email) { + return new Creator().put("email", email); + } + + public static Creator requestVerify(boolean byEmail, String phoneOrMobile, int memberId) { + return new Creator() + .put(byEmail ? "email" : "mobile_number", phoneOrMobile) + .put("member_id", memberId == 0 ? null : memberId); + } + + public static Creator phoneNumber(String phone) { + return new Creator().put("mobile_number", phone); + } + + public static Creator uniqueName(String phone) { + return new Creator().put("unique_name", phone); + } + + public static Creator changePassword(String password, String oldPassword){ + return new Creator(). + put("is_browser", false). + put("device_unique_identifier", DeviceInfoUtils.ANDROID_ID). + put("client_version", DeviceInfoUtils.CLIENT_VERSION_CODE). + put("password", password). + put("old_password", oldPassword); + } + + public static Creator resetPassword(String newPassword, String code) { + return new Creator(). + put("password", newPassword). + put("reset_password_hash", code). + put("is_browser", false). + put("device_unique_identifier", DeviceInfoUtils.ANDROID_ID). + put("client_version", DeviceInfoUtils.CLIENT_VERSION_CODE); + } + + public static Creator reportMemberOrPost(int memberIdOrPostId) { + return new Creator(). + put("memberIdOrPostId", memberIdOrPostId); + } + + public static Creator postIdList(JSONArray ids) { + + List list = new ArrayList<>(); + + int length = ids.length(); + for(int i=0 ; i < length ; i ++){ + try { + list.add(ids.getInt(i)); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + return new Creator(). + put("post_id_list", list); + } + + public static Creator register(String verificationCode, String fullName, String userName, String gender, String password, String email, String phone) { +// return loginParams(userName, email, password).put("verification_code", verificationCode).put("full_name", fullName) +// .put("phone", phone) +// .put("gender", gender); + + return loginParams(userName, email, password).put("full_name", fullName) + .put("mobile_number", phone) + .put("gender", gender); + } + + public static Creator verifyPhoneOrMobile(String verificationCode, int memberId, String mobileOrPhone, boolean byEmail){ + + if(TextUtils.isEmpty(mobileOrPhone)){ + return new Creator() + .put("verification_code", verificationCode) + .put("member_id", memberId); + }else { + return new Creator() + .put(byEmail ? "email" : "mobile_number" , mobileOrPhone) + .put("verification_code", verificationCode) + .put("member_id", memberId); + } + } + + public static class Creator{ + private static final String METHOD = "action"; + private static final String BODY = "data"; + /*JSONObject jsonHashMap; + JSONArray jsonArray;*/ + HashMap jsonHashMap; + List jsonArray; + Creator parent; + private Object jsonObject; + + public Creator(){ + jsonHashMap = new HashMap<>(); + } + + public Creator put(String key, Object value){ + if (value != null) + jsonHashMap.put(key, value); + return this; + } + + public Creator putArrayAsData(List list){ + if (list != null) { + jsonArray = list; + } + return this; + } + + public Creator putObjectAsData(Object object){ + if (object != null){ + jsonObject = object; + } + return this; + } + + public HashMap toRequestPacketJsonObject(String method){ + HashMap packet = new HashMap<>(); + packet.put(METHOD, method); + if (jsonObject != null) packet.put(BODY, jsonObject); + else if (jsonArray != null) packet.put(BODY, jsonArray); + else packet.put(BODY, jsonHashMap); + return packet; + } + +/* public Creator child() { + Creator c = new Creator(); + c.parent = this; + return c; + } + + public Creator insertInParent(String key){ + this.parent.put(key, toRequestPacketJsonObject()); + return this.parent; + }*/ + } +} diff --git a/src/main/java/data/PoinilaCallback.java b/src/main/java/data/PoinilaCallback.java new file mode 100755 index 0000000..7d36397 --- /dev/null +++ b/src/main/java/data/PoinilaCallback.java @@ -0,0 +1,127 @@ +package data; + +import android.content.Intent; +import android.support.v4.content.LocalBroadcastManager; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.google.gson.reflect.TypeToken; +import com.google.gson.stream.JsonReader; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.util.ConnectionUitls; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.ContextHolder; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.utils.PonilaJsonParser; + +import java.io.StringReader; +import java.lang.reflect.Type; + +import data.model.PoinilaResponse; +import retrofit.Callback; +import retrofit.RetrofitError; +import retrofit.client.Response; +import retrofit.mime.TypedByteArray; + +/** + * Created by iran on 2015-09-01. + */ +public abstract class PoinilaCallback implements Callback { + + public static String getStringFromResponse(Response response){ + return new String(((TypedByteArray) response.getBody()).getBytes()); + } + + public boolean handleError(){ + return false; + } + /* public boolean validate(T poinilaResponse, Response response){ + + }*/ + + @Override + public final void success(Response response, Response response2){ +// JsonElement jsonElement = new JsonParser().parse(getStringFromResponse(response)); + // PonilaJsonParser set Lenient to True for malformed json + JsonElement jsonElement = new PonilaJsonParser().parse(getStringFromResponse(response)); + int code = jsonElement.getAsJsonObject().get("code").getAsInt(); + //if (validate(poinilaResponse, response)) + switch (code){ + case 401: + + LocalBroadcastManager.getInstance(ContextHolder.getContext()).sendBroadcast(new Intent(ConstantsUtils.INTENT_FILTER_JWT)); + break; + + case 200: + + JsonElement dataElement = jsonElement.getAsJsonObject().get("data"); + if (dataElement.isJsonArray()) + dataList = dataElement.getAsJsonArray(); + else { + dataList = new JsonArray(); + dataList.add(dataElement); + } + JsonReader reader = new JsonReader(new StringReader(jsonElement.toString())); + reader.setLenient(true); + T t = PoinilaNetService.getGson().fromJson(reader, getType()); +// T t = PoinilaNetService.getGson().fromJson(jsonElement, getType()); + poinilaSuccess(t, response); + + break; + + default: + PoinilaResponse poinilaResponse = PoinilaNetService.getGson().fromJson(jsonElement, getPoinilaResponseType()); + if (!poinilaError(poinilaResponse)){ + Logger.toast(R.string.error_sth_bad_happened); + } + } + + } + + //public abstract Type getType(); + + public Type getType(){ + return getPoinilaResponseType(); + } + + private Type getPoinilaResponseType() { + return new TypeToken(){}.getType(); + } + + + public JsonArray getDataList() { + return dataList; + } + + JsonArray dataList; + + + @Override + public final void failure(RetrofitError error) { + switch (error.getKind()){ + case NETWORK: + if (ConnectionUitls.isNetworkOnline()) { + Logger.toast(R.string.error_unable_to_connect); + } + break; + case CONVERSION: // error on converting response + case UNEXPECTED: + throw error; + case HTTP: // 503, 502, etc + // do nothing for now + } + } + + public abstract void poinilaSuccess(T ponilaResponse, Response response); + + /** + * hook method for handling poinila responses. (Http status is 200) + *
Can consume the call so default error handling doesn't occur. + * @param poinilaResponse standard Poinila response packet. {@link PoinilaResponse#data} type is not known. + * @return true if you want to disable parent default handling, false otherwise. + */ + public boolean poinilaError(PoinilaResponse poinilaResponse){ + return false; + } +} diff --git a/src/main/java/data/PoinilaNetService.java b/src/main/java/data/PoinilaNetService.java new file mode 100755 index 0000000..f6b396f --- /dev/null +++ b/src/main/java/data/PoinilaNetService.java @@ -0,0 +1,1985 @@ +package data; + +import android.content.Intent; +import android.graphics.Bitmap; +import android.support.annotation.Nullable; +import android.support.v4.content.LocalBroadcastManager; + +import com.google.gson.ExclusionStrategy; +import com.google.gson.FieldAttributes; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.reflect.TypeToken; +import com.raizlabs.android.dbflow.annotation.NotNull; +import com.raizlabs.android.dbflow.structure.ModelAdapter; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.presentation.uievent.GoogleLoginSucceedEvent; +import com.shaya.poinila.android.presentation.uievent.SuggestionPosts; +import com.shaya.poinila.android.presentation.uievent.UpdateNewPostDialogEvent; +import com.shaya.poinila.android.presentation.uievent.UpdateUiRepostEvent; +import com.shaya.poinila.android.presentation.view.activity.SettingActivity; +import com.shaya.poinila.android.presentation.view.dialog.ForgotPasswordFragment; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.ConnectionUitls; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.ContextHolder; +import com.shaya.poinila.android.util.DeviceInfoUtils; +import com.shaya.poinila.android.util.ImageUtils; +import com.shaya.poinila.android.util.Logger; +import com.shaya.poinila.android.util.PoinilaPreferences; +import com.shaya.poinila.android.util.StringUtils; +import com.shaya.poinila.android.utils.PonilaAccountManager; +import com.squareup.okhttp.Interceptor; +import com.squareup.okhttp.OkHttpClient; +import com.squareup.okhttp.Request; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import data.event.AbstractNotificationsReceivedEvent; +import data.event.AnswerFriendRequestResponse; +import data.event.BaseEvent; +import data.event.CircleReceivedEvent; +import data.event.CollectionReceivedEvent; +import data.event.CollectionUpdatedEvent; +import data.event.CollectionsReceivedEvent; +import data.event.CommentReceivedEvent; +import data.event.CommentsReceivedEvent; +import data.event.ContentReceivedEvent; +import data.event.DashboardEvent; +import data.event.FailEvent; +import data.event.FrameReceivedEvent; +import data.event.InterestsReceivedEvent; +import data.event.InviteUsedEvent; +import data.event.LoadingImagedFailedEvent; +import data.event.LoginFailedEvent; +import data.event.LoginSucceedEvent; +import data.event.MemberReceivedEvent; +import data.event.MembersReceivedEvent; +import data.event.ModelCreatedEvent; +import data.event.ModelDeletedEvent; +import data.event.ModelUpdatedEvent; +import data.event.MyFriendshipRequestsEvent; +import data.event.MyInfoReceivedEvent; +import data.event.NotificationSettingsReceivedEvent; +import data.event.PostReceivedEvent; +import data.event.PostsReceivedEvent; +import data.event.ProfileDirtyEvent; +import data.event.ProfileSettingReceivedEvent; +import data.event.RegisterResponseEvent; +import data.event.RemainedInvitesEvent; +import data.event.ServerResponseEvent; +import data.event.SuggestedWebpagePostReceived; +import data.event.SystemPreferencesReceivedEvent; +import data.event.TopicsReceivedEvent; +import data.event.UndoFavePostEvent; +import data.event.UndoUnfavePostEvent; +import data.event.UpdateProfileSettingResponse; +import data.event.UserInterestsReceivedEvent; +import data.event.UserNameValidityEvent; +import data.event.VerificationRequestResponse; +import data.model.Circle; +import data.model.Collection; +import data.model.Comment; +import data.model.Content; +import data.model.DefaultType; +import data.model.Frame; +import data.model.FriendRequestAnswer; +import data.model.Gender; +import data.model.ImageTag; +import data.model.InvitationNotif; +import data.model.Member; +import data.model.Notification; +import data.model.OnOffSetting; +import data.model.PoinilaInvite; +import data.model.PoinilaResponse; +import data.model.Post; +import data.model.PostType; +import data.model.SuggestedWebPagePost; +import data.model.SystemPreferences; +import data.model.Tag; +import data.model.Topic; +import manager.DBFacade; +import retrofit.Callback; +import retrofit.RequestInterceptor; +import retrofit.RestAdapter; +import retrofit.RetrofitError; +import retrofit.client.Header; +import retrofit.client.OkClient; +import retrofit.client.Response; +import retrofit.converter.GsonConverter; +import retrofit.mime.TypedByteArray; +import retrofit.mime.TypedOutput; +import static com.shaya.poinila.android.util.ConstantsUtils.CONNECT_TIME_OUT_MILLISECONDS; +import static com.shaya.poinila.android.util.ConstantsUtils.HEADER_AUTH; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_JSON_DATA_ROOT; +import static com.shaya.poinila.android.util.ConstantsUtils.KEY_JSON_OWNED_COLLECTIONS; +import static com.shaya.poinila.android.util.ConstantsUtils.POINILA_DATE_FORMAT; +import static com.shaya.poinila.android.util.ConstantsUtils.READ_TIME_OUT_MILLISECONDS; +import static com.shaya.poinila.android.util.ConstantsUtils.WRITE_TIME_OUT_MILLISECONDS; +import static com.shaya.poinila.android.util.ConstantsUtils.SHOULD_SET_INTEREST; +import static com.shaya.poinila.android.util.ImageUtils.convertBitmapToByteArray; +import static com.shaya.poinila.android.util.ResourceUtils.getString; + +/** + * Created by iran on 2015-06-30. + */ +public class PoinilaNetService { + private static final String MULTIPART_FORMDATA = "multipart/form-data"; + private static final String MYME_TYPE_IMAGE = "image/*"; + public static final String POST = "post"; + public static final String PUT = "put"; + public static final String DELETE = "delete"; + private static RestServices restServices; + + public static Gson gson; + private static HashSet cookies = new HashSet<>(); + + public static Gson getGson() { + return gson; + } + + private static final RestAdapter restAdapter; + + private static final GsonConverter gsonConverter; + + private static final OkHttpClient okClient; + + static { + okClient = new OkHttpClient(); + okClient.networkInterceptors().add(new AddCookiesInterceptor()); + okClient.networkInterceptors().add(new ReceivedCookiesInterceptor()); + okClient.networkInterceptors().add(new AgentAndVersionInterceptor()); + okClient.setConnectTimeout(CONNECT_TIME_OUT_MILLISECONDS, TimeUnit.MILLISECONDS); + okClient.setReadTimeout(READ_TIME_OUT_MILLISECONDS, TimeUnit.MILLISECONDS); + okClient.setWriteTimeout(WRITE_TIME_OUT_MILLISECONDS, TimeUnit.MILLISECONDS); + + gson = new GsonBuilder(). + setDateFormat(POINILA_DATE_FORMAT). + setExclusionStrategies(new ExclusionStrategy() { + @Override + public boolean shouldSkipField(FieldAttributes f) { + // because our models inherite from DBFlow Model class which have a protected + // field of ModelAdapter class. we skip it for the sake of object marshalling. + return f.getDeclaredClass().equals(ModelAdapter.class); + } + + @Override + public boolean shouldSkipClass(Class clazz) { + return false; + } + }).create(); + + gsonConverter = new GsonConverter(gson); + + // with removing requestInterceptor in retrofit 2.x this must be done using okhttp interceptor. + // like adding and updating cookie we do already + RequestInterceptor requestInterceptor = new RequestInterceptor() { + @Override + public void intercept(RequestFacade request) { + request.addHeader("Authorization", PoinilaPreferences.getAuthToken()); + //request.addHeader(""); + } + }; + + //String[] ipPort = StorageUtils.readIpPortFromFile(); + restAdapter = new RestAdapter.Builder(). + //setEndpoint(ConstantsUtils.POINILA_BASE_URL). + setEndpoint(ConstantsUtils.POINILA_SERVER_ADDRESS). + setLogLevel(RestAdapter.LogLevel.NONE). // HEADERS_AND_ARGS). + setRequestInterceptor(requestInterceptor). + setClient(new OkClient(okClient)). + setConverter(gsonConverter). + //setErrorHandler(new RetrofitErrorHandler()). + build(); + restServices = restAdapter.create(RestServices.class); + + + } + + public static void setEmailNotificationSetting(OnOffSetting setting) { + restServices.setEmailNotificationSetting( + JsonRequestBodyMaker.OnOffSetting(setting.typeID, setting.value).toRequestPacketJsonObject(PUT), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + + } + }); + } + + public static void setApplicationNotificationSetting(OnOffSetting setting) { + restServices.setApplicationNotification( + JsonRequestBodyMaker.OnOffSetting(setting.typeID, setting.value).toRequestPacketJsonObject(PUT), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + + } + + }); + } + + public static void getMemberInterests(String memberID) { + restServices.getMemberInterests(memberID, new PoinilaCallback>>() { + + @Override + public void poinilaSuccess(PoinilaResponse> poinilaResponse, Response response) { + postEvent(new UserInterestsReceivedEvent(poinilaResponse.data)); + } + + @Override + public Type getType() { + return new TypeToken>>() { + }.getType(); + } + }); + + } + + public static void removeInterest(Tag tag) { + restServices.removeInterest(PoinilaPreferences.getMyId(), tag.getId(), + JsonRequestBodyMaker.emptyPacket().toRequestPacketJsonObject(DELETE), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + } + + }); + } + + public static void getInterests() { + restServices.getInterests(new PoinilaCallback>>() { + + @Override + public void poinilaSuccess(PoinilaResponse> listPoinilaResponse, Response response) { + postEvent(new InterestsReceivedEvent(listPoinilaResponse.data)); + } + + @Override + public Type getType() { + return new TypeToken>>() { + }.getType(); + } + }); + } + + public static void getSubInterests(final String superInterestID) { + restServices.getSubInterest(superInterestID, new PoinilaCallback>>() { + @Override + public void poinilaSuccess(PoinilaResponse> poinilaResponse, Response response) { + postEvent(new InterestsReceivedEvent(poinilaResponse.data, superInterestID)); + } + + @Override + public Type getType() { + return new TypeToken>>() { + }.getType(); + } + }); + + } + + public static void updateUserInterests(List selectedInterests) { + restServices.updateUserInterests(PoinilaPreferences.getMyId(), + JsonRequestBodyMaker.userInterests(selectedInterests).toRequestPacketJsonObject(PUT), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + postEvent(new ServerResponseEvent(true, BaseEvent.ReceiverName.SelectInterest)); + } + + @Override + public boolean poinilaError(PoinilaResponse poinilaResponse) { + postEvent(new ServerResponseEvent(false, BaseEvent.ReceiverName.SelectInterest)); + return super.poinilaError(poinilaResponse); + } + }); + } + + public static void uploadProfilePicture(Bitmap croppedImage) { + + restServices.uploadProfilePic(PoinilaPreferences.getMyId(), POST, + new PoinilaTypedByteArray(MYME_TYPE_IMAGE, + convertBitmapToByteArray( + ImageUtils.resizeBitmapForProfilePic(croppedImage))), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + postEvent(new ProfileDirtyEvent()); + } + + }); + } + + public static void getRemainedInvites() { + restServices.getRemainedInvites(new PoinilaCallback>() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + int remained = poinilaResponse.data.limit - poinilaResponse.data.usedInvites; + postEvent(new RemainedInvitesEvent(remained)); + } + + @Override + public boolean poinilaError(PoinilaResponse error) { + postEvent(new RemainedInvitesEvent(0)); + return true; + } + + @Override + public Type getType() { + return new TypeToken>() { + }.getType(); + } + }); + } + + public static void inviteToPoinila(String email, String message) { + restServices.inviteToPoinila( + JsonRequestBodyMaker.inviteToPoinila(email, message).toRequestPacketJsonObject(POST), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + // Order of events are important! first one updates the data layer witch + // view invoked by second one uses that. (Otto dispatches the events synchronously. + postEvent(new RemainedInvitesEvent(-1)); // -1 for decrementing + postEvent(new InviteUsedEvent()); + } + }); + } + + public static void getWebsiteInfo(String address, PostType type) { + String typeString = ""; + switch (type){ + case TEXT: + typeString = "txt"; + break; + case IMAGE: + typeString = "img"; + break; + case VIDEO: + typeString = "video"; + break; + } + restServices.getWebsiteInfo(typeString, address, + new PoinilaCallback>() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + postEvent(new SuggestedWebpagePostReceived(poinilaResponse.data)); + } + + @Override + public boolean poinilaError(PoinilaResponse poinilaResponse) { + postEvent(new LoadingImagedFailedEvent()); + return true; + } + + @Override + public Type getType() { + return new TypeToken>() { + }.getType(); + } + + }); + } + + public static void createReferencedPost(String collectionID, Post newPost, String siteAddress, String imageAddress, String videoAddress) { + restServices.createWebsitePost(collectionID, + JsonRequestBodyMaker.websitePost(newPost, siteAddress, imageAddress, videoAddress).toRequestPacketJsonObject(POST), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + Logger.toast(R.string.successfully_created); + } + }); + } + + public static void recoverPassword(ForgotPasswordFragment.RECOVERY_PASS_TYPE recoveryPassType, String emailOrPhone) { + HashMap data = null; + switch (recoveryPassType){ + case EMAIL: + data = JsonRequestBodyMaker.email(emailOrPhone).toRequestPacketJsonObject(POST); + break; + case MOBILE_NUMBER: + data = JsonRequestBodyMaker.phoneNumber(emailOrPhone).toRequestPacketJsonObject(POST); + break; + case UNIQUE_NAME: + data = JsonRequestBodyMaker.uniqueName(emailOrPhone).toRequestPacketJsonObject(POST); + break; + + } + + + restServices.recoverPassword(data, + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse ponilaResponse, Response response) { + Logger.toast(R.string.successful_recovery); + postEvent(new VerificationRequestResponse(true)); // avoiding duplicate events + } + + @Override + public boolean poinilaError(PoinilaResponse poinilaResponse) { + if (poinilaResponse.code == 446) { + postEvent(new VerificationRequestResponse(false, poinilaResponse.code)); + return true; + } + return super.poinilaError(poinilaResponse); + } + }); + } + + public static void resetPassword(final String newPassword, String code) { + restServices.resetPassword(JsonRequestBodyMaker.resetPassword(newPassword, code).toRequestPacketJsonObject(POST), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse ponilaResponse, Response response) { + // TODO: go To login page + String authToken = getJWTTokenFromHeaders(response); + if (authToken != null) { + PoinilaPreferences.putAuthToken(authToken); + + String userName = ((ArrayList)ponilaResponse.data).get(1); + PonilaAccountManager.getInstance().addPonilaAccount( + userName, newPassword, authToken + ); + +// PonilaAccountManager.getInstance().updatePonilaAccount(newPassword, authToken); + + postEvent(new LoginSucceedEvent()); + }/*else{ + postEvent(new LoginFailedEvent()); + }*/ + //Logger.debugToast("password reset successfully but auth token was not set"); + } + + @Override + public boolean poinilaError(PoinilaResponse poinilaResponse) { + postEvent(new LoginFailedEvent(poinilaResponse.code, null)); + + return true; + } + }); + } + + public static void requestVerificationCode(final boolean byEmail, final String emailOrPhone) { + restServices.requestVerificationCode(byEmail ? + JsonRequestBodyMaker.email(emailOrPhone).toRequestPacketJsonObject(POST) : + JsonRequestBodyMaker.phoneNumber(emailOrPhone).toRequestPacketJsonObject(POST), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse ponilaResponse, Response response) { + postEvent(new VerificationRequestResponse(true, byEmail, emailOrPhone)); + } + + @Override + public boolean poinilaError(PoinilaResponse poinilaResponse) { + switch (poinilaResponse.code) { + case 425: + postEvent(new VerificationRequestResponse(false, + getString(byEmail ? + R.string.error_already_registered_email : + R.string.error_already_registered_phone))); + break; + case 423: + postEvent(new VerificationRequestResponse(false, getString(R.string.error_invalid_phone_no))); + break; + } + return true; + } + }); + } + + public static void requestVerificationCode(final boolean byEmail, final String emailOrPhone, int memberId) { + restServices.requestVerificationCode( + JsonRequestBodyMaker.requestVerify(byEmail, emailOrPhone, memberId).toRequestPacketJsonObject(POST), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse ponilaResponse, Response response) { + postEvent(new VerificationRequestResponse(true, byEmail, emailOrPhone)); + } + + @Override + public boolean poinilaError(PoinilaResponse poinilaResponse) { + switch (poinilaResponse.code) { + case 425: + postEvent(new VerificationRequestResponse(false, + getString(byEmail ? + R.string.error_already_registered_email : + R.string.error_already_registered_phone))); + break; + case 423: + postEvent(new VerificationRequestResponse(false, getString(R.string.error_invalid_phone_no))); + break; + } + return true; + } + }); + } + + public static void register(String verificationCode, String fullName, final String userName, Gender gender, final String password, boolean byEmail, String emailOrPhone) { + restServices.register(JsonRequestBodyMaker.register( + verificationCode, fullName, userName, gender.name().toLowerCase(), password, byEmail ? emailOrPhone : "", !byEmail ? emailOrPhone : ""). + toRequestPacketJsonObject(POST), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse ponilaResponse, Response response) { + /*postEvent(new RegisterResponseEvent(true));*/ + String authToken = getJWTTokenFromHeaders(response); + PoinilaPreferences.putAuthToken(authToken); + PonilaAccountManager.getInstance().addPonilaAccount( + userName, password, authToken + ); +// PonilaAccountManager.getInstance(). + postEvent(new RegisterResponseEvent(true)); + } + + @Override + public boolean poinilaError(PoinilaResponse poinilaResponse) { + if (poinilaResponse.code == ConstantsUtils.CODE_NO_OCCURRENCE) + postEvent(new RegisterResponseEvent(false, RegisterResponseEvent.USED_VERIFICATION_CODE)); + return true; + } + }); + } + + public static void checkUserNameValidity(final String tempUserName) { + restServices.checkUserNameValidity(tempUserName, new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse ponilaResponse, Response response) { + //postEvent(new UserNameValidityEvent(true)); + } + + @Override + public boolean poinilaError(PoinilaResponse poinilaResponse) { + int error = 0; + switch (poinilaResponse.code) { + case ConstantsUtils.ERROR_DUPLICATE: + error = UserNameValidityEvent.DUPLICATE; + break; + case ConstantsUtils.ERROR_BAD_KEYWORD: + error = UserNameValidityEvent.RESERVED; + break; + case ConstantsUtils.ERROR_RULE_EXCEPTION: + error = (tempUserName.length() < 6 || tempUserName.length() > 32) ? UserNameValidityEvent.LENGTH : UserNameValidityEvent.RULE; + break; + } + postEvent(new UserNameValidityEvent(false, error)); + return true; + } + }); + } + + public static void login(final String uniqueName, final String email, final String password) { + restServices.login(JsonRequestBodyMaker.loginParams(uniqueName, email, password).toRequestPacketJsonObject(POST), + new Callback() { + @Override + public void success(Response response, Response response2) { + PoinilaResponse poinilaResponse = gson.fromJson(getStringFromResponse(response), PoinilaResponse.class); + if (poinilaResponse.code == 200) { + String authToken = getJWTTokenFromHeaders(response); + PoinilaPreferences.putAuthToken(authToken); + PonilaAccountManager.getInstance().addPonilaAccount( + uniqueName != null ? uniqueName : email, password, authToken + ); + postEvent(new LoginSucceedEvent()); + //} else if (poinilaResponse.code == 401) { + } else if (poinilaResponse.code == 401) { + JsonObject responseJson = new JsonParser().parse(getStringFromResponse(response)).getAsJsonObject(); + JsonObject dataJson = responseJson.get(KEY_JSON_DATA_ROOT).getAsJsonObject(); + /*if (params.has("parameter")){ + params.get("parameter"); + }*/ + postEvent(new LoginFailedEvent(poinilaResponse.code, dataJson)); + } else { + failure(null); + } + } + + @Override + public void failure(RetrofitError error) { + postEvent(new LoginFailedEvent(0, null)); + Logger.toast(R.string.error_sth_bad_happened); + } + }); + } + + public static void loginByGoogle(String tokenId){ + restServices.loginByGoogle(JsonRequestBodyMaker.loginByGoogleParams(tokenId).toRequestPacketJsonObject(POST), + new Callback() { + + @Override + public void success(Response response, Response response2) { + PoinilaResponse poinilaResponse = gson.fromJson(getStringFromResponse(response), PoinilaResponse.class); + if (poinilaResponse.code == 200) { + String authToken = getJWTTokenFromHeaders(response); + +// Log.i(getClass().getName(), "poinilaResponse = " + poinilaResponse.data); + + GoogleLoginSucceedEvent loginEvent = new GoogleLoginSucceedEvent(); + try { + loginEvent.firstLoginDoneByGoogle = new JSONObject( + poinilaResponse.data.toString()).optBoolean(SHOULD_SET_INTEREST); + } catch (JSONException e) { + e.printStackTrace(); + } + + if(PonilaAccountManager.getInstance().getGoogleAccount() != null){ + PonilaAccountManager.getInstance().addPonilaAccountFromGoogle(authToken); + postEvent(loginEvent); + }else + Logger.toast(R.string.error_google_account_not_found); + + }else if (poinilaResponse.code == 401) { + JsonObject responseJson = new JsonParser().parse(getStringFromResponse(response)).getAsJsonObject(); + JsonObject dataJson = responseJson.get(KEY_JSON_DATA_ROOT).getAsJsonObject(); + /*if (params.has("parameter")){ + params.get("parameter"); + }*/ + postEvent(new LoginFailedEvent(poinilaResponse.code, dataJson)); + }else { + failure(null); + } + + } + + @Override + public void failure(RetrofitError error) { + Logger.toast(R.string.error_sth_bad_happened); + } + }); + } + + private static String getJWTTokenFromHeaders(Response response) { + for (Header header : response.getHeaders()) { + if (header.getName().equals(HEADER_AUTH)) + return header.getValue(); + } + return null; + } + + public static void logout() { + if (ConnectionUitls.isNetworkOnline()) { // hameye requesta bayad to "ye noghte" az hamchin logici obur konan + restServices.logout(new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + } + }); + } + } + + /** + * @param bookmark passing null value ignores the query parameter + */ + public static void getSuggestions(final String bookmark) { + restServices.getSuggestions(bookmark, new PoinilaCallback>>() { + @Override + public void poinilaSuccess(PoinilaResponse> poinilaResponse, Response response) { + for (int i = 0; i < poinilaResponse.data.size(); i++) { + Post post = poinilaResponse.data.get(i); + post.jsonContent = getDataList().get(i).toString(); + } + postEvent(new DashboardEvent(poinilaResponse.data, false, poinilaResponse.bookmark)); + } + + @Override + public Type getType() { + return new TypeToken>>() { + }.getType(); + } + }); + } + + + public static void getFollowedCollections(String memberId, String frameID, String bookmark, + final BaseEvent.ReceiverName receiverTag) { + restServices.getMemberFollowingCollections(memberId, bookmark, frameID, + new PoinilaCallback>>() { + + @Override + public void poinilaSuccess(PoinilaResponse> collections, Response response) { + postEvent(new CollectionsReceivedEvent(collections.data, + collections.bookmark, receiverTag)); + } + + @Override + public Type getType() { + return new TypeToken>>() { + }.getType(); + } + }); + } + + public static void searchPostWithQuery(List query, String bookmark) { + restServices.getPostsWithQuery(query, bookmark, new PoinilaCallback>>() { + @Override + public void poinilaSuccess(PoinilaResponse> listPoinilaResponse, Response response) { + postEvent(new PostsReceivedEvent(listPoinilaResponse.data, + listPoinilaResponse.bookmark, BaseEvent.ReceiverName.SearchFragment)); + } + + + @Override + public Type getType() { + return new TypeToken>>() { + }.getType(); + } + }); + } + + public static void searchCollectionsWithQuery(List query, String bookmark) { + restServices.getCollectionsWithQuery(query, bookmark, new PoinilaCallback>>() { + @Override + public void poinilaSuccess(PoinilaResponse> listPoinilaResponse, Response response) { + postEvent(new CollectionsReceivedEvent(listPoinilaResponse.data, + listPoinilaResponse.bookmark, BaseEvent.ReceiverName.SearchFragment)); + } + + @Override + public Type getType() { + return new TypeToken>>() { + }.getType(); + } + }); + } + + public static void searchMembersWithQuery(List query, String bookmark) { + restServices.getMembersWithQuery(query, bookmark, new PoinilaCallback>>() { + @Override + public void poinilaSuccess(PoinilaResponse> listPoinilaResponse, Response response) { + postEvent(new MembersReceivedEvent(listPoinilaResponse.data, + listPoinilaResponse.bookmark)); + //, ReceiverName.SearchFragment)); + } + + @Override + public Type getType() { + return new TypeToken>>() { + }.getType(); + } + }); + } + + public static void getMemberProfile(String profileID) { + PoinilaCallback> cb = new PoinilaCallback>() { + @Override + public void poinilaSuccess(PoinilaResponse memberPoinilaResponse, Response response) { + postEvent(new MemberReceivedEvent(memberPoinilaResponse.data)); + } + + @Override + public Type getType() { + return new TypeToken>() { + }.getType(); + } + }; + if (StringUtils.isInteger(profileID)) + restServices.getProfileById(profileID, cb); + else + restServices.getProfileByUserName(profileID, cb); + } + + public static void favePost(final String postID) { + restServices.favePost(postID, + JsonRequestBodyMaker.emptyPacket().toRequestPacketJsonObject(POST), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + if (poinilaResponse.code != ConstantsUtils.CODE_SUCCESS) + postEvent(new UndoFavePostEvent()); + } + + @Override + public boolean poinilaError(PoinilaResponse error) { + postEvent(new UndoFavePostEvent()); + return true; + } + }); + } + + public static void unfavePost(final String postID) { + restServices.unfavePost(postID, + JsonRequestBodyMaker.emptyPacket().toRequestPacketJsonObject(DELETE), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + if (poinilaResponse.code != ConstantsUtils.CODE_SUCCESS) + postEvent(new UndoUnfavePostEvent()); + } + + @Override + public boolean poinilaError(PoinilaResponse error) { + postEvent(new UndoUnfavePostEvent()); + return true; + } + }); + } + + public static void getPostContent(final String contentUrl, final int postID) { + Request request = new Request.Builder().url(contentUrl) + .addHeader("Origin", ConstantsUtils.POINILA_ORIGIN_ADDRESS) + .build(); + okClient.newCall(request).enqueue(new com.squareup.okhttp.Callback() { + @Override + public void onFailure(Request request, IOException e) { + } + + @Override + public void onResponse(final com.squareup.okhttp.Response response) throws IOException { + String content = response.body().string(); + postEvent(new ContentReceivedEvent(content, postID)); + postEvent(new ModelCreatedEvent(new Content(contentUrl, content))); + } + }); + } + + public static void getMemberFriends(String memberID, String bookmark) { + restServices.getMemberFriends(memberID, bookmark, new PoinilaCallback>>() { + @Override + public void poinilaSuccess(PoinilaResponse> listPoinilaResponse, Response response) { + postEvent(new MembersReceivedEvent(listPoinilaResponse.data, + listPoinilaResponse.bookmark)); + //, ReceiverName.MemberListFragment)); + } + + @Override + public Type getType() { + return new TypeToken>>() { + }.getType(); + } + }); + } + + public static void getRelatedPosts(String postID, String bookmark, final int requestId) { + restServices.getRelatedPosts(postID, bookmark, new PoinilaCallback>>() { + @Override + public void poinilaSuccess(PoinilaResponse> listPoinilaResponse, Response response) { + postEvent(new PostsReceivedEvent( + listPoinilaResponse.data, listPoinilaResponse.bookmark, + BaseEvent.ReceiverName.PostRelatedPosts, requestId + )); + } + + @Override + public Type getType() { + return new TypeToken>>() { + }.getType(); + } + }); + } + + public static void getPostComments(String postID, String bookmark) { + restServices.getPostComments(postID, bookmark, new PoinilaCallback>>() { + @Override + public void poinilaSuccess(PoinilaResponse> listPoinilaResponse, Response response) { + postEvent(new CommentsReceivedEvent(listPoinilaResponse.data, listPoinilaResponse.bookmark)); + } + + @Override + public Type getType() { + return new TypeToken>>() { + }.getType(); + } + }); + } + + public static void getRepostCollections(String postID, String bookmark) { + restServices.getRepostCollections(postID, bookmark, new PoinilaCallback>>() { + @Override + public void poinilaSuccess(PoinilaResponse> listPoinilaResponse, Response response) { + postEvent(new CollectionsReceivedEvent(listPoinilaResponse.data, + listPoinilaResponse.bookmark, BaseEvent.ReceiverName.CollectionListFragment)); + } + + @Override + public Type getType() { + return new TypeToken>>() { + }.getType(); + } + }); + } + + public static void getPostLikers(String postID, String bookmark) { + restServices.getPostLikers(postID, bookmark, new PoinilaCallback>>() { + @Override + public void poinilaSuccess(PoinilaResponse> listPoinilaResponse, Response response) { + postEvent(new MembersReceivedEvent(listPoinilaResponse.data, + listPoinilaResponse.bookmark)); + //, ReceiverName.MemberListFragment)); + } + + @Override + public Type getType() { + return new TypeToken>>() { + }.getType(); + } + }); + } + + public static void getFavedPostByMember(String memberID, String bookmark) { + restServices.getMemberLikedPosts(memberID, bookmark, new PoinilaCallback>>() { + @Override + public void poinilaSuccess(PoinilaResponse> listPoinilaResponse, Response response) { + postEvent(new PostsReceivedEvent(listPoinilaResponse.data, + listPoinilaResponse.bookmark, BaseEvent.ReceiverName.PostListFragment)); + } + + + @Override + public Type getType() { + return new TypeToken>>() { + }.getType(); + } + }); + } + + public static void deleteComment(String commentID) { + restServices.deleteComment(commentID, + JsonRequestBodyMaker.emptyPacket().toRequestPacketJsonObject(DELETE), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + if (poinilaResponse.code != ConstantsUtils.CODE_SUCCESS) { + //TODO + } + } + }); + } + + /** + * {@code repostCaption} is the post {@code summary} + * + * @param collectionID + * @param postID + * @param repostCaption + * @param tags + */ + public static void repost(String collectionID, final int postID, String repostCaption, List tags) { + restServices.repost(collectionID, + JsonRequestBodyMaker.repost(postID, repostCaption, tags).toRequestPacketJsonObject(POST) + , new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + Logger.toast(R.string.successfully_created); + } + + @Override + public boolean poinilaError(PoinilaResponse poinilaResponse) { + + + Logger.toast(R.string.error_repost); + + BusProvider.getSyncUIBus().post(new UpdateUiRepostEvent(postID, false)); + + return super.poinilaError(poinilaResponse); + } + }); + } + + public static void commentOnPost(final String postID, String comment) { + restServices.commentOnPost(postID, JsonRequestBodyMaker.commentOnPost(comment).toRequestPacketJsonObject(POST) + , new PoinilaCallback>() { + @Override + public void poinilaSuccess(PoinilaResponse commentPoinilaResponse, Response response) { + postEvent(new CommentReceivedEvent(commentPoinilaResponse.data, postID)); + } + + @Override + public Type getType() { + return new TypeToken>() { + }.getType(); + } + }); + } + + public static void getMemberPosts(String memberID, String bookmark) { + + restServices.getMemberPosts(memberID, bookmark, new PoinilaCallback>>() { + @Override + public void poinilaSuccess(PoinilaResponse> listPoinilaResponse, Response response) { + postEvent(new PostsReceivedEvent(listPoinilaResponse.data, + listPoinilaResponse.bookmark, BaseEvent.ReceiverName.PostListFragment)); + } + + @Override + public Type getType() { + return new TypeToken>>() { + }.getType(); + } + + }); + + } + + public static void getMemberFollowers(String memberID, String bookmark) { + restServices.getMemberFollowers(memberID, bookmark, new PoinilaCallback>>() { + @Override + public void poinilaSuccess(PoinilaResponse> listPoinilaResponse, Response response) { + postEvent(new MembersReceivedEvent(listPoinilaResponse.data, + listPoinilaResponse.bookmark)); + //, ReceiverName.MemberListFragment)); + } + + + @Override + public Type getType() { + return new TypeToken>>() { + }.getType(); + } + }); + } + + + public static void getMemberCollections(String memberID, String bookmark) { + restServices.getMemberCollections(memberID, bookmark, new PoinilaCallback>>() { + @Override + public void poinilaSuccess(PoinilaResponse> listPoinilaResponse, Response response) { + postEvent(new CollectionsReceivedEvent(listPoinilaResponse.data, + listPoinilaResponse.bookmark, BaseEvent.ReceiverName.CollectionListFragment)); + } + + + @Override + public Type getType() { + return new TypeToken>>() { + }.getType(); + } + }); + } + + + /*public static void updateProfile(String memberId, String firstName, String lastName, + String email, String password, String oldPassword, boolean isActive) { + restServices.updateProfile(memberId, + JsonRequestBodyMaker.updateProfile(firstName, lastName, password, oldPassword, email, isActive),*/ + + public static void answerFriendRequest(int memberID, final FriendRequestAnswer answer, int circleID) { + restServices.answerFriendRequest(PoinilaPreferences.getMyId(), + JsonRequestBodyMaker.answerFriendRequest(answer, memberID, circleID).toRequestPacketJsonObject(PUT), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + postEvent(new AnswerFriendRequestResponse(true, answer)); + } + + /* @Override + public void poinilaError(PoinilaResponse error) { + //postEvent(new AnswerFriendRequestResponse()); + }*/ + }); + } + + public static void changeFriendCircle(final List circleIDs, final int friendId) { + restServices.updateFriendCircles(PoinilaPreferences.getMyId(), + JsonRequestBodyMaker.updateFriendCircles(circleIDs, friendId).toRequestPacketJsonObject(PUT), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + if (poinilaResponse.code != ConstantsUtils.CODE_SUCCESS) { + //postEvent(new FriendCircleNotChangedEvent(circleIDs, friendId)); + } + } + }); + } + + public static void friendRequest(String memberID, int publicCircleID) { + restServices.friendRequest(memberID, + JsonRequestBodyMaker.friendRequest( + Collections.singletonList(publicCircleID)).toRequestPacketJsonObject(POST), //new ArrayList<>(Arrays.asList(circleID)) + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + } + + @Override + public boolean poinilaError(PoinilaResponse poinilaResponse) { + return true; // just to avoid default toast error + } + }); + } + + public static void createCollection(String memberID, Collection collection, Bitmap bitmap) { //String imageAddress) { + //if (imageAddress == null){ + if (bitmap == null) { + restServices.createCollectionWithoutImage(memberID, + JsonRequestBodyMaker.createCollection(collection).toRequestPacketJsonObject(POST), + new PoinilaCallback>() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + Logger.toast(R.string.successfully_created); + poinilaResponse.data.jsonContent = getDataList().get(0).toString(); + postEvent(new ModelCreatedEvent(poinilaResponse.data)); + postEvent(new ProfileDirtyEvent()); + postEvent(new UpdateNewPostDialogEvent(poinilaResponse.data)); + } + + @Override + public boolean poinilaError(PoinilaResponse poinilaResponse) { + if (poinilaResponse.code == ConstantsUtils.CODE_DUPLICATE) { + Logger.toast(R.string.error_duplication_collection_name); + return true; + } + return false; + } + + @Override + public Type getType() { + return new TypeToken>() { + }.getType(); + } + + }); + } else { + restServices.createCollectionWithImage(memberID, POST, collection, + //new TypedFile(MULTIPART_FORMDATA, new File(imageAddress)), + new PoinilaTypedByteArray(MYME_TYPE_IMAGE, convertBitmapToByteArray( + ImageUtils.resizeBitmapForCollectionCover(bitmap))), + new PoinilaCallback>() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + Logger.toast(R.string.successfully_created); + poinilaResponse.data.jsonContent = getDataList().get(0).toString(); + postEvent(new ModelCreatedEvent(poinilaResponse.data)); + postEvent(new ProfileDirtyEvent()); + } + + @Override + public boolean poinilaError(PoinilaResponse poinilaResponse) { + if (poinilaResponse.code == ConstantsUtils.CODE_DUPLICATE) { + Logger.toast(R.string.error_duplication_collection_name); + return true; + } + return false; + } + + @Override + public Type getType() { + return new TypeToken>() { + }.getType(); + } + }); + } + } + + // TODO: separate updates having image with not having ones + public static void updateCollection(String collectionID, Collection collection, Bitmap bitmap) { + if (bitmap == null) { + restServices.updateCollectionWithoutCover(collectionID, + JsonRequestBodyMaker.createCollection(collection).toRequestPacketJsonObject(PUT), + new PoinilaCallback>() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + Logger.toast(R.string.successfully_updated); + poinilaResponse.data.jsonContent = getDataList().get(0).toString(); + postEvent(new ModelUpdatedEvent(poinilaResponse.data)); + postEvent(new CollectionUpdatedEvent(poinilaResponse.data)); + } + + @Override + public Type getType() { + return new TypeToken>() { + }.getType(); + } + }); + } else { + restServices.updateCollectionWithCover(collectionID, PUT, collection, + new PoinilaTypedByteArray(MYME_TYPE_IMAGE, convertBitmapToByteArray( + ImageUtils.resizeBitmapForCollectionCover(bitmap))), + new PoinilaCallback>() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + Logger.toast(R.string.successfully_updated); + poinilaResponse.data.jsonContent = getDataList().get(0).toString(); + postEvent(new ModelUpdatedEvent(poinilaResponse.data)); + postEvent(new CollectionReceivedEvent(poinilaResponse.data)); + } + + @Override + public Type getType() { + return new TypeToken>() { + }.getType(); + } + }); + } + } + + public static void removeFriend(String friendID) { + restServices.removeFriend(PoinilaPreferences.getMyId(), friendID, + JsonRequestBodyMaker.emptyPacket().toRequestPacketJsonObject(DELETE), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + } + + @Override + public boolean poinilaError(PoinilaResponse poinilaResponse) { + return true; // just to avoid default toast error + } + }); + } + + /** + * fetches collection post either by collection id or combination of collection name and user name + * + * @param collectionIdOrName if is collection id, user is useless + * @param userName if first parameter is collection name it's mandatory to pass a valid user name here + * @param bookmark + * @param receiverName + */ + public static void getCollectionPosts(String collectionIdOrName, @Nullable String userName, + String bookmark, final BaseEvent.ReceiverName receiverName, boolean justImages) { + PoinilaCallback>> cb = new PoinilaCallback>>() { + + @Override + public void poinilaSuccess(PoinilaResponse> listPoinilaResponse, Response response) { + postEvent(new PostsReceivedEvent(listPoinilaResponse.data, + listPoinilaResponse.bookmark, receiverName)); + } + + @Override + public Type getType() { + return new TypeToken>>() { + }.getType(); + } + + }; + if (userName != null) + restServices.getCollectionPostsByName(userName, collectionIdOrName, bookmark, cb); + else{ + if(justImages) + restServices.getCollectionPosts(collectionIdOrName, bookmark, "img", cb); + else + restServices.getCollectionPosts(collectionIdOrName, bookmark, cb); + } + } + + public static void createTextPost(String collectionID, Post post) { + restServices.uploadTextPost(collectionID, + JsonRequestBodyMaker.createTextPost(post).toRequestPacketJsonObject(POST), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + Logger.toast(R.string.successfully_created); + postEvent(new ProfileDirtyEvent()); + } + }); + } + + + public static void createImagePost(String collectionID, Bitmap image, Post newPost) { + imagedPost(collectionID, new PoinilaTypedByteArray(MYME_TYPE_IMAGE, + convertBitmapToByteArray(image)), newPost); + } + + /*public static void createImagePostFromFile(String collectionID, String imageAddress, Post post) { + imagedPost(collectionID, new PoinilaTypedByteArray(MYME_TYPE_IMAGE, + convertBitmapToByteArray(ImageUtils.loadBitmapScaledToUpload(imageAddress))), post); + } + + public static void createImagePostFromContentUri(String collectionID, Uri uri, Post post) { + imagedPost(collectionID, new PoinilaTypedByteArray(MYME_TYPE_IMAGE, + convertBitmapToByteArray(ImageUtils.loadBitmapScaledToUpload(uri))), post); + }*/ + + private static void imagedPost(String collectionID, TypedOutput image, Post post) { + restServices.uploadImagePost(collectionID, POST, post, image, + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + Logger.toast(R.string.successfully_created); + postEvent(new ProfileDirtyEvent()); + } + }); + } + + public static void deleteCollection(final Collection collection) { + restServices.deleteCollection(collection.getId(), + JsonRequestBodyMaker.emptyPacket().toRequestPacketJsonObject(DELETE), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + Logger.toast(R.string.successfully_deleted); + postEvent(new ModelDeletedEvent(collection)); + } + }); + } + + public static void followCollection(String collectionID) { + restServices.followCollection(collectionID, + JsonRequestBodyMaker.emptyPacket().toRequestPacketJsonObject(POST), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + } + + //TODO: kasif! tu search age beri collection follow koni bargardi update nemishe + // bad request mizane error default midim! + @Override + public boolean poinilaError(PoinilaResponse poinilaResponse) { + return true; + } + }); + } + + public static void unfollowCollection(String collectionID) { + restServices.unfollowCollection(collectionID, + JsonRequestBodyMaker.emptyPacket().toRequestPacketJsonObject(DELETE), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + } + + //TODO: kasif! tu search age beri collection follow koni bargardi update nemishe + // bad request mizane error default midim! + @Override + public boolean poinilaError(PoinilaResponse poinilaResponse) { + return true; + } + }); + } + + public static void updateCircle(final Circle circle) { + restServices.updateCircle(circle.getId(), + JsonRequestBodyMaker.createCircle(circle.name).toRequestPacketJsonObject(PUT) + , new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + postEvent(new ModelUpdatedEvent(circle)); + } + }); + } + + public static void deleteCircle(final Circle circle) { + restServices.deleteCircle(circle.getId(), + JsonRequestBodyMaker.emptyPacket().toRequestPacketJsonObject(DELETE), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + postEvent(new ModelDeletedEvent(circle)); + } + }); + } + + public static void createCircle(String circleName) { + restServices.createCircle(JsonRequestBodyMaker.createCircle(circleName).toRequestPacketJsonObject(POST), + new PoinilaCallback>() { + @Override + public void poinilaSuccess(PoinilaResponse circlePoinilaResponse, Response response) { + Circle newCircle = circlePoinilaResponse.data; + newCircle.defaultType = DefaultType.NOT_DEFAULT; + postEvent(new ModelCreatedEvent(newCircle)); + postEvent(new CircleReceivedEvent(circlePoinilaResponse.data)); + } + + @Override + public boolean poinilaError(PoinilaResponse error) { + postEvent(new FailEvent(RequestType.CREATE_CIRCLE)); + return true; + } + + @Override + public Type getType() { + return new TypeToken>() { + }.getType(); + } + }); + } + + public static void updateFrame(final Frame frame) { + restServices.updateFrame(frame.getId(), + JsonRequestBodyMaker.createCircle(frame.name).toRequestPacketJsonObject(PUT), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + postEvent(new ModelUpdatedEvent(frame)); + } + }); + } + + public static void deleteFrame(final Frame frame) { + restServices.deleteFrame(frame.getId(), + JsonRequestBodyMaker.emptyPacket().toRequestPacketJsonObject(DELETE), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + postEvent(new ModelDeletedEvent(frame)); + } + }); + } + + public static void createFrame(final String frameName) { + restServices.createFrame(JsonRequestBodyMaker.createCircle(frameName).toRequestPacketJsonObject(POST), + new PoinilaCallback>() { + @Override + public void poinilaSuccess(PoinilaResponse framePoinilaResponse, Response response) { + postEvent(new ModelCreatedEvent(framePoinilaResponse.data)); + postEvent(new FrameReceivedEvent(framePoinilaResponse.data)); + } + + @Override + public Type getType() { + return new TypeToken>() { + }.getType(); + } + }); + } + + public static void addCollectionToFrame(String frameID, String collectionID) { + restServices.addCollectionToFrame(frameID, collectionID, + JsonRequestBodyMaker.emptyPacket().toRequestPacketJsonObject(POST), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + + } + }); + } + + public static void removeCollectionFromFrame(String frameID, String collectionID) { + restServices.removeCollectionFromFrame(frameID, collectionID, + JsonRequestBodyMaker.emptyPacket().toRequestPacketJsonObject(DELETE), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + + } + }); + } + + public static void getProfileSettings() { + restServices.getProfileSettings(PoinilaPreferences.getMyId(), "people", + new PoinilaCallback>() { + @Override + public void poinilaSuccess(PoinilaResponse memberPoinilaResponse, Response response) { + postEvent(new ProfileSettingReceivedEvent(memberPoinilaResponse.data)); + } + + @Override + public Type getType() { + return new TypeToken>() { + }.getType(); + } + }); + } + + public static void updateProfileSetting(Member profile, final SettingActivity.SettingType settingType) { + restServices.updateProfile(PoinilaPreferences.getMyId(), + JsonRequestBodyMaker.updateProfile(profile).toRequestPacketJsonObject(PUT), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + postEvent(new UpdateProfileSettingResponse(true, settingType)); + } + + @Override + public boolean poinilaError(PoinilaResponse poinilaResponse) { + postEvent(new UpdateProfileSettingResponse(false, settingType)); + return false; + } + }); + } + + public static void changePassword(String newPassword, String oldPassword) { + restServices.changePassword(PoinilaPreferences.getMyId(), + JsonRequestBodyMaker.changePassword(newPassword, oldPassword).toRequestPacketJsonObject(PUT), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + Logger.toast(R.string.successfully_updated); + } + + @Override + public boolean poinilaError(PoinilaResponse poinilaResponse) { + if (poinilaResponse.code == 447) { // old password wrong + postEvent(new ServerResponseEvent(false, BaseEvent.ReceiverName.ChangePassword, poinilaResponse.code)); + return true; + } + return super.poinilaError(poinilaResponse); + } + }); + } + + public static void getMyInfo(final MyInfoReceivedEvent.MY_INFO_TYPE type) { + restServices.getMyInfo(new PoinilaCallback>() { + @Override + public void poinilaSuccess(PoinilaResponse ponilaResponse, Response response) { + // must be analyzed in detail. not sure it does't do any harm + String authToken = getJWTTokenFromHeaders(response); +// PoinilaPreferences.putAuthToken(authToken); + + PonilaAccountManager.getInstance().updatePonilaAccount(null, authToken); + + Member member = ponilaResponse.data; + member.jsonContent = getDataList().get(0).toString(); + + JsonElement collectionsJsonElement = getDataList().get(0).getAsJsonObject().get(KEY_JSON_OWNED_COLLECTIONS); + if (collectionsJsonElement != null && !member.isAnonymous) { // in anonymous login + JsonArray collectionsElements = collectionsJsonElement.getAsJsonArray(); + for (int i = 0; i < collectionsElements.size(); i++) { + member.owningCollections.get(i).jsonContent = collectionsElements.get(i).toString(); + } + } + + postEvent(new MyInfoReceivedEvent(ponilaResponse.data, false, type)); + + } + + @Override + public boolean poinilaError(PoinilaResponse poinilaResponse) { + if (poinilaResponse.code == 401) { + postEvent(new MyInfoReceivedEvent(null, false, type)); + return true; + } + return super.poinilaError(poinilaResponse); + } + + @Override + public Type getType() { + return new TypeToken>() { + }.getType(); + } + }); + } + + + public static void getMyFriendshipRequests(String bookmark) { + restServices.getMyFriendshipRequests(bookmark, new PoinilaCallback>>() { + + @Override + public void poinilaSuccess(PoinilaResponse> listPoinilaResponse, Response response) { + postEvent(new MyFriendshipRequestsEvent(listPoinilaResponse.data, listPoinilaResponse.bookmark)); + } + + @Override + public Type getType() { + return new TypeToken>>() { + }.getType(); + } + }); + } + + /* public static void getMyAcceptedFriendships(String bookmark){ + restServices.getMyAcceptedFriendships(bookmark, new PoinilaCallback>>() { + + @Override + public void poinilaSuccess(PoinilaResponse> listPoinilaResponse, Response response) { + postEvent(new AcceptedInvitationsEvent(listPoinilaResponse.data, listPoinilaResponse.bookmark)); + } + + @Override + public void poinilaError(PoinilaResponse error) { + Logger.toast("get accept error: " + error.getMessage()); + } + }); + }*/ + + public static void getMyNotifications(String bookmark) { + restServices.getMyNotifications(bookmark, new PoinilaCallback>>() { + @Override + public void poinilaSuccess(PoinilaResponse> listPoinilaResponse, Response response) { + //removing notifications related to sharing a collection between peoples + for (int i = listPoinilaResponse.data.size() - 1; i >= 0; i--) { + if (listPoinilaResponse.data.get(i).type == null) + listPoinilaResponse.data.remove(i); + } + postEvent(new AbstractNotificationsReceivedEvent.MyNotificationsReceivedEvent(listPoinilaResponse.data, listPoinilaResponse.bookmark)); + } + + @Override + public Type getType() { + return new TypeToken>>() { + }.getType(); + } + }); + } + + public static void getOthersNotification(String bookmark) { + restServices.getOthersNotification(bookmark, new PoinilaCallback>>() { + @Override + public void poinilaSuccess(PoinilaResponse> listPoinilaResponse, Response response) { + postEvent(new AbstractNotificationsReceivedEvent.OthersNotificationsReceivedEvent(listPoinilaResponse.data, listPoinilaResponse.bookmark)); + } + + @Override + public Type getType() { + return new TypeToken>>() { + }.getType(); + } + }); + } + + + public static void deletePost(String id) { + restServices.deletePost(id, + JsonRequestBodyMaker.emptyPacket().toRequestPacketJsonObject(DELETE), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + + } + }); + } + + public static void getServerTime() { + final long start = System.currentTimeMillis(); + restServices.getServerTime(new PoinilaCallback>() { + @Override + public void poinilaSuccess(PoinilaResponse datePoinilaResponse, Response response) { + //postEvent(new TimeReceivedEvent(datePoinilaResponse.data)); + Intent intent = (new Intent(ConstantsUtils.INTENT_FILTER_SERVER_TIME)); + intent.putExtra(ConstantsUtils.KEY_TIME_DIFFERENCE, datePoinilaResponse.data.getTime() - start); + LocalBroadcastManager.getInstance(ContextHolder.getContext()).sendBroadcast(intent); + } + + @Override + public Type getType() { + return new TypeToken>() { + }.getType(); + } + }); + } + + public static void getTopics() { + restServices.getTopics(new PoinilaCallback>>() { + @Override + public void poinilaSuccess(PoinilaResponse> listPoinilaResponse, Response response) { + postEvent(new TopicsReceivedEvent(listPoinilaResponse.data)); + } + + @Override + public Type getType() { + return new TypeToken>>() { + }.getType(); + } + }); + } + + public static void getApplicationNotification() { + restServices.getApplicationNotification(new PoinilaCallback>>() { + @Override + public void poinilaSuccess(PoinilaResponse> listPoinilaResponse, Response response) { + postEvent(new NotificationSettingsReceivedEvent(listPoinilaResponse.data)); + } + + @Override + public Type getType() { + return new TypeToken>>() { + }.getType(); + } + }); + } + + public static void getEmailNotification() { + restServices.getEmailNotification(new PoinilaCallback>>() { + @Override + public void poinilaSuccess(PoinilaResponse> listPoinilaResponse, Response response) { + postEvent(new NotificationSettingsReceivedEvent(listPoinilaResponse.data)); + } + + @Override + public Type getType() { + return new TypeToken>>() { + }.getType(); + } + }); + } + + + public static void addFriendToCircle(String circleID, String friendID) { + restServices.addFriendToCircle( + PoinilaPreferences.getMyId(), + friendID, circleID, JsonRequestBodyMaker.emptyPacket().toRequestPacketJsonObject(POST), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + + } + }); + } + + public static void removeFriendFromCircle(String circleID, String friendID) { + restServices.removeFriendFromCircle( + PoinilaPreferences.getMyId(), + friendID, circleID, JsonRequestBodyMaker.emptyPacket().toRequestPacketJsonObject(DELETE), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + } + }); + } + + + public static void getPost(String postID, final int requestId) { + restServices.getPost(postID, new PoinilaCallback>() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + postEvent(new PostReceivedEvent(poinilaResponse.data, requestId)); + } + + @Override + public Type getType() { + return new TypeToken>() { + }.getType(); + } + }); + } + + public static void getCollection(@NotNull String collectionIdOrName, @Nullable String userName) { + PoinilaCallback> cb = new PoinilaCallback>() { + @Override + public void poinilaSuccess(PoinilaResponse poinilaResponse, Response response) { + postEvent(new CollectionReceivedEvent(poinilaResponse.data)); + } + + @Override + public Type getType() { + return new TypeToken>() { + }.getType(); + } + }; + if (userName != null) + restServices.getCollectionByName(collectionIdOrName, userName, cb); + else + restServices.getCollection(collectionIdOrName, cb); + } + + /** + * @param type either bug or proposal + */ + public static void sendReport(String type, String title, String content) { + restServices.report(JsonRequestBodyMaker.contactUs(type, title, content).toRequestPacketJsonObject(POST), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse painingResponse, Response response) { + Logger.toast(R.string.successfully_submitted_report); + } + }); + } + + public static void informServerOfInlineBrowsing(String postId) { + restServices.informServerOfPostInlineBrowsing(postId, new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse ponilaResponse, Response response) { + } + }); + } + + public static void informServerOfExternalBrowsing(String postId) { + restServices.informServerOfPostExternalBrowsing(postId, new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse ponilaResponse, Response response) { + } + }); + } + + public static void explore(String mainEntityId, String bookmark) { + restServices.explore(mainEntityId, bookmark, new PoinilaCallback>>() { + @Override + public void poinilaSuccess(PoinilaResponse> ponilaResponse, Response response) { + postEvent(new PostsReceivedEvent(ponilaResponse.data, ponilaResponse.bookmark, BaseEvent.ReceiverName.ExploredTagPosts)); + } + + @Override + public Type getType() { + return new TypeToken>>() { + }.getType(); + } + + @Override + public boolean poinilaError(PoinilaResponse poinilaResponse) { + return super.poinilaError(poinilaResponse); + } + }); + } + + public static void verifyPhoneOrMobile(String verificationCode, int memberId, String mobileOrPhone, boolean byEmail){ + restServices.verifyPhoneOrEmail( + JsonRequestBodyMaker.verifyPhoneOrMobile(verificationCode, memberId, mobileOrPhone, byEmail).toRequestPacketJsonObject(POST) + , new PoinilaCallback() { + + @Override + public void poinilaSuccess(PoinilaResponse ponilaResponse, Response response) { + postEvent(new VerificationRequestResponse(true, ponilaResponse.code)); + } + + @Override + public boolean poinilaError(PoinilaResponse poinilaResponse) { + postEvent(new VerificationRequestResponse(false, poinilaResponse.code)); + return true; + } + }); + } + + public static void getSystemPreferences() { + + restServices.getSystemPreferences(new PoinilaCallback>() { + @Override + public void poinilaSuccess(PoinilaResponse ponilaResponse, Response response) { + postEvent(new SystemPreferencesReceivedEvent(ponilaResponse.data)); + } + + @Override + public Type getType() { + return new TypeToken>() { + }.getType(); + } + }); + + } + + public static void reportMemberOrPost(int memberIdOrPostId){ + restServices.reportMemberOrPost(JsonRequestBodyMaker.reportMemberOrPost( + memberIdOrPostId). + toRequestPacketJsonObject(POST), new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse ponilaResponse, Response response) { + + } + }); + } + + public static void getSuggestedPosts(String ids){ + try { + JSONArray postIds = new JSONArray(ids); + restServices.getSuggestedPosts(JsonRequestBodyMaker.postIdList(postIds).toRequestPacketJsonObject(POST), new PoinilaCallback>>() { + @Override + public void poinilaSuccess(PoinilaResponse> ponilaResponse, Response response) { + postEvent(new SuggestionPosts(ponilaResponse.data)); + } + + @Override + public boolean poinilaError(PoinilaResponse poinilaResponse) { + return super.poinilaError(poinilaResponse); + } + + @Override + public Type getType() { + return new TypeToken>>() { + }.getType(); + } + }); + } catch (JSONException e) { + e.printStackTrace(); + } + + } + + public static void setUsernamePassword(final String uniqueName, String password){ + restServices.setUserNamePassword(DBFacade.getCachedMyInfo().getId(), + JsonRequestBodyMaker.setUsernamePassword(uniqueName, password, PoinilaPreferences.getGoogleToken()).toRequestPacketJsonObject(PUT), + new PoinilaCallback() { + @Override + public void poinilaSuccess(PoinilaResponse ponilaResponse, Response response) { + Logger.toast(R.string.successfully_updated); + } + + @Override + public boolean poinilaError(PoinilaResponse poinilaResponse) { + + switch (poinilaResponse.code){ + case 425: + Logger.toastError(uniqueName + " " + getString(R.string.error_already_taken_username)); + break; + default: + Logger.toastError(getString(R.string.change_user_pass_fail)); + } + + + return super.poinilaError(poinilaResponse); + + + } + }); + } + + public static class AddCookiesInterceptor implements Interceptor { + + @Override + public com.squareup.okhttp.Response intercept(Chain chain) throws IOException { + Request.Builder builder = chain.request().newBuilder(); + //Set cookies = PoinilaPreferences.getCookies(); + for (String cookie : PoinilaNetService.cookies) { //cookies) { + builder.addHeader("Cookie", cookie); + } + + return chain.proceed(builder.build()); + } + } + + public static class ReceivedCookiesInterceptor implements Interceptor { + @Override + public com.squareup.okhttp.Response intercept(Chain chain) throws IOException { + com.squareup.okhttp.Response originalResponse = chain.proceed(chain.request()); + + if (!originalResponse.headers("Set-Cookie").isEmpty()) { + HashSet cookies = new HashSet<>(); + for (String header : originalResponse.headers("Set-Cookie")) { + if (header.startsWith("session")) { + header = header.split(" ")[0].replace(";", ""); + } + cookies.add(header); + } + //PoinilaPreferences.putCookies(cookies); + PoinilaNetService.cookies = cookies; + } + return originalResponse; + } + } + + + + private static class AgentAndVersionInterceptor implements Interceptor { + @Override + public com.squareup.okhttp.Response intercept(Chain chain) throws IOException { + Request.Builder builder = chain.request().newBuilder(); + builder.addHeader("ponila-version", String.valueOf(DeviceInfoUtils.CLIENT_VERSION_CODE)); + // TODO: add user-agent header. + return chain.proceed(builder.build()); + } + } + + + + /* private static class RetrofitErrorHandler implements ErrorHandler { + @Override + public Throwable handleError(RetrofitError cause) { + Exception poinilaError; + //Response r = cause.getResponse(); + + if (cause.getCause() instanceof SocketTimeoutException) { + return new Exception(ResourceUtils.getString(R.string.error_socket_timeout)); + } + return cause; + } + }*/ + + public static String getStringFromResponse(Response response) { + return new String(((TypedByteArray) response.getBody()).getBytes()); + } + + public static JsonElement getDataOfResponse(Response response) { + JsonElement jsonElement = new JsonParser().parse(getStringFromResponse(response)); + return jsonElement.getAsJsonObject().get(KEY_JSON_DATA_ROOT); + } + + private static void postEvent(T event) { + BusProvider.getBus().post(event); + } + + +} diff --git a/src/main/java/data/PoinilaTypedByteArray.java b/src/main/java/data/PoinilaTypedByteArray.java new file mode 100755 index 0000000..7257174 --- /dev/null +++ b/src/main/java/data/PoinilaTypedByteArray.java @@ -0,0 +1,24 @@ +package data; + +import retrofit.mime.TypedByteArray; + +/** + * Created by iran on 2015-09-20. + */ +public class PoinilaTypedByteArray extends TypedByteArray{ + /** + * Constructs a new typed byte array. Sets mimeType to {@code application/unknown} if absent. + * + * @param mimeType + * @param bytes + * @throws NullPointerException if bytes are null + */ + public PoinilaTypedByteArray(String mimeType, byte[] bytes) { + super(mimeType, bytes); + } + + @Override + public String fileName() { + return "temp"; + } +} diff --git a/src/main/java/data/RequestKind.java b/src/main/java/data/RequestKind.java new file mode 100755 index 0000000..cf57076 --- /dev/null +++ b/src/main/java/data/RequestKind.java @@ -0,0 +1,10 @@ +package data; + +/** + * Created by AlirezaF on 11/16/2015. + */ +public enum RequestKind { + OneTimeRequest, + ListDataRequest, + RepetitiveRequest, // info, currentTime. No use yet :) +} diff --git a/src/main/java/data/RequestTracker.java b/src/main/java/data/RequestTracker.java new file mode 100755 index 0000000..77d71bd --- /dev/null +++ b/src/main/java/data/RequestTracker.java @@ -0,0 +1,33 @@ +package data; + +import java.util.HashSet; +import java.util.Set; + +/** + * Created by iran on 2015-11-15. + */ +public class RequestTracker { + public Set initRequestsIDs; + public Set loadMoreRequestIDs; + + public RequestTracker() { + initRequestsIDs = new HashSet<>(); + loadMoreRequestIDs = new HashSet<>(); + } + + public void addInitRequestID(int requestID){ + initRequestsIDs.add(requestID); + } + + public boolean hasInitResponseValidID(int requestID){ + return initRequestsIDs.remove(requestID); + } + + public void addLoadMoreRequestID(int requestID){ + loadMoreRequestIDs.add(requestID); + } + + public boolean hasLoadMoreResponseValidID(int requestID){ + return loadMoreRequestIDs.remove(requestID); + } +} diff --git a/src/main/java/data/RequestType.java b/src/main/java/data/RequestType.java new file mode 100755 index 0000000..ba733ba --- /dev/null +++ b/src/main/java/data/RequestType.java @@ -0,0 +1,8 @@ +package data; + +/** + * Created by iran on 2015-09-05. + */ +public enum RequestType { + CREATE_FRAME, CREATE_CIRCLE +} diff --git a/src/main/java/data/RestServices.java b/src/main/java/data/RestServices.java new file mode 100755 index 0000000..5b75e79 --- /dev/null +++ b/src/main/java/data/RestServices.java @@ -0,0 +1,442 @@ +package data; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; + +import data.model.SuggestedWebPagePost; +import retrofit.Callback; +import retrofit.client.Response; +import retrofit.http.Body; +import retrofit.http.GET; +import retrofit.http.Multipart; +import retrofit.http.POST; +import retrofit.http.Part; +import retrofit.http.Path; +import retrofit.http.Query; +import retrofit.mime.TypedOutput; + +/** + * Created by iran on 2015-06-15. + * @author Alireza Farahani + */ +public interface RestServices { + String BOOKMARK = "bookmark"; + String ACTION = "action"; + String DATA = "data"; + String IMAGE = "image"; + + /*@GET("/users/{user}/repos") + List testJson(@Path("user") String user);*/ + + + /*--------Suggestion---------*/ + @GET("/suggestion/") + void getSuggestions(@Query(value = BOOKMARK) String bookmark, Callback cb);//PoinilaCallback cb); + //void getSuggestions(@Query(value = "bookmark") String bookmark, PoinilaCallback>> cb); + + /*----------SEARCH-----------*/ + @GET("/post/search/") + void getPostsWithQuery(@Query(value = "q") List query, @Query(value = BOOKMARK) String bookmark, + data.PoinilaCallback>> cb); + + @GET("/collection/search/") + void getCollectionsWithQuery(@Query(value = "q") List query, @Query(value = BOOKMARK) String bookmark, + data.PoinilaCallback>> cb); + + @GET("/member/search/") + void getMembersWithQuery(@Query(value = "q") List query, @Query(value = BOOKMARK) String bookmark, + data.PoinilaCallback>> cb); + + /*----------POST-----------*/ + /*------GET-------*/ + @GET("/post/{post_id}/") + void getPost(@Path("post_id") String postID, data.PoinilaCallback> cb); + + @GET("/post/{id}/relatedpost/") + void getRelatedPosts(@Path("id") String postID, @Query(value = BOOKMARK) String bookmark, data.PoinilaCallback>> cb); + + //TODO: double check with server format + @GET("/post/{id}/comment/") + void getPostComments(@Path("id") String postID, @Query(value = BOOKMARK) String bookmark, data.PoinilaCallback>> cb); + + /** + * retrieve the collections consisting a specific post. + * @param postID + * @param cb + */ + @GET("/post/{id}/repostcollection/") + void getRepostCollections(@Path("id") String postID, @Query(value = BOOKMARK) String bookmark, data.PoinilaCallback>> cb); + + @GET("/post/{id}/like/") + void getPostLikers(@Path("id") String postID, @Query(value = BOOKMARK) String bookmark, data.PoinilaCallback>> cb); + + @GET("/post/{id}/open/") + void informServerOfPostInlineBrowsing(@Path("id") String postId, data.PoinilaCallback cb); + + @GET("/post/{id}/browse/") + void informServerOfPostExternalBrowsing(@Path("id") String postId, data.PoinilaCallback cb); + + /*@GET("/posts/{id}/") + void getPost(@Path("id") String id, PoinilaCallback cb);*/ + + /*----------POST-----------*/ + /*------PUT-------*/ + @POST("/post/{id}/like/") + void favePost(@Path("id") String PostID, @Body HashMap emptyBody, data.PoinilaCallback cb); + + /*@POST("/post/{id}/") + void updatePost(@Path("id") String postID, @Body Post post, PoinilaCallback cb);*/ + + /*-----DELETE-----*/ + @POST("/post/{id}/like/") + void unfavePost(@Path("id") String PostID, @Body HashMap hashMap, data.PoinilaCallback cb); + + @POST("/post/{id}/") + void deletePost(@Path("id") String postID, @Body HashMap hashMap, data.PoinilaCallback cb); + + + /*-----POST------*/ + @POST("/collection/{id}/repost/") + void repost(@Path("id") String collectionID, @Body HashMap post, data.PoinilaCallback cb); + + @POST("/post/{id}/comment/") + void commentOnPost(@Path("id") String postID, @Body HashMap comment, data.PoinilaCallback> cb); + + @POST("/comment/{id}/") + void deleteComment(@Path("id") String commentID, @Body HashMap hashMap, data.PoinilaCallback cb); + + /*----------MEMBER-----------*/ + /*------GET-------*/ + @GET("/member/{id}/profile/") + void getProfileById(@Path("id") String memberID, data.PoinilaCallback> cb); + + @GET("/unique_name/{unique_name}/profile/") + void getProfileByUserName(@Path("unique_name") String userName, data.PoinilaCallback> cb); + + + @GET("/member/{id}/like/") + void getMemberLikedPosts(@Path("id") String memberID, @Query(value = BOOKMARK) String bookmark, + data.PoinilaCallback>> cb); + + @GET("/member/{id}/friend/") + void getMemberFriends(@Path("id") String memberID, @Query(value = BOOKMARK) String bookmark, + data.PoinilaCallback>> cb); + + @GET("/member/{id}/post/") + void getMemberPosts(@Path("id") String memberID, @Query(value = BOOKMARK) String bookmark, + data.PoinilaCallback>> cb); + + @GET("/member/{id}/follower/") + void getMemberFollowers(@Path("id") String memberID, @Query(value = BOOKMARK) String bookmark, + data.PoinilaCallback>> cb); + + @GET("/member/{id}/collection/") + void getMemberCollections(@Path("id") String memberID, @Query(value = BOOKMARK) String bookmark, + data.PoinilaCallback>> cb); + + @GET("/member/{id}/followcollection/") + void getMemberFollowingCollections(@Path("id") String memberID, + @Query(value = BOOKMARK) String bookmark, + @Query(value = "frame_id") String frameID, + data.PoinilaCallback>> cb); + + @GET("/member/{id}/generalconfiguration/") + void getProfileSettings(@Path("id") String memberID, @Query("type") String type, data.PoinilaCallback> cb); + + @GET("/member/{id}/interest/") + void getMemberInterests(@Path("id") String memberID, data.PoinilaCallback>> cb); + + /*----------MEMBER-----------*/ + /*------PUT-------*/ + @POST("/member/{id}/generalconfiguration/") + void updateProfile(@Path("id") String memberID, @Body HashMap user, data.PoinilaCallback cb); + + + @POST("/member/{user_id}/friendshipinvitation/") + void answerFriendRequest(@Path("user_id") String userID, @Body HashMap circleRequester, + data.PoinilaCallback cb); + + @POST("/member/{id}/password/") + void changePassword(@Path("id") String memberID, @Body HashMap password, data.PoinilaCallback cb); + + @POST("/member/{id}/friendcircle/") + void updateFriendCircles(@Path("id") String memberID, @Body HashMap circles, data.PoinilaCallback cb); + + /*-----POST------*/ + + @POST("/member/{user_id}/friend/{friend_id}/circle/{circle_id}/") + void addFriendToCircle( + @Path("user_id") String userID, + @Path("friend_id") String friendID, + @Path("circle_id") String circleID, + @Body HashMap emptyPacket, + data.PoinilaCallback cb); + + // TODO: + @Multipart + @POST("/member/{id}/profile/photo/") + void uploadProfilePic(@Path("id") String memberID, + @Part(ACTION) String action, + @Part(IMAGE) TypedOutput image, + data.PoinilaCallback cb); + + @POST("/member/{member_id}/friendshipinvitation/") + void friendRequest(@Path("member_id") String memberID, @Body HashMap circle,//JsonObject circle, //JSONObject circle, + data.PoinilaCallback cb); + + + @Multipart + @POST("/member/{id}/collection/") + void createCollectionWithImage(@Path("id") String memberID, + @Part(ACTION) String action, + @Part(DATA) data.model.Collection collection, + @Part(IMAGE) TypedOutput image, + data.PoinilaCallback> cb); + /* @Part(value = "name") String name, + @Part(value = "description") String description, + @Part(value = "topic_id") int topicID, + @Part(value = "circle_ids") String circle_IDs,*/ + + @POST("/member/{id}/collection/") + void createCollectionWithoutImage(@Path("id") String memberID, + @Body HashMap CollectionPacket, + data.PoinilaCallback> cb); + + /*----DELETE---*/ + @POST("/member/{user_id}/friend/{friend_id}/circle/{circle_id}/") + void removeFriendFromCircle( + @Path("user_id") String userID, + @Path("friend_id") String friendID, + @Path("circle_id") String circleID, + @Body HashMap emptyPacket, + data.PoinilaCallback cb); + + /** + * + * @param userID, ID of the user not his friend! only for consistency in url formats + * @param cb + */ + @POST("/member/{id}/friend/{friend_id}/") + void removeFriend(@Path("id") String userID,// JSONObject friendID, + @Path("friend_id") String friendID, @Body HashMap method, + data.PoinilaCallback cb); + + + /*--------COLLECTION----------*/ + /*--GET--*/ + @GET("/collection/{collection_id}/") + void getCollection(@Path("collection_id") String collectionID, data.PoinilaCallback> cb); + + @GET("/unique_name/{unique_name}/collection_name/{collection_name}/") + void getCollectionByName(@Path(value = "collection_name", encode = false) String collectionName, @Path(value = "unique_name", encode = false) String uniqueName, + data.PoinilaCallback> cb); + + // TODO: man bayad vorudi bedam az che zamanio mikham?? + @GET("/collection/{id}/post/") + void getCollectionPosts(@Path("id") String collectionID, @Query(value = BOOKMARK) String bookmark, + @Query(value = "post_type") String post_type, + data.PoinilaCallback>> cb); + + @GET("/collection/{id}/post/") + void getCollectionPosts(@Path("id") String collectionID, @Query(value = BOOKMARK) String bookmark, + data.PoinilaCallback>> cb); + + @GET("/unique_name/{unique_name}/collection_name/{collection_name}/post/") + void getCollectionPostsByName(@Path("unique_name") String userName, @Path("collection_name") String collectionName, + @Query(value = BOOKMARK) String bookmark, + data.PoinilaCallback>> cb); + + /*--POST--*/ + @POST("/collection/{id}/post/") + void uploadTextPost(@Path("id") String CollectionID, @Body HashMap body, data.PoinilaCallback cb); + + /*@POST("/collection/{id}/post/") + void uploadImagePost(@Path("id") String CollectionID, @Body Post post, + @Body TypedFile image, PoinilaCallback cb);*/ + @Multipart + @POST("/collection/{id}/post/") + void uploadImagePost(@Path("id") String CollectionID, + /*@Part("title") String title, + @Part("caption") String caption, + @Part("tags") String tags,*/ + @Part(ACTION) String action, + @Part(DATA) data.model.Post post, + @Part(IMAGE) TypedOutput image, data.PoinilaCallback cb); + + @POST("/collection/{id}/post/") + void createWebsitePost(@Path("id") String collectionID, @Body HashMap post, data.PoinilaCallback cb); + + @POST("/collection/{id}/follow/") + void followCollection(@Path("id") String CollectionID, @Body HashMap emptyBody, data.PoinilaCallback cb); + + /*--PUT--*/ + @POST("/collection/{id}/") + void updateCollectionWithoutCover(@Path("id") String collectionID, + @Body HashMap collectionPacket, + data.PoinilaCallback> cb); + + @POST("/collection/{id}/coverphoto/") + void uploadCollectionCoverImage(@Path("id") String collectionID, @Body HashMap image, + data.PoinilaCallback cb); + @Multipart + @POST("/collection/{id}/") + void updateCollectionWithCover(@Path("id") String collectionID, + @Part(ACTION) String action, + @Part(DATA) data.model.Collection collection, + @Part(IMAGE) TypedOutput image, + data.PoinilaCallback> cb); + + /*--DELETE--*/ + @POST("/collection/{id}/") + void deleteCollection(@Path("id") String collectionID, + @Body HashMap actionPacket, + data.PoinilaCallback cb); + + @POST("/collection/{id}/follow/") + void unfollowCollection(@Path("id") String collectionID, @Body HashMap emptyBody, + data.PoinilaCallback cb); + + + /*----------CIRCLE-----------*/ + /*DELETE*/ + @POST("/circle/{id}/") + void deleteCircle(@Path("id") String circleID, @Body HashMap emptyBody, data.PoinilaCallback cb); + + @POST("/frame/{id}/") + void deleteFrame(@Path("id") String frameID, @Body HashMap emptyBody, data.PoinilaCallback cb); + + @POST("/frame/{id}/collection/{collection_id}/") + void removeCollectionFromFrame(@Path("id") String frameID, + @Path("collection_id") String collectionID, + @Body HashMap emptyPacket, + data.PoinilaCallback cb); + /*PUT*/ + @POST("/circle/{id}/") + void updateCircle(@Path("id") String circleID, @Body HashMap circleName, data.PoinilaCallback cb); + + @POST("/frame/{id}/") + void updateFrame(@Path("id") String frameID, @Body HashMap frameName, data.PoinilaCallback cb); + + /*POST*/ + @POST("/circle/") + void createCircle(@Body HashMap circleName, data.PoinilaCallback> cb); + + @POST("/frame/{id}/collection/{collection_id}/") + void addCollectionToFrame(@Path("id") String frameID, + @Path("collection_id") String collectionID, + @Body HashMap emptyPacket, + data.PoinilaCallback cb); + + @POST("/frame/") + void createFrame(@Body HashMap frameName, data.PoinilaCallback> cb); + + /*--------Notifications-------*/ + @GET("/notification/ownfriendshipinvitations/") + void getMyFriendshipRequests(@Query(value = BOOKMARK) String bookmark, + data.PoinilaCallback>> cb); + + @GET("/notification/ownnotifications/") + void getMyNotifications(@Query(value = BOOKMARK) String bookmark, + data.PoinilaCallback>> cb); + + @GET("/notification/othernotifications/") + void getOthersNotification(@Query(value = BOOKMARK) String bookmark, + data.PoinilaCallback>> cb); + + /*----------OTHERS-----------*/ + // first time after loginParams and anytime user wants to set interests; + + @GET("/topic/") + void getTopics(data.PoinilaCallback>> cb); + + @GET("/currentservertime/") + void getServerTime(data.PoinilaCallback> cb); + + //TODO: methoda doroste? vorudi nemikhan ina? + @GET("/logout/") + void logout(data.PoinilaCallback cb); + + @GET("/applicationnotificationsettings/") + void getApplicationNotification(data.PoinilaCallback>> cb); + + @GET("/emailnotificationsettings/") + void getEmailNotification(data.PoinilaCallback>> cb); + + @GET("/interest/") + void getInterests(data.PoinilaCallback>> cb); + + @GET("/interest/{id}/") + void getSubInterest(@Path("id") String subInterests, data.PoinilaCallback>> cb); + + @GET("/invitetopoinila/") + void getRemainedInvites(data.PoinilaCallback> cb); + + @GET("/websiteinfo/{type}/") + void getWebsiteInfo(@Path("type") String postType, @Query(value = "url") String address, + data.PoinilaCallback> cb); + + @GET("/info2/") + void getMyInfo(data.PoinilaCallback> cb); + + /*--POST---*/ + @POST("/login/") + void login(@Body HashMap loginParas, Callback cb); + + @POST("/loginbygoogle/") + void loginByGoogle(@Body HashMap loginParas, Callback cb); + + @POST("/applicationnotificationsettings/") + void setApplicationNotification(@Body HashMap settingParams, data.PoinilaCallback cb); + + @POST("/emailnotificationsettings/") + void setEmailNotificationSetting(@Body HashMap settingParams, data.PoinilaCallback cb); + + @POST("/member/{user_id}/interest/{tag_id}/") + void removeInterest(@Path("user_id") String userID, @Path("tag_id") String tagID, + @Body HashMap emptyBody, data.PoinilaCallback cb); + + @POST("/forgetpassword/") + void recoverPassword(@Body HashMap recoveryDest, data.PoinilaCallback cb); + + @POST("/resetpassword/") + void resetPassword(@Body HashMap newPassword, data.PoinilaCallback cb); + + @POST("/verificationcode/") + void requestVerificationCode(@Body HashMap hashMap, data.PoinilaCallback cb); + + /*---put----*/ + @POST("/member/{id}/interest/") + void updateUserInterests(@Path("id") String memberID, @Body HashMap hashMap, data.PoinilaCallback cb); + + @POST("/invitetopoinila/") + void inviteToPoinila(@Body HashMap invitation, data.PoinilaCallback cb); + + @POST("/report/") + void report(@Body HashMap body, data.PoinilaCallback cb); + + @POST("/register/") + void register(@Body HashMap registerBody, data.PoinilaCallback cb); + + @GET("/unique_name/validate/") + void checkUserNameValidity(@Query("unique_name") String tempUserName, data.PoinilaCallback cb); + + @GET("/explore/{tag_name}/") + void explore(@Path("tag_name") String tagName, @Query(BOOKMARK) String bookmark, data.PoinilaCallback>> cb); + + @GET("/ponilasystempreferences/") + void getSystemPreferences(data.PoinilaCallback> cb); + + @POST("/post/member_id/report") + void reportMemberOrPost(@Body HashMap memberIdOrPostId,data.PoinilaCallback cb); + + @POST("/verifyuser/") + void verifyPhoneOrEmail(@Body HashMap verificationCode, data.PoinilaCallback cb); + + @POST("/suggestedposts/") + void getSuggestedPosts(@Body HashMap postIds, data.PoinilaCallback>> cb); + + @POST("/member/{member_id}/setusernamepassword/") + void setUserNamePassword(@Path("member_id") String userId, @Body HashMap data, data.PoinilaCallback cb); +} + diff --git a/src/main/java/data/database/PoinilaDataBase.java b/src/main/java/data/database/PoinilaDataBase.java new file mode 100755 index 0000000..0dab81a --- /dev/null +++ b/src/main/java/data/database/PoinilaDataBase.java @@ -0,0 +1,117 @@ +package data.database; + +import android.os.Parcelable; +import android.text.Html; +import android.text.Spanned; + +import com.google.gson.Gson; +import com.raizlabs.android.dbflow.annotation.Column; +import com.raizlabs.android.dbflow.annotation.Database; +import com.raizlabs.android.dbflow.converter.TypeConverter; +import com.raizlabs.android.dbflow.structure.BaseModel; + +import org.parceler.Parcel; +import org.parceler.ParcelClass; +import org.parceler.ParcelConverter; + +import java.io.Serializable; + +import data.PoinilaNetService; +import data.model.Gender; +import data.model.PrivacyType; + +/** + * Created by iran on 2015-07-23. + */ +@Database(name = PoinilaDataBase.NAME, version = PoinilaDataBase.VERSION, generatedClassSeparator = "$") +public class PoinilaDataBase { + public static final String NAME = "poinila"; + + public static final int VERSION = 1; + public static final String SUGGESTION_INDEX = "index_suggestion"; + + private static Gson gson; + public static Gson getGson(){ + return gson; + } + /* static { + gson = new GsonBuilder().create(); + Index index = new Index<>(SUGGESTION_INDEX). + on(Suggestion.class, Suggestion$Table.CREATIONTIME); + // begins an index + index.enable(); + }*/ + +/* @ParcelClass(value = ModelAdapter.class, annotation = @Parcel(converter = ModelAdapterConverter.class)) + public abstract static class PoinilaDBModel extends BaseModel{ + @Column + public String jsonContent; + + protected abstract T getModel(); + protected T getModelFromJson(Class t){ + return PoinilaNetService.getGson().fromJson(jsonContent, t); + } + } + + public class ModelAdapterConverter implements ParcelConverter { + @Override + public void toParcel(ModelAdapter input, android.os.Parcel parcel) { + + } + + @Override + public ModelAdapter fromParcel(android.os.Parcel parcel) { + return null; + } + }*/ + public abstract static class PoinilaDBModel extends BaseModel implements Serializable{ + + @Column + public String jsonContent; + + protected abstract T getModel(); + protected T getModelFromJson(Class t){ + return PoinilaNetService.getGson().fromJson(jsonContent, t); + } + } + + public static class PrivacyTypeConverter extends TypeConverter{ + + @Override + public String getDBValue(PrivacyType model) { + return model.name(); + } + + @Override + public PrivacyType getModelValue(String data) { + return PrivacyType.valueOf(data); + } + } + + public static class SpannedTypeConverter extends TypeConverter{ + + @Override + public String getDBValue(Spanned model) { + return model.toString(); + } + + @Override + public Spanned getModelValue(String data) { + return Html.fromHtml(data); + } + } + + public static class GenderTypeConverter extends TypeConverter{ + + @Override + public String getDBValue(Gender model) { + return model.name(); + } + + @Override + public Gender getModelValue(String data) { + return Gender.valueOf(data); + } + } + +} diff --git a/src/main/java/data/event/AbstractNotificationsReceivedEvent.java b/src/main/java/data/event/AbstractNotificationsReceivedEvent.java new file mode 100755 index 0000000..13be58f --- /dev/null +++ b/src/main/java/data/event/AbstractNotificationsReceivedEvent.java @@ -0,0 +1,31 @@ +package data.event; + + +import java.util.List; + +/** + * Created by iran on 3/6/2016. + */ +public abstract class AbstractNotificationsReceivedEvent extends data.event.BaseEvent { + public List data; + public String bookmark; + + public AbstractNotificationsReceivedEvent(List data, String bookmark) { + this.data = data; + this.bookmark = bookmark; + } + + public static class MyNotificationsReceivedEvent extends AbstractNotificationsReceivedEvent { + + public MyNotificationsReceivedEvent(List data, String bookmark) { + super(data, bookmark); + } + } + + public static class OthersNotificationsReceivedEvent extends AbstractNotificationsReceivedEvent { + + public OthersNotificationsReceivedEvent(List data, String bookmark) { + super(data, bookmark); + } + } +} diff --git a/src/main/java/data/event/AnswerFriendRequestResponse.java b/src/main/java/data/event/AnswerFriendRequestResponse.java new file mode 100755 index 0000000..4d0624a --- /dev/null +++ b/src/main/java/data/event/AnswerFriendRequestResponse.java @@ -0,0 +1,13 @@ +package data.event; + +/** + * Created by iran on 2015-10-04. + */ +public class AnswerFriendRequestResponse extends data.event.ServerResponseEvent { + public data.model.FriendRequestAnswer answer; + + public AnswerFriendRequestResponse(boolean succeed, data.model.FriendRequestAnswer answer) { + super(succeed, ReceiverName.SelectInterest); + this.answer = answer; + } +} diff --git a/src/main/java/data/event/BaseEvent.java b/src/main/java/data/event/BaseEvent.java new file mode 100755 index 0000000..6516b1f --- /dev/null +++ b/src/main/java/data/event/BaseEvent.java @@ -0,0 +1,38 @@ +package data.event; + +import com.shaya.poinila.android.presentation.view.fragments.notification.NPostListFragment; + +/** + * Created by iran on 2015-06-16. + */ +public abstract class BaseEvent { + public int requestType; + + public BaseEvent(){ + + } + + public BaseEvent(ReceiverName receiverName) { + this.receiverName = receiverName; + } + + public enum ReceiverName { + DashboardFragment, + MyFollowedCollections, + SearchFragment, + PostListFragment, + CollectionPageFragment, + CollectionListFragment, + MemberListFragment, + RepostCollectionsList, + PostsImagesDialog, + NotificationFragment, + PostRelatedPosts, + CollectionDetailFragment, + MyProfileFragment, + ProfileFragment, + SelectInterest, ChangePassword, ExploredTagPosts, + NPostListFragment + } + public ReceiverName receiverName; +} diff --git a/src/main/java/data/event/CacheEvent.java b/src/main/java/data/event/CacheEvent.java new file mode 100755 index 0000000..30d785a --- /dev/null +++ b/src/main/java/data/event/CacheEvent.java @@ -0,0 +1,16 @@ +package data.event; + +/** + * Created by iran on 2015-07-07. + */ +public class CacheEvent { + private final T data; + + public CacheEvent(T data) { + this.data = data; + } + + public T getData(){ + return this.data; + } +} diff --git a/src/main/java/data/event/CircleReceivedEvent.java b/src/main/java/data/event/CircleReceivedEvent.java new file mode 100755 index 0000000..a38a99e --- /dev/null +++ b/src/main/java/data/event/CircleReceivedEvent.java @@ -0,0 +1,14 @@ +package data.event; + +import data.model.Circle; + +/** + * Created by iran on 2015-09-20. + */ +public class CircleReceivedEvent { + public Circle circle; + + public CircleReceivedEvent(Circle circle) { + this.circle = circle; + } +} diff --git a/src/main/java/data/event/CollectionReceivedEvent.java b/src/main/java/data/event/CollectionReceivedEvent.java new file mode 100755 index 0000000..c964b4b --- /dev/null +++ b/src/main/java/data/event/CollectionReceivedEvent.java @@ -0,0 +1,17 @@ +package data.event; + +/** + * Created by iran on 2015-07-25. + */ +public class CollectionReceivedEvent extends data.event.BaseEvent { + public data.model.Collection collection; + + public CollectionReceivedEvent(data.model.Collection collection){ + this.collection = collection; + } + + public CollectionReceivedEvent(data.model.Collection collection, ReceiverName target){ + this.collection = collection; + this.receiverName = target; + } +} diff --git a/src/main/java/data/event/CollectionUpdatedEvent.java b/src/main/java/data/event/CollectionUpdatedEvent.java new file mode 100755 index 0000000..940a3fa --- /dev/null +++ b/src/main/java/data/event/CollectionUpdatedEvent.java @@ -0,0 +1,11 @@ +package data.event; +/** + * Created by iran on 2015-11-07. + */ +public class CollectionUpdatedEvent extends data.event.BaseEvent { + public data.model.Collection collection; + + public CollectionUpdatedEvent(data.model.Collection collection) { + this.collection = collection; + } +} diff --git a/src/main/java/data/event/CollectionsReceivedEvent.java b/src/main/java/data/event/CollectionsReceivedEvent.java new file mode 100755 index 0000000..301e125 --- /dev/null +++ b/src/main/java/data/event/CollectionsReceivedEvent.java @@ -0,0 +1,21 @@ +package data.event; + +import java.util.List; + +import data.model.Collection; + +/** + * Created by iran on 2015-07-06. + */ +public class CollectionsReceivedEvent extends IdentifiableEvent{ + public List collections; + public String bookmark; + + + public CollectionsReceivedEvent(List collections, String bookmark, ReceiverName receiverName) { + super(receiverName); + this.collections = collections; + this.bookmark = bookmark; + } + +} diff --git a/src/main/java/data/event/CommentReceivedEvent.java b/src/main/java/data/event/CommentReceivedEvent.java new file mode 100755 index 0000000..62d8ecd --- /dev/null +++ b/src/main/java/data/event/CommentReceivedEvent.java @@ -0,0 +1,14 @@ +package data.event; + +/** + * Created by iran on 2015-09-20. + */ +public class CommentReceivedEvent { + public String postID; + public data.model.Comment comment; + + public CommentReceivedEvent(data.model.Comment comment, String postID) { + this.comment = comment; + this.postID = postID; + } +} diff --git a/src/main/java/data/event/CommentsReceivedEvent.java b/src/main/java/data/event/CommentsReceivedEvent.java new file mode 100755 index 0000000..e712d54 --- /dev/null +++ b/src/main/java/data/event/CommentsReceivedEvent.java @@ -0,0 +1,18 @@ +package data.event; + +import java.util.List; + +import data.model.Comment; + +/** + * Created by iran on 2015-07-27. + */ +public class CommentsReceivedEvent { + public List data; + public String bookmark; + + public CommentsReceivedEvent(List data, String bookmark) { + this.data = data; + this.bookmark = bookmark; + } +} diff --git a/src/main/java/data/event/ContentReceivedEvent.java b/src/main/java/data/event/ContentReceivedEvent.java new file mode 100755 index 0000000..1165811 --- /dev/null +++ b/src/main/java/data/event/ContentReceivedEvent.java @@ -0,0 +1,14 @@ +package data.event; + +/** + * Created by AlirezaF on 7/22/2015. + */ +public class ContentReceivedEvent { + public String content; + public int postID; + + public ContentReceivedEvent(String content, int postID) { + this.content = content; + this.postID = postID; + } +} diff --git a/src/main/java/data/event/DashboardEvent.java b/src/main/java/data/event/DashboardEvent.java new file mode 100755 index 0000000..abd531e --- /dev/null +++ b/src/main/java/data/event/DashboardEvent.java @@ -0,0 +1,36 @@ +package data.event; +import java.util.List; + +/** + * Created by iran on 2015-07-06. + */ +public class DashboardEvent extends data.event.BaseEvent { + public String statusCode; + public List data; + public String bookmark; + public boolean isFromCache; + + public DashboardEvent(List posts, boolean isFromCache) { + this.data = posts; + this.isFromCache = isFromCache; + } + + public DashboardEvent(String statusCode) { + this.statusCode = statusCode; + } + + public DashboardEvent(List data, boolean isFromCache, String bookmark) { + + this.data = data; + this.isFromCache = isFromCache; + this.bookmark = bookmark; + } + + public List getData(){ + return data; + } + + public String getMessage(){ + return statusCode; + } +} diff --git a/src/main/java/data/event/FailEvent.java b/src/main/java/data/event/FailEvent.java new file mode 100755 index 0000000..7549270 --- /dev/null +++ b/src/main/java/data/event/FailEvent.java @@ -0,0 +1,15 @@ +package data.event; + + +import data.RequestType; + +/** + * Created by iran on 2015-09-05. + */ +public class FailEvent { + public RequestType requestType; + + public FailEvent(RequestType requestType) { + this.requestType = requestType; + } +} diff --git a/src/main/java/data/event/FrameCollectionsReceivedEvent.java b/src/main/java/data/event/FrameCollectionsReceivedEvent.java new file mode 100755 index 0000000..b3eb68c --- /dev/null +++ b/src/main/java/data/event/FrameCollectionsReceivedEvent.java @@ -0,0 +1,16 @@ +package data.event; + +import java.util.List; + +import data.model.Collection; + +/** + * Created by iran on 2015-07-28. + */ +public class FrameCollectionsReceivedEvent { + public FrameCollectionsReceivedEvent(List frames) { + this.frames = frames; + } + + public List frames; +} diff --git a/src/main/java/data/event/FrameReceivedEvent.java b/src/main/java/data/event/FrameReceivedEvent.java new file mode 100755 index 0000000..4d878ee --- /dev/null +++ b/src/main/java/data/event/FrameReceivedEvent.java @@ -0,0 +1,15 @@ +package data.event; + +import data.model.Frame; + +/** + * Created by iran on 2015-09-20. + */ +public class FrameReceivedEvent { + public Frame frame; + + public FrameReceivedEvent(Frame frame) { + + this.frame = frame; + } +} diff --git a/src/main/java/data/event/FramesReceivedEvent.java b/src/main/java/data/event/FramesReceivedEvent.java new file mode 100755 index 0000000..58b1c9a --- /dev/null +++ b/src/main/java/data/event/FramesReceivedEvent.java @@ -0,0 +1,14 @@ +package data.event; + +import java.util.List; + +/** + * Created by iran on 2015-07-28. + */ +public class FramesReceivedEvent { + public FramesReceivedEvent(List frames) { + this.frames = frames; + } + + public List frames; +} diff --git a/src/main/java/data/event/FriendCircleNotChangedEvent.java b/src/main/java/data/event/FriendCircleNotChangedEvent.java new file mode 100755 index 0000000..8d2ae66 --- /dev/null +++ b/src/main/java/data/event/FriendCircleNotChangedEvent.java @@ -0,0 +1,14 @@ +package data.event; + +/** + * Created by iran on 2015-07-27. + */ +public class FriendCircleNotChangedEvent { + public String circleID; + public String friendId; + + public FriendCircleNotChangedEvent(String circleID, String friendId) { + this.circleID = circleID; + this.friendId = friendId; + } +} diff --git a/src/main/java/data/event/IdentifiableEvent.java b/src/main/java/data/event/IdentifiableEvent.java new file mode 100755 index 0000000..6f45cdc --- /dev/null +++ b/src/main/java/data/event/IdentifiableEvent.java @@ -0,0 +1,10 @@ +package data.event; + +/** + * Created by iran on 2015-08-22. + */ +public class IdentifiableEvent extends BaseEvent { + public IdentifiableEvent(ReceiverName receiverName){ + this.receiverName = receiverName; + } +} diff --git a/src/main/java/data/event/InterestsReceivedEvent.java b/src/main/java/data/event/InterestsReceivedEvent.java new file mode 100755 index 0000000..a85c5af --- /dev/null +++ b/src/main/java/data/event/InterestsReceivedEvent.java @@ -0,0 +1,20 @@ +package data.event; + +import java.util.List; + +/** + * Created by iran on 2015-09-08. + */ +public class InterestsReceivedEvent { + public String superInterestID; + public List interests; + + public InterestsReceivedEvent(List interests) { + this.interests = interests; + } + + public InterestsReceivedEvent(List interests, String superInterestID) { + this.interests = interests; + this.superInterestID = superInterestID; + } +} diff --git a/src/main/java/data/event/InviteUsedEvent.java b/src/main/java/data/event/InviteUsedEvent.java new file mode 100755 index 0000000..cb9e6c8 --- /dev/null +++ b/src/main/java/data/event/InviteUsedEvent.java @@ -0,0 +1,7 @@ +package data.event; + +/** + * Created by iran on 11/25/2015. + */ +public class InviteUsedEvent extends BaseEvent{ +} diff --git a/src/main/java/data/event/LoadingImagedFailedEvent.java b/src/main/java/data/event/LoadingImagedFailedEvent.java new file mode 100755 index 0000000..ad877e8 --- /dev/null +++ b/src/main/java/data/event/LoadingImagedFailedEvent.java @@ -0,0 +1,7 @@ +package data.event; + +/** + * Created by iran on 2015-10-14. + */ +public class LoadingImagedFailedEvent { +} diff --git a/src/main/java/data/event/LoginFailedEvent.java b/src/main/java/data/event/LoginFailedEvent.java new file mode 100755 index 0000000..c7c607e --- /dev/null +++ b/src/main/java/data/event/LoginFailedEvent.java @@ -0,0 +1,17 @@ +package data.event; + +import com.google.gson.JsonObject; + +/** + * Created by iran on 2015-08-24. + */ +public class LoginFailedEvent { + public int code; + public JsonObject dataJson; + public String error; + + public LoginFailedEvent(int code, JsonObject dataJson) { + this.code = code; + this.dataJson = dataJson; + } +} diff --git a/src/main/java/data/event/LoginSucceedEvent.java b/src/main/java/data/event/LoginSucceedEvent.java new file mode 100755 index 0000000..b6f7fcd --- /dev/null +++ b/src/main/java/data/event/LoginSucceedEvent.java @@ -0,0 +1,7 @@ +package data.event; + +/** + * Created by iran on 2015-08-24. + */ +public class LoginSucceedEvent { +} diff --git a/src/main/java/data/event/MemberFollowersEvent.java b/src/main/java/data/event/MemberFollowersEvent.java new file mode 100755 index 0000000..de1aee9 --- /dev/null +++ b/src/main/java/data/event/MemberFollowersEvent.java @@ -0,0 +1,16 @@ +package data.event; + +import java.util.List; + +import data.model.Member; + +/** + * Created by iran on 2015-07-27. + */ +public class MemberFollowersEvent { + public MemberFollowersEvent(List members, String bookmark) { + this.members = members; + } + + public List members; +} diff --git a/src/main/java/data/event/MemberPostsEvent.java b/src/main/java/data/event/MemberPostsEvent.java new file mode 100755 index 0000000..8db60c7 --- /dev/null +++ b/src/main/java/data/event/MemberPostsEvent.java @@ -0,0 +1,16 @@ +package data.event; + +import java.util.List; + +import data.model.Post; + +/** + * Created by iran on 2015-07-27. + */ +public class MemberPostsEvent { + public List posts; + + public MemberPostsEvent(List posts) { + this.posts = posts; + } +} diff --git a/src/main/java/data/event/MemberReceivedEvent.java b/src/main/java/data/event/MemberReceivedEvent.java new file mode 100755 index 0000000..df62ae3 --- /dev/null +++ b/src/main/java/data/event/MemberReceivedEvent.java @@ -0,0 +1,14 @@ +package data.event; + +import data.model.Member; + +/** + * Created by AlirezaF on 7/22/2015. + */ +public class MemberReceivedEvent extends BaseEvent{ + public Member member; + + public MemberReceivedEvent(Member member) { + this.member = member; + } +} diff --git a/src/main/java/data/event/MembersReceivedEvent.java b/src/main/java/data/event/MembersReceivedEvent.java new file mode 100755 index 0000000..2b5ff0b --- /dev/null +++ b/src/main/java/data/event/MembersReceivedEvent.java @@ -0,0 +1,21 @@ +package data.event; + +import java.util.List; + +import data.model.Member; + +/** + * Created by iran on 2015-07-27. + */ +public class MembersReceivedEvent extends BaseEvent{// extends IdentifiableEvent{ + + public List members; + public String bookmark; + + public MembersReceivedEvent(List members, String bookmark) { + //super(receiverTag); + + this.members = members; + this.bookmark = bookmark; + } +} diff --git a/src/main/java/data/event/ModelCreatedEvent.java b/src/main/java/data/event/ModelCreatedEvent.java new file mode 100755 index 0000000..3068bdb --- /dev/null +++ b/src/main/java/data/event/ModelCreatedEvent.java @@ -0,0 +1,12 @@ +package data.event; + +import com.raizlabs.android.dbflow.structure.BaseModel; + +/** + * Created by iran on 2015-09-27. + */ +public class ModelCreatedEvent extends data.event.ModelEvent { + public ModelCreatedEvent(BaseModel model) { + super(model); + } +} diff --git a/src/main/java/data/event/ModelDeletedEvent.java b/src/main/java/data/event/ModelDeletedEvent.java new file mode 100755 index 0000000..d4da103 --- /dev/null +++ b/src/main/java/data/event/ModelDeletedEvent.java @@ -0,0 +1,11 @@ +package data.event; + +import com.raizlabs.android.dbflow.structure.BaseModel; +/** + * Created by iran on 2015-09-27. + */ +public class ModelDeletedEvent extends ModelEvent{ + public ModelDeletedEvent(BaseModel model) { + super(model); + } +} diff --git a/src/main/java/data/event/ModelEvent.java b/src/main/java/data/event/ModelEvent.java new file mode 100755 index 0000000..4c6b3ca --- /dev/null +++ b/src/main/java/data/event/ModelEvent.java @@ -0,0 +1,14 @@ +package data.event; + +import com.raizlabs.android.dbflow.structure.BaseModel; + +/** + * Created by iran on 2015-09-27. + */ +public abstract class ModelEvent { + public BaseModel model; + + public ModelEvent(BaseModel model) { + this.model = model; + } +} diff --git a/src/main/java/data/event/ModelUpdatedEvent.java b/src/main/java/data/event/ModelUpdatedEvent.java new file mode 100755 index 0000000..6464cbc --- /dev/null +++ b/src/main/java/data/event/ModelUpdatedEvent.java @@ -0,0 +1,12 @@ +package data.event; + +import com.raizlabs.android.dbflow.structure.BaseModel; + +/** + * Created by iran on 2015-09-27. + */ +public class ModelUpdatedEvent extends data.event.ModelEvent { + public ModelUpdatedEvent(BaseModel model) { + super(model); + } +} diff --git a/src/main/java/data/event/MyFrameReceivedEvent.java b/src/main/java/data/event/MyFrameReceivedEvent.java new file mode 100755 index 0000000..5032fca --- /dev/null +++ b/src/main/java/data/event/MyFrameReceivedEvent.java @@ -0,0 +1,14 @@ +package data.event; + +import java.util.List; + +/** + * Created by iran on 2015-08-01. + */ +public class MyFrameReceivedEvent { + public List frames; + + public MyFrameReceivedEvent(List frames) { + this.frames = frames; + } +} diff --git a/src/main/java/data/event/MyFriendshipRequestsEvent.java b/src/main/java/data/event/MyFriendshipRequestsEvent.java new file mode 100755 index 0000000..e283084 --- /dev/null +++ b/src/main/java/data/event/MyFriendshipRequestsEvent.java @@ -0,0 +1,19 @@ +package data.event; + + +import java.util.List; + +import data.model.InvitationNotif; + +/** + * Created by iran on 2015-08-15. + */ +public class MyFriendshipRequestsEvent extends BaseEvent { + public List data; + public String bookmark; + + public MyFriendshipRequestsEvent(List data, String bookmark) { + this.data = data; + this.bookmark = bookmark; + } +} diff --git a/src/main/java/data/event/MyInfoReceivedEvent.java b/src/main/java/data/event/MyInfoReceivedEvent.java new file mode 100755 index 0000000..7147d4c --- /dev/null +++ b/src/main/java/data/event/MyInfoReceivedEvent.java @@ -0,0 +1,23 @@ +package data.event; + + +import data.model.Member; + +/** + * Created by iran on 2015-07-27. + */ +public class MyInfoReceivedEvent extends BaseEvent { + public Member me; + public boolean fromCache; + public MY_INFO_TYPE type; + + public enum MY_INFO_TYPE{ + LOAD, UPDATE, VERIFY + } + + public MyInfoReceivedEvent(Member me, boolean fromCache, MY_INFO_TYPE type) { + this.me = me; + this.fromCache = fromCache; + this.type = type; + } +} diff --git a/src/main/java/data/event/NotificationSettingsReceived.java b/src/main/java/data/event/NotificationSettingsReceived.java new file mode 100755 index 0000000..8231716 --- /dev/null +++ b/src/main/java/data/event/NotificationSettingsReceived.java @@ -0,0 +1,18 @@ +package data.event; + + +import java.util.List; + +import data.model.OnOffSetting; + +/** + * Created by iran on 2015-09-07. + */ +public class NotificationSettingsReceived { + public List notificationSettings; + + public NotificationSettingsReceived(List notificationSettings) { + + this.notificationSettings = notificationSettings; + } +} diff --git a/src/main/java/data/event/NotificationSettingsReceivedEvent.java b/src/main/java/data/event/NotificationSettingsReceivedEvent.java new file mode 100755 index 0000000..98ddd3a --- /dev/null +++ b/src/main/java/data/event/NotificationSettingsReceivedEvent.java @@ -0,0 +1,17 @@ +package data.event; + +import java.util.List; + +import data.model.OnOffSetting; + +/** + * Created by iran on 2015-09-07. + */ +public class NotificationSettingsReceivedEvent extends BaseEvent{ + public List notificationSettings; + + public NotificationSettingsReceivedEvent(List notificationSettings) { + + this.notificationSettings = notificationSettings; + } +} diff --git a/src/main/java/data/event/PostReceivedEvent.java b/src/main/java/data/event/PostReceivedEvent.java new file mode 100755 index 0000000..15f8488 --- /dev/null +++ b/src/main/java/data/event/PostReceivedEvent.java @@ -0,0 +1,18 @@ +package data.event; + +/** + * Created by AlirezaF on 7/22/2015. + */ +public class PostReceivedEvent extends data.event.BaseEvent { + public int requestId; + public data.model.Post post; + + public PostReceivedEvent(data.model.Post post) { + this.post = post; + } + + public PostReceivedEvent(data.model.Post post, int requestId) { + this(post); + this.requestId = requestId; + } +} diff --git a/src/main/java/data/event/PostsReceivedEvent.java b/src/main/java/data/event/PostsReceivedEvent.java new file mode 100755 index 0000000..360fc70 --- /dev/null +++ b/src/main/java/data/event/PostsReceivedEvent.java @@ -0,0 +1,25 @@ +package data.event; + +import java.util.List; + +import data.model.Post; + +/** + * Created by iran on 2015-07-27. + */ +public class PostsReceivedEvent extends IdentifiableEvent{ + public List posts; + public String bookmark; + public int requestId; + + public PostsReceivedEvent(List posts, String bookmark, ReceiverName receiverName, int requestId) { + this(posts, bookmark, receiverName); + this.requestId = requestId; + } + + public PostsReceivedEvent(List posts, String bookmark, ReceiverName receiverName) { + super(receiverName); + this.posts = posts; + this.bookmark = bookmark; + } +} diff --git a/src/main/java/data/event/ProfileDirtyEvent.java b/src/main/java/data/event/ProfileDirtyEvent.java new file mode 100755 index 0000000..8f82729 --- /dev/null +++ b/src/main/java/data/event/ProfileDirtyEvent.java @@ -0,0 +1,7 @@ +package data.event; + +/** + * Created by iran on 2015-10-13. + */ +public class ProfileDirtyEvent { +} diff --git a/src/main/java/data/event/ProfileSettingReceivedEvent.java b/src/main/java/data/event/ProfileSettingReceivedEvent.java new file mode 100755 index 0000000..d645bbd --- /dev/null +++ b/src/main/java/data/event/ProfileSettingReceivedEvent.java @@ -0,0 +1,14 @@ +package data.event; + +import data.model.Member; + +/** + * Created by iran on 2015-07-29. + */ +public class ProfileSettingReceivedEvent { + public ProfileSettingReceivedEvent(Member member) { + this.member = member; + } + + public Member member; +} diff --git a/src/main/java/data/event/RegisterResponseEvent.java b/src/main/java/data/event/RegisterResponseEvent.java new file mode 100755 index 0000000..be815d1 --- /dev/null +++ b/src/main/java/data/event/RegisterResponseEvent.java @@ -0,0 +1,22 @@ +package data.event; + +/** + * Created by AlirezaF on 12/7/2015. + */ +public class RegisterResponseEvent extends BaseEvent { + public static final int DUPLICATE_USERNAME = 1; + public static final int USED_VERIFICATION_CODE = 2; + + public RegisterResponseEvent(boolean successful) { + this.successful = successful; + } + + public boolean successful; + public int errorCode; + + public RegisterResponseEvent(boolean successful, int errorCode) { + + this.successful = successful; + this.errorCode = errorCode; + } +} diff --git a/src/main/java/data/event/RemainedInvitesEvent.java b/src/main/java/data/event/RemainedInvitesEvent.java new file mode 100755 index 0000000..6565cda --- /dev/null +++ b/src/main/java/data/event/RemainedInvitesEvent.java @@ -0,0 +1,12 @@ +package data.event; + +/** + * Created by iran on 2015-10-01. + */ +public class RemainedInvitesEvent { + public int remained; + + public RemainedInvitesEvent(int remained) { + this.remained = remained; + } +} diff --git a/src/main/java/data/event/SearchCollectionEvent.java b/src/main/java/data/event/SearchCollectionEvent.java new file mode 100755 index 0000000..b5ff9c4 --- /dev/null +++ b/src/main/java/data/event/SearchCollectionEvent.java @@ -0,0 +1,28 @@ +package data.event; +import java.util.List; + +import data.model.Collection; + +/** + * Created by iran on 2015-07-07. + */ +public class SearchCollectionEvent { + private final int statusCode; + List data; + public SearchCollectionEvent(List data) { + this.data = data; + statusCode=200; + } + + public SearchCollectionEvent(int statusCode) { + this.statusCode = statusCode; + } + + public List getData(){ + return data; + } + + public int getStatusCode(){ + return statusCode; + } +} diff --git a/src/main/java/data/event/SearchMemberEvent.java b/src/main/java/data/event/SearchMemberEvent.java new file mode 100755 index 0000000..4b8e522 --- /dev/null +++ b/src/main/java/data/event/SearchMemberEvent.java @@ -0,0 +1,29 @@ +package data.event; + +import java.util.List; + +import data.model.Member; + +/** + * Created by iran on 2015-07-07. + */ +public class SearchMemberEvent { + private int statusCode; + List data; + public SearchMemberEvent(List posts) { + this.data = posts; + statusCode=200; + } + + public SearchMemberEvent(int statusCode) { + this.statusCode = statusCode; + } + + public List getData(){ + return data; + } + + public int getStatusCode(){ + return statusCode; + } +} diff --git a/src/main/java/data/event/SearchPostEvent.java b/src/main/java/data/event/SearchPostEvent.java new file mode 100755 index 0000000..e205454 --- /dev/null +++ b/src/main/java/data/event/SearchPostEvent.java @@ -0,0 +1,27 @@ +package data.event; + +import java.util.List; + +/** + * Created by iran on 2015-07-07. + */ +public class SearchPostEvent { + private int statusCode; + List data; + public SearchPostEvent(List posts) { + this.data = posts; + statusCode=200; + } + + public SearchPostEvent(int statusCode) { + this.statusCode = statusCode; + } + + public List getData(){ + return data; + } + + public int getStatusCode(){ + return statusCode; + } +} diff --git a/src/main/java/data/event/ServerResponseEvent.java b/src/main/java/data/event/ServerResponseEvent.java new file mode 100755 index 0000000..1364355 --- /dev/null +++ b/src/main/java/data/event/ServerResponseEvent.java @@ -0,0 +1,21 @@ +package data.event; + +/** + * Created by iran on 2015-10-04. + */ +public class ServerResponseEvent extends data.event.BaseEvent { + public int errorCode; + + public ServerResponseEvent(boolean succeed, ReceiverName receiverName) { + super(receiverName); + this.succeed = succeed; + } + public boolean succeed; + + public ServerResponseEvent(boolean succeed, ReceiverName receiverName, int errorCode) { + + this.succeed = succeed; + this.receiverName = receiverName; + this.errorCode = errorCode; + } +} diff --git a/src/main/java/data/event/StringsReceivedEvent.java b/src/main/java/data/event/StringsReceivedEvent.java new file mode 100755 index 0000000..98f0c81 --- /dev/null +++ b/src/main/java/data/event/StringsReceivedEvent.java @@ -0,0 +1,14 @@ +package data.event; + +import java.util.List; + +/** + * Created by iran on 2015-10-10. + */ +public class StringsReceivedEvent { + public List strings; + + public StringsReceivedEvent(List strings) { + this.strings = strings; + } +} diff --git a/src/main/java/data/event/SuggestedWebpagePostReceived.java b/src/main/java/data/event/SuggestedWebpagePostReceived.java new file mode 100755 index 0000000..ab06b03 --- /dev/null +++ b/src/main/java/data/event/SuggestedWebpagePostReceived.java @@ -0,0 +1,15 @@ +package data.event; + +import data.model.SuggestedWebPagePost; + +/** + * Created by AlirezaF on 10/15/2015. + */ +public class SuggestedWebpagePostReceived extends data.event.BaseEvent { + public SuggestedWebPagePost webpagePost; + + public SuggestedWebpagePostReceived(SuggestedWebPagePost webpagePost) { + + this.webpagePost = webpagePost; + } +} diff --git a/src/main/java/data/event/SystemPreferencesReceivedEvent.java b/src/main/java/data/event/SystemPreferencesReceivedEvent.java new file mode 100755 index 0000000..44711bb --- /dev/null +++ b/src/main/java/data/event/SystemPreferencesReceivedEvent.java @@ -0,0 +1,13 @@ +package data.event; + +/** + * Created by iran on 1/20/2016. + */ +public class SystemPreferencesReceivedEvent extends data.event.BaseEvent { + public data.model.SystemPreferences systemPreferences; + + public SystemPreferencesReceivedEvent(data.model.SystemPreferences systemPreferences) { + + this.systemPreferences = systemPreferences; + } +} diff --git a/src/main/java/data/event/TopicsReceivedEvent.java b/src/main/java/data/event/TopicsReceivedEvent.java new file mode 100755 index 0000000..2a2937c --- /dev/null +++ b/src/main/java/data/event/TopicsReceivedEvent.java @@ -0,0 +1,14 @@ +package data.event; + +import java.util.List; + +/** + * Created by iran on 2015-08-26. + */ +public class TopicsReceivedEvent extends data.event.BaseEvent { + public List data; + + public TopicsReceivedEvent(List data) { + this.data = data; + } +} diff --git a/src/main/java/data/event/UndoFavePostEvent.java b/src/main/java/data/event/UndoFavePostEvent.java new file mode 100755 index 0000000..4096a6e --- /dev/null +++ b/src/main/java/data/event/UndoFavePostEvent.java @@ -0,0 +1,7 @@ +package data.event; + +/** + * Created by iran on 2015-07-23. + */ +public class UndoFavePostEvent { +} diff --git a/src/main/java/data/event/UndoUnfavePostEvent.java b/src/main/java/data/event/UndoUnfavePostEvent.java new file mode 100755 index 0000000..7d66516 --- /dev/null +++ b/src/main/java/data/event/UndoUnfavePostEvent.java @@ -0,0 +1,7 @@ +package data.event; + +/** + * Created by iran on 2015-07-23. + */ +public class UndoUnfavePostEvent { +} diff --git a/src/main/java/data/event/UpdateProfileSettingResponse.java b/src/main/java/data/event/UpdateProfileSettingResponse.java new file mode 100755 index 0000000..40d0473 --- /dev/null +++ b/src/main/java/data/event/UpdateProfileSettingResponse.java @@ -0,0 +1,20 @@ +package data.event; + +import com.shaya.poinila.android.presentation.view.activity.SettingActivity; + +/** + * Created by iran on 1/11/2016. + */ +public class UpdateProfileSettingResponse extends BaseEvent { + public boolean success; + public SettingActivity.SettingType settingType; + + public UpdateProfileSettingResponse(boolean success) { + this.success = success; + } + + public UpdateProfileSettingResponse(boolean success, SettingActivity.SettingType settingType) { + this.success = success; + this.settingType = settingType; + } +} diff --git a/src/main/java/data/event/UserInterestsReceivedEvent.java b/src/main/java/data/event/UserInterestsReceivedEvent.java new file mode 100755 index 0000000..6f204a4 --- /dev/null +++ b/src/main/java/data/event/UserInterestsReceivedEvent.java @@ -0,0 +1,16 @@ +package data.event; + +import java.util.List; + +import data.model.ImageTag; + +/** + * Created by iran on 2015-09-08. + */ +public class UserInterestsReceivedEvent extends BaseEvent{ + public List userInterests; + + public UserInterestsReceivedEvent(List userInterests) { + this.userInterests = userInterests; + } +} diff --git a/src/main/java/data/event/UserNameValidityEvent.java b/src/main/java/data/event/UserNameValidityEvent.java new file mode 100755 index 0000000..b680bde --- /dev/null +++ b/src/main/java/data/event/UserNameValidityEvent.java @@ -0,0 +1,23 @@ +package data.event; + +/** + * Created by iran on 12/14/2015. + */ +public class UserNameValidityEvent { + public static final int DUPLICATE = 1; + public static final int RESERVED = 2; + public static final int RULE = 3; + public static final int LENGTH = 4; + public boolean success; + public final int error; + + public UserNameValidityEvent(int error) { + this.error = error; + } + + public UserNameValidityEvent(boolean success, int error) { + + this.success = success; + this.error = error; + } +} diff --git a/src/main/java/data/event/VerificationRequestResponse.java b/src/main/java/data/event/VerificationRequestResponse.java new file mode 100755 index 0000000..1890f9f --- /dev/null +++ b/src/main/java/data/event/VerificationRequestResponse.java @@ -0,0 +1,41 @@ +package data.event; + + +/** + * Created by AlirezaF on 12/7/2015. + */ +public class VerificationRequestResponse extends data.event.BaseEvent { + public boolean succeed; + public int code; + public String errorExplanation; + public boolean byEmail; + public String emailOrPhone; + + public VerificationRequestResponse(boolean succeed, boolean byEmail, String emailOrPhone) { + this(succeed, null); + this.byEmail = byEmail; + this.emailOrPhone = emailOrPhone; + } + + public VerificationRequestResponse(boolean succeed, String errorExplanation) { + this.succeed = succeed; + this.errorExplanation = errorExplanation; + } + + public VerificationRequestResponse(boolean succeed) { + this(succeed, null); + } + + public VerificationRequestResponse(boolean succeed, int code) { + this.succeed = succeed; + this.code = code; + } + /*public VerificationRequestSentEvent(String code) { + Code = code; + } + + public VerificationRequestSentEvent() { + } + + public String Code;*/ +} diff --git a/src/main/java/data/exception/AuthorizationException.java b/src/main/java/data/exception/AuthorizationException.java new file mode 100755 index 0000000..6cc43c6 --- /dev/null +++ b/src/main/java/data/exception/AuthorizationException.java @@ -0,0 +1,7 @@ +package data.exception; + +/** + * Created by iran on 2015-10-01. + */ +public class AuthorizationException extends RuntimeException { +} diff --git a/src/main/java/data/model/Circle.java b/src/main/java/data/model/Circle.java new file mode 100755 index 0000000..91d01fa --- /dev/null +++ b/src/main/java/data/model/Circle.java @@ -0,0 +1,59 @@ +package data.model; + +import com.google.gson.annotations.SerializedName; +import com.raizlabs.android.dbflow.annotation.Column; +import com.raizlabs.android.dbflow.annotation.PrimaryKey; +import com.raizlabs.android.dbflow.annotation.Table; +import com.raizlabs.android.dbflow.structure.BaseModel; +import org.parceler.Parcel; +import org.parceler.ParcelConverter; + +import data.database.PoinilaDataBase; + +/** + * Created by iran on 2015-07-20. + */ +@Parcel(analyze= Circle.class) +@Table(database = PoinilaDataBase.class) +public class Circle extends BaseModel implements Identifiable { + public Circle() { + } + + public Circle(int id, String name) { + this.id = id; + this.name = name; + } + + @Column + @PrimaryKey + public int id; + + @Column + public String name; + + @Column + @SerializedName(value = "is_default") + public DefaultType defaultType; + + @Override + public String getId() { + return String.valueOf(id); + } + + /* @Override + public Circle getModel() { + return getModelFromJson(Circle.class); + }*/ + + // used in edit friend circles we must show which circles the friend is already assigned. + public transient boolean selected = false; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Circle circle = (Circle) o; + return id == circle.id; + } + +} diff --git a/src/main/java/data/model/Collection.java b/src/main/java/data/model/Collection.java new file mode 100755 index 0000000..65366f9 --- /dev/null +++ b/src/main/java/data/model/Collection.java @@ -0,0 +1,84 @@ +package data.model; + + +import com.google.gson.annotations.SerializedName; +import com.raizlabs.android.dbflow.annotation.Column; +import com.raizlabs.android.dbflow.annotation.PrimaryKey; +import com.raizlabs.android.dbflow.annotation.Table; + +import org.parceler.Parcel; +import org.parceler.ParcelConverter; +import org.parceler.Parcels; +import org.parceler.TypeRangeParcelConverter; + +import java.util.Date; +import java.util.List; + +/** + * @author Alireza Farahani + * Created by iran on 2015-06-10. + */ +@Parcel(analyze=Collection.class) +@Table(database = data.database.PoinilaDataBase.class) +public class Collection extends data.database.PoinilaDataBase.PoinilaDBModel implements data.model.Identifiable { + + // Invisible Fields + @Column + @PrimaryKey + public int id; + + // Visible Fields + @Column + public String name; + public data.model.Member owner; + public data.model.PrivacyType privacy; + public String description; + // har collection hatman ye topic dare ke moghe + // sakht azash miporsim va editable'e + public Tag topic; + + @SerializedName(value = "last_post_creation_time") public Date lastPostCreationTime; + @SerializedName(value = "like_count") public int totalLikeCount; + @SerializedName(value = "comment_count") public int totalCommentCount; + @SerializedName(value = "repost_count") public int totalRepostCount; + @SerializedName(value = "follow_count") public int followerCount; + @SerializedName(value = "post_count") public int postCount; + @SerializedName(value = "followed_by_me") public boolean followedByMe; + @SerializedName(value = "images") public data.model.ImageUrls coverImageUrls; + @SerializedName(value = "first_post_images") public data.model.ImageUrls image1Url; + @SerializedName(value = "second_post_images") public data.model.ImageUrls image2Url; + @SerializedName(value = "third_post_images") public data.model.ImageUrls image3Url; + @SerializedName(value = "frame_ids") public List frameIDs; + @SerializedName(value = "unseen_posts_count") public int unseenPostsCount; + // TODO: az koja miad. felan tu api nist + @SerializedName(value = "circle_ids") public List circleIDs; + + // used in frame management. + public transient boolean selected = false; + + @Override + public Collection getModel() { + return getModelFromJson(Collection.class); + } + + @Override + public String getId() { + return String.valueOf(id); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Collection that = (Collection) o; + return id == that.id; + } + + public String getOriginalCoverUrl() { + if (coverImageUrls == null) return null; + Image image = coverImageUrls.properCollectionImage(data.model.ImageUrls.ImageSize.BIG); + return (image != null) ? image.url : null; + } + +} diff --git a/src/main/java/data/model/Comment.java b/src/main/java/data/model/Comment.java new file mode 100755 index 0000000..cf35d8c --- /dev/null +++ b/src/main/java/data/model/Comment.java @@ -0,0 +1,39 @@ +package data.model; + +import com.google.gson.annotations.SerializedName; +import com.raizlabs.android.dbflow.annotation.Column; +import com.raizlabs.android.dbflow.annotation.PrimaryKey; +import com.raizlabs.android.dbflow.annotation.Table; +import com.raizlabs.android.dbflow.structure.BaseModel; +import com.shaya.poinila.android.util.PoinilaPreferences; + +import org.parceler.Parcel; + +import java.util.Date; + +import data.database.PoinilaDataBase; + +/** + * Created by iran on 2015-06-25. + */ +@Parcel(analyze= Comment.class) +@Table(database = PoinilaDataBase.class) +public class Comment extends BaseModel implements Identifiable { + @Column + @PrimaryKey + public int id; + public String content; + @SerializedName(value = "creation_time") public Date creationDate; + // last_update_time + @SerializedName(value = "is_deletable") public boolean deletable; + public Member commenter; + + @Override + public String getId() { + return String.valueOf(id); + } + + public boolean isDeletable() { + return commenter.getId().equals(PoinilaPreferences.getMyId()); + } +} diff --git a/src/main/java/data/model/Content.java b/src/main/java/data/model/Content.java new file mode 100755 index 0000000..21d06b4 --- /dev/null +++ b/src/main/java/data/model/Content.java @@ -0,0 +1,41 @@ +package data.model; + +import com.raizlabs.android.dbflow.annotation.Column; +import com.raizlabs.android.dbflow.annotation.PrimaryKey; +import com.raizlabs.android.dbflow.annotation.Table; +import com.raizlabs.android.dbflow.structure.BaseModel; + +/** + * Created by iran on 2015-07-03. + */ +@Table(database = data.database.PoinilaDataBase.class) +public class Content extends BaseModel implements data.model.Identifiable { + public Content(String url) { + this.url = url; + } + + public Content(){} + + public Content(String url, String text) { + this.url = url; + this.text = text; + } + + @Column(name = "id") + @PrimaryKey + public String url; + + @Column + public String text; + + /* public Content(String url, Spanned text) { + this.url = url; + this.text = text.toString(); + }*/ + + + @Override + public String getId() { + return url; + } +} diff --git a/src/main/java/data/model/DefaultType.java b/src/main/java/data/model/DefaultType.java new file mode 100755 index 0000000..c46978c --- /dev/null +++ b/src/main/java/data/model/DefaultType.java @@ -0,0 +1,13 @@ +package data.model; + +import com.google.gson.annotations.SerializedName; + +/** + * Created by iran on 2015-09-30. + */ +public enum DefaultType { + @SerializedName("default") + DEFAULT, + @SerializedName("not_default") + NOT_DEFAULT, +} diff --git a/src/main/java/data/model/Frame.java b/src/main/java/data/model/Frame.java new file mode 100755 index 0000000..1b139b2 --- /dev/null +++ b/src/main/java/data/model/Frame.java @@ -0,0 +1,56 @@ +package data.model; + +import com.raizlabs.android.dbflow.annotation.Column; +import com.raizlabs.android.dbflow.annotation.PrimaryKey; +import com.raizlabs.android.dbflow.annotation.Table; +import com.raizlabs.android.dbflow.structure.BaseModel; + +import org.parceler.Parcel; +import org.parceler.ParcelConverter; + +/** + * Created by iran on 2015-07-28. + */ +@Parcel(analyze=Frame.class) +@Table(database = data.database.PoinilaDataBase.class) +public class Frame extends BaseModel implements data.model.Identifiable { + public Frame() { + } + + public Frame(int id, String name) { + this.id = id; + this.name = name; + } + @Column + @PrimaryKey + public int id; + @Column + public String name; + + @Override + public String toString() { + return name; + } + + @Override + public String getId() { + return String.valueOf(id); + } + + public static class ModelConverter implements ParcelConverter { + @Override + public void toParcel(Frame frame, android.os.Parcel parcel) { + + } + + @Override + public Frame fromParcel(android.os.Parcel parcel) { + return null; + } + } + + /* @Override + public Frame getModel() { + return getModelFromJson(Frame.class); + }*/ +} diff --git a/src/main/java/data/model/FriendRequestAnswer.java b/src/main/java/data/model/FriendRequestAnswer.java new file mode 100755 index 0000000..d1455d6 --- /dev/null +++ b/src/main/java/data/model/FriendRequestAnswer.java @@ -0,0 +1,18 @@ +package data.model; + +/** + * Created by iran on 2015-07-20. + */ +public enum FriendRequestAnswer { + ACCEPT ("accept"), + REJECT ("reject"); + + + private final String answer; + FriendRequestAnswer(String answer) { + this.answer = answer; + } + public String getAnswer(){ + return answer; + } +} diff --git a/src/main/java/data/model/FriendshipNotif.java b/src/main/java/data/model/FriendshipNotif.java new file mode 100755 index 0000000..d3cf4e6 --- /dev/null +++ b/src/main/java/data/model/FriendshipNotif.java @@ -0,0 +1,22 @@ +package data.model; + +import com.google.gson.annotations.SerializedName; + +import java.util.Date; + +/** + * Created by iran on 2015-08-16. + */ +public class FriendshipNotif implements Timed { + public boolean seen; + @SerializedName(value = "creation_time") public Date creationTime; + + + + public Member member; + + @Override + public long getCreationTime() { + return 0; + } +} diff --git a/src/main/java/data/model/FriendshipStatus.java b/src/main/java/data/model/FriendshipStatus.java new file mode 100755 index 0000000..84516aa --- /dev/null +++ b/src/main/java/data/model/FriendshipStatus.java @@ -0,0 +1,14 @@ +package data.model; + +import com.google.gson.annotations.SerializedName; + +/** + + * Created by iran on 2015-11-14. + */ +public enum FriendshipStatus { + @SerializedName("friend") IsFriend, + @SerializedName("not_friend") NotFriend, + @SerializedName("waiting_for_my_response") WaitingForAction, + @SerializedName("my_request_pending") Pending, +} diff --git a/src/main/java/data/model/Gender.java b/src/main/java/data/model/Gender.java new file mode 100755 index 0000000..3133866 --- /dev/null +++ b/src/main/java/data/model/Gender.java @@ -0,0 +1,13 @@ +package data.model; + +import com.google.gson.annotations.SerializedName; + +/** + * Created by iran on 2015-07-20. + */ +public enum Gender { + @SerializedName(value = "female") + FEMALE, + @SerializedName(value = "male") + MALE +} diff --git a/src/main/java/data/model/Identifiable.java b/src/main/java/data/model/Identifiable.java new file mode 100755 index 0000000..19b8dbc --- /dev/null +++ b/src/main/java/data/model/Identifiable.java @@ -0,0 +1,8 @@ +package data.model; + +/** + * Created by iran on 2015-07-07. + */ +public interface Identifiable { + String getId(); +} diff --git a/src/main/java/data/model/Image.java b/src/main/java/data/model/Image.java new file mode 100755 index 0000000..012a3fb --- /dev/null +++ b/src/main/java/data/model/Image.java @@ -0,0 +1,24 @@ +package data.model; + +import org.parceler.Parcel; + +/** + * Created by iran on 2015-07-20. + */ +@Parcel +public class Image { + public Image(){} + public Image(String url){ + this.url = url; + } + + public String url; + public int width; + public int height; + + public Image(String url, int width, int height) { + this.url = url; + this.width = width; + this.height = height; + } +} diff --git a/src/main/java/data/model/ImageTag.java b/src/main/java/data/model/ImageTag.java new file mode 100755 index 0000000..4b05f98 --- /dev/null +++ b/src/main/java/data/model/ImageTag.java @@ -0,0 +1,19 @@ +package data.model; + +import com.google.gson.annotations.SerializedName; +import com.raizlabs.android.dbflow.annotation.Table; + +import data.database.PoinilaDataBase; + +/** + * Created by iran on 2015-11-03. + */ +@Table(database = PoinilaDataBase.class) +public class ImageTag extends Tag{ + //TODO: DBFlow doesn't support inheritance for primary key field; it will be added in 3.0.0 release though + + @SerializedName("images") + public ImageUrls imageUrls; + + public ImageTag(){} +} diff --git a/src/main/java/data/model/ImageUrls.java b/src/main/java/data/model/ImageUrls.java new file mode 100755 index 0000000..921b0d6 --- /dev/null +++ b/src/main/java/data/model/ImageUrls.java @@ -0,0 +1,118 @@ +package data.model; + +import android.text.TextUtils; + +import com.google.gson.annotations.SerializedName; + +import org.parceler.Parcel; + +/** + * Created by iran on 2015-07-20. + */ +@Parcel +public class ImageUrls { + public long id; + + @SerializedName(value = "is_uploaded") public boolean uploaded; + @SerializedName(value = "1000x1000") public data.model.Image x1000square; + @SerializedName(value = "736x") public data.model.Image x736; // for post + public data.model.Image origin; + @SerializedName(value = "160x160") public data.model.Image x160square; + @SerializedName(value = "100x100") public data.model.Image x100square; + @SerializedName(value = "200x100") public data.model.Image interest; + @SerializedName(value = "236x") public data.model.Image x236; + @SerializedName(value = "60x60") public data.model.Image x60square; + @SerializedName(value = "75x75") public data.model.Image x75square; + @SerializedName(value = "40x40") public data.model.Image x40square; + @SerializedName("dominant_color") + public String dominantColor; + + + public data.model.Image properMemberImage(ImageSize imageSize) { + switch (imageSize){ + case AVATAR: + return x75square; + case BIG: + return x160square; + case FULL_SIZE: + return x1000square; + } + return x75square; + } + + public data.model.Image properCollectionImage(ImageSize imageSize) { + switch (imageSize){ + case AVATAR: + return x100square; + case BIG: + return x160square; + } + return x75square; + } + + public data.model.Image properPostImage(ImageSize imageSize) { + switch (imageSize){ + case AVATAR: + return x100square; + case BIG: + return x736; + case MEDIUM: + return x236; + case FULL_SIZE: + return origin; + } + return x75square; + } + + + public boolean isNotEmpty() { + data.model.Image[] images = new data.model.Image[]{x236, x75square, x160square, x100square, x60square, x40square, x736, interest}; + for (data.model.Image image : images){ + if (image != null) + return true; + } + return false; + } + + public static boolean hasValidUrl(ImageUrls imageUrls, ImageType imageType, ImageSize imageSize) { + if (imageUrls == null) return false; + data.model.Image image; + switch (imageType){ + case COLLECTION: + image = imageUrls.properCollectionImage(imageSize); + break; + case POST: + image = imageUrls.properPostImage(imageSize); + break; + case MEMBER: + image = imageUrls.properMemberImage(imageSize); + break; + case INTEREST: + image = imageUrls.interest; + break; + default: + image = null; + break; + } + return !(image == null || TextUtils.isEmpty(image.url)); + } + + public enum ImageType { + @SerializedName("collection") + COLLECTION, + @SerializedName("member") + MEMBER, + @SerializedName("post") + POST, + INTEREST + } + public enum ImageSize { + AVATAR, + /** + * only used for posts in dashboards + */ + MEDIUM, + BIG, + FULL_SIZE, + } +} diff --git a/src/main/java/data/model/InvitationNotif.java b/src/main/java/data/model/InvitationNotif.java new file mode 100755 index 0000000..d340e4b --- /dev/null +++ b/src/main/java/data/model/InvitationNotif.java @@ -0,0 +1,8 @@ +package data.model; + + +/** + * Created by iran on 2015-08-15. + */ +public class InvitationNotif extends FriendshipNotif { +} diff --git a/src/main/java/data/model/Loading.java b/src/main/java/data/model/Loading.java new file mode 100755 index 0000000..67722f6 --- /dev/null +++ b/src/main/java/data/model/Loading.java @@ -0,0 +1,10 @@ +package data.model; + +import com.raizlabs.android.dbflow.structure.BaseModel; + +/** + * Created by iran on 5/10/2016. + */ +public class Loading { + +} diff --git a/src/main/java/data/model/Member.java b/src/main/java/data/model/Member.java new file mode 100755 index 0000000..5fa0d88 --- /dev/null +++ b/src/main/java/data/model/Member.java @@ -0,0 +1,114 @@ +package data.model; + +import com.google.gson.annotations.SerializedName; +import com.raizlabs.android.dbflow.annotation.Column; +import com.raizlabs.android.dbflow.annotation.PrimaryKey; +import com.raizlabs.android.dbflow.annotation.Table; +import com.shaya.poinila.android.util.JavaUtils; + +import org.parceler.Parcel; + +import java.util.List; + +/** + * Created by iran on 2015-06-23. + */ +@Parcel(analyze=Member.class) +@Table(database = data.database.PoinilaDataBase.class) +public class Member extends data.database.PoinilaDataBase.PoinilaDBModel implements data.model.Identifiable { + @Column + @PrimaryKey + public int id; + /*@SerializedName(value = "first_name") public String firstName; + @SerializedName(value = "last_name") public String lastName;*/ + public Gender gender; + @SerializedName("mobile_number") + public String mobileNumber; + public String email; + @SerializedName(value = "images") public data.model.ImageUrls imageUrls; + @SerializedName(value = "full_name") public String fullName; + @SerializedName(value = "unique_name") public String uniqueName; + @SerializedName(value = "description") public String aboutMe; + @SerializedName(value = "type") public data.model.MemberType memberType; + + //TODO: String khali bashe ya entity + public List interests; + @SerializedName(value = "circles") public List circles; // for offline use. + @SerializedName(value = "circle_ids") public List circle_ids; // what comes from server + @SerializedName(value = "frames") public List frames; + + // url website taraf + public String url; + // esme website + @SerializedName(value = "url_name") public String urlName; + //@SerializedName(value = "is_verified") public boolean verified; + + @SerializedName(value = "friend_count") public int friendsCount; + @SerializedName(value = "like_count") public int likesCount; + @SerializedName(value = "follower_count") public int followerCount; + @SerializedName(value = "post_count") public int postsCount; + + @SerializedName(value = "own_collections") public List owningCollections; + @SerializedName(value = "follow_collections") public List followingCollections; + @SerializedName(value = "own_collection_count") public int owningCollectionsCount; + @SerializedName(value = "following_collection_count") public int followingCollectionsCount; + + //@SerializedName(value = "is_friend") public boolean isFriend; + @SerializedName(value = "friendship_status") public data.model.FriendshipStatus friendshipStatus; + // public String siteFaviconUrl; + + // previously was false, but I think its wiser to assume user is anonymous firstly + @SerializedName("is_anonymous") public boolean isAnonymous = true; + + public transient boolean selected = false; + + @SerializedName(value = "email_verified") public boolean isEmailVerified = false; + @SerializedName(value = "mobile_verified") public boolean isMobileVerified = false; + @SerializedName(value = "set_password") public boolean isPassword = true; + + + + + public Member(){} + /** + * Works as cloning class instances. used in changing profile setting + * @param originalProfile + */ + public Member(Member originalProfile) { + fullName = originalProfile.fullName; + email = originalProfile.email; + mobileNumber = originalProfile.mobileNumber; + aboutMe = originalProfile.aboutMe; + //mobileNumber = originalProfile.mobileNumber; + urlName = originalProfile.urlName; + url = originalProfile.url; + //isActive = originalProfile.isActive; + } + + + @Override + public Member getModel() { + return getModelFromJson(Member.class); + } + + @Override + public String getId() { + return String.valueOf(id); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Member member = (Member) o; + + // based on what we need in profile setting + return JavaUtils.equal(this.fullName, member.fullName) && + JavaUtils.equal(this.email, member.email) && + JavaUtils.equal(this.mobileNumber, member.mobileNumber) && + JavaUtils.equal(this.aboutMe, member.aboutMe) && + JavaUtils.equal(this.urlName, member.urlName) && + JavaUtils.equal(this.url, member.url); + } + +} diff --git a/src/main/java/data/model/MemberType.java b/src/main/java/data/model/MemberType.java new file mode 100755 index 0000000..ef7f765 --- /dev/null +++ b/src/main/java/data/model/MemberType.java @@ -0,0 +1,32 @@ +package data.model; + +import com.google.gson.annotations.SerializedName; + +/** + * Created by iranian on 6/25/2015. + */ +public enum MemberType { + @SerializedName(value = "entity") + Entity, + @SerializedName(value = "people") + People + +/* String code; + + + MemberType(String code) { + this.code = code; + } + + public static final MemberType findByCode(String code){ + if(code==null){ + return null; + } + for (MemberType memberType: MemberType.values()){ + if(memberType.code.equals(code)){ + return memberType; + } + } + return null; + }*/ +} diff --git a/src/main/java/data/model/Notification.java b/src/main/java/data/model/Notification.java new file mode 100755 index 0000000..89833df --- /dev/null +++ b/src/main/java/data/model/Notification.java @@ -0,0 +1,69 @@ +package data.model; + +import com.google.gson.annotations.SerializedName; + +import java.util.Date; +import java.util.List; + +/** + * Created by iran on 2015-08-15. + */ +public class Notification { + + @SerializedName(value = "notifiable_action_type") public NotificationType type; + @SerializedName(value = "main_actor") public Participant mainActor; + public boolean seen; + public List participants; + // TODO: chera "last"?. mage update mishe? + @SerializedName(value = "last_creation_time") public Date lastCreationTime; + + public ImageUrls.ImageType getParticipantImageType() { + switch (type){ + case MY_POST_LIKED: + case MY_COLLECTION_FOLLOWED: + case COMMENT_MY_POST: + case MY_POST_REPOSTED: + case COMMENT_AFTER_YOUR_COMMENT: + case FRIENDSHIP_ACCEPTED: + return ImageUrls.ImageType.MEMBER; + + case FRIENDS_FOLLOWED_COLLECTIONS: + case FRIENDS_CREATED_COLLECTIONS: + return ImageUrls.ImageType.COLLECTION; + case FRIENDS_LIKED_POSTS: + return ImageUrls.ImageType.POST; + default: + return ImageUrls.ImageType.MEMBER; + } + + } + // for now its unused + // @SerializedName(value = "notification_view_type") public Date lastCreationTime; + + + + public enum NotificationType{ + /*-----My notifs-------*/ + @SerializedName(value = "other_like_own_post") + MY_POST_LIKED, + @SerializedName(value = "other_follow_own_collection") + MY_COLLECTION_FOLLOWED, + @SerializedName(value = "comment_on_own_post") + COMMENT_MY_POST, + @SerializedName(value = "repost_post") + MY_POST_REPOSTED, + @SerializedName(value = "comment_on_post_that_people_commented_on") + COMMENT_AFTER_YOUR_COMMENT, + @SerializedName(value = "accepted_friendship_invitation") + FRIENDSHIP_ACCEPTED, + + /*-----Other's Notifs-------*/ + @SerializedName(value = "friend_follow_other_collection") + FRIENDS_FOLLOWED_COLLECTIONS, + @SerializedName(value = "friend_create_collection") + FRIENDS_CREATED_COLLECTIONS, + @SerializedName(value = "friend_like_other_post") + FRIENDS_LIKED_POSTS, + + } +} diff --git a/src/main/java/data/model/OnOffSetting.java b/src/main/java/data/model/OnOffSetting.java new file mode 100755 index 0000000..329acbb --- /dev/null +++ b/src/main/java/data/model/OnOffSetting.java @@ -0,0 +1,27 @@ +package data.model; + +import com.google.gson.annotations.SerializedName; + +/** + * Created by iran on 2015-09-07. + */ +public class OnOffSetting { + public static final int OFF = 0; + public static final int ON = 1; + @SerializedName("type_id") + public int typeID; + + public int value; // 0 for off and 1 for on + + public String name; + + public String code; + public transient boolean enabled = true; + + public void off(){ + value = OFF; + } + public void on(){ + value = ON; + } +} diff --git a/src/main/java/data/model/Participant.java b/src/main/java/data/model/Participant.java new file mode 100755 index 0000000..241a028 --- /dev/null +++ b/src/main/java/data/model/Participant.java @@ -0,0 +1,23 @@ +package data.model; + +import com.google.gson.annotations.SerializedName; + +/** + * Created by iran on 2015-08-15. + */ +public class Participant implements data.model.Identifiable { + public int id; + @SerializedName(value = "images") public data.model.ImageUrls imageUrls; + @SerializedName(value = "type") public data.model.ImageUrls.ImageType type; + @SerializedName(value = "unique_name") + public String userName; // for member + @SerializedName(value = "name") + public String collectionName; // for collection + @SerializedName(value = "title") + public String postTitle; // post + + @Override + public String getId() { + return String.valueOf(id); + } +} diff --git a/src/main/java/data/model/PoinilaInvite.java b/src/main/java/data/model/PoinilaInvite.java new file mode 100755 index 0000000..37a9da4 --- /dev/null +++ b/src/main/java/data/model/PoinilaInvite.java @@ -0,0 +1,14 @@ +package data.model; + +import com.google.gson.annotations.SerializedName; + +/** + * Created by iran on 2015-10-03. + */ +public class PoinilaInvite { + @SerializedName("email_used_invitation_count") + public int usedInvites; + + @SerializedName("email_invitation_limit") + public int limit; +} diff --git a/src/main/java/data/model/PoinilaResponse.java b/src/main/java/data/model/PoinilaResponse.java new file mode 100755 index 0000000..820c5aa --- /dev/null +++ b/src/main/java/data/model/PoinilaResponse.java @@ -0,0 +1,13 @@ +package data.model; + +/** + * Created by iran on 2015-07-07. + */ +public class PoinilaResponse { + public String bookmark; + public String message; + public String status; + public int code; + public String Authorization; + public T data; +} diff --git a/src/main/java/data/model/Post.java b/src/main/java/data/model/Post.java new file mode 100755 index 0000000..5ce985f --- /dev/null +++ b/src/main/java/data/model/Post.java @@ -0,0 +1,134 @@ +package data.model; + +import com.google.gson.annotations.SerializedName; +import com.raizlabs.android.dbflow.annotation.Column; +import com.raizlabs.android.dbflow.annotation.ForeignKey; +import com.raizlabs.android.dbflow.annotation.PrimaryKey; +import com.raizlabs.android.dbflow.annotation.Table; + +import org.parceler.Parcel; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * Created by iran on 2015-06-16. + * Completely different with post in presentation module + * This class is only for testing purpose + */ +@Parcel(analyze=Post.class) +@Table(database = data.database.PoinilaDataBase.class) +public class Post extends data.database.PoinilaDataBase.PoinilaDBModel implements Timed, Identifiable { + + @Column + @PrimaryKey + public int id; + + @Column + @SerializedName(value = "creation_time") + public Date creationTime; + + // Visible Field + @SerializedName(value = "like_count") + public int faveCount; + + @SerializedName(value = "repost_count") + public int repostCount; + + @SerializedName(value = "comment_count") + public int commentCount; + + public List comments; + //@SerializedName(value = "last_update_time") public Date lastUpdateTime; + + @SerializedName(value = "is_repost") + public boolean isRepost; + + @SerializedName(value = "liked_by_me") + public boolean favedByMe; + + @SerializedName(value = "title") + public String name; + + @SerializedName(value = "caption") + public String summary; + // mese image bayad joda request zade she + // TODO:chizi ke server mide daghighan chie? processi lazeme in vasat? + //@SerializedName(value = "content_url") public Content contentUrl; + + @SerializedName(value = "content_url") + public String contentUrl; + + // only used in uploading a new post + public String content; + + // tag haye marbut be post + @SerializedName(value = "tags") + public List tags; + // TODO: enum? + + @SerializedName(value = "video_url") + public String videoUrl; + + @SerializedName(value = "type") + public PostType type; + + //TODO: remove after it moved to imagesUrls array + @SerializedName(value = "initial_img") + public String placeholder; + + @SerializedName(value = "images") + public data.model.ImageUrls imagesUrls; + + @SerializedName(value = "original_poster") + public Member originalAuthor; + + @SerializedName(value = "poster") + public Member author; + + @SerializedName(value = "original_collection") + public Collection originalCollection; + + public Collection collection; + /** + * the webpage this post is created from + */ + @SerializedName(value = "url") + public String originalWebpage; + + public PrivacyType privacy; + + public SuggestionReason reason; + + public Post() { + + } + + public Post(String name, String summary, String content, List tags) { + + this.name = name; + this.summary = summary; + this.content = content; + this.tags = new ArrayList<>(tags.size()); + for (String tagString : tags) { + this.tags.add(Tag.invalidIdTag(tagString)); + } + } + + @Override + public long getCreationTime() { + return creationTime.getTime(); + } + + @Override + public String getId() { + return String.valueOf(id); + } + + @Override + public Post getModel() { + return getModelFromJson(Post.class); + } + +} diff --git a/src/main/java/data/model/PostType.java b/src/main/java/data/model/PostType.java new file mode 100755 index 0000000..2e1b716 --- /dev/null +++ b/src/main/java/data/model/PostType.java @@ -0,0 +1,37 @@ +package data.model; + +import com.google.gson.annotations.SerializedName; + +/** + * Created by iranian on 6/25/2015. + */ +public enum PostType { + @SerializedName(value = "img") + IMAGE, + @SerializedName(value = "text") + TEXT, + @SerializedName(value = "video") + VIDEO + + /* String code; + + PostType(String code) { + this.code = code; + } + + public String getCode() { + return code; + } + + public static final PostType findByCode(String code){ + if(code==null){ + return null; + } + for (PostType postType: PostType.values()){ + if(postType.code.equals(code)){ + return postType; + } + } + return null; + }*/ +} diff --git a/src/main/java/data/model/PrivacyType.java b/src/main/java/data/model/PrivacyType.java new file mode 100755 index 0000000..a32c28e --- /dev/null +++ b/src/main/java/data/model/PrivacyType.java @@ -0,0 +1,13 @@ +package data.model; + +import com.google.gson.annotations.SerializedName; + +/** + * Created by iran on 2015-07-06. + */ +public enum PrivacyType { + @SerializedName(value = "public") + PUBLIC, + @SerializedName(value = "private") + PRIVATE +} diff --git a/src/main/java/data/model/SuggestedWebPagePost.java b/src/main/java/data/model/SuggestedWebPagePost.java new file mode 100755 index 0000000..b229f29 --- /dev/null +++ b/src/main/java/data/model/SuggestedWebPagePost.java @@ -0,0 +1,30 @@ +package data.model; + +import com.google.gson.annotations.SerializedName; + +import org.parceler.Parcel; + +import java.util.List; + +/** + * Created by AlirezaF on 10/15/2015. + */ +@Parcel +public class SuggestedWebPagePost { + + /* fields directly filled with converter (GSON)*/ + @SerializedName(value = "title") + public String name; + @SerializedName(value = "caption") + public String summary; + public List tags; // comes from server in string format, what can I do? :( + public List images; + public String content; + + /* fields we must set by hand!*/ + public String imageAddress; + public String siteAddress; + + @SerializedName(value = "video") + public String videoAddress; +} diff --git a/src/main/java/data/model/SuggestionReason.java b/src/main/java/data/model/SuggestionReason.java new file mode 100755 index 0000000..ff5cfe4 --- /dev/null +++ b/src/main/java/data/model/SuggestionReason.java @@ -0,0 +1,15 @@ +package data.model; + +import com.google.gson.annotations.SerializedName; + +/** + * Created by iran on 12/15/2015. + */ +public enum SuggestionReason { + @SerializedName("picked_for_you") + PickedForYou, + @SerializedName("found_in_interest") + FoundInInterest, + @SerializedName("found_in_collection") + Following, +} diff --git a/src/main/java/data/model/SystemPreferences.java b/src/main/java/data/model/SystemPreferences.java new file mode 100755 index 0000000..70dcdfa --- /dev/null +++ b/src/main/java/data/model/SystemPreferences.java @@ -0,0 +1,46 @@ +package data.model; + +import android.net.Uri; + +import com.google.gson.annotations.SerializedName; + +import java.util.List; +import java.util.Set; + +/** + * Created by iran on 1/20/2016. + */ +public class SystemPreferences { + + @SerializedName("rating_market") + public MarketPackages rateDestinationMarket; + + @SerializedName("sms_provider_number") + public List smsProviderNumbers; + + + public enum MarketPackages { + @SerializedName("bazaar") + Bazaar("com.farsitel.bazaar", "bazaar://details?id="), + @SerializedName("myket") + Myket("ir.mservices.market", "myket://comment/#Intent;scheme=comment;package="), + @SerializedName("google_play") + GooglePlay("com.android.vending", "https://play.google.com/store/apps/details?id="); + + public static final String MARKET_ADDRESS_PREFIX = "market://details?id="; + public String packageName; + public String pageAddressPrefix; + + MarketPackages(String packageName, String pageAddressPrefix) { + this.packageName = packageName; + this.pageAddressPrefix = pageAddressPrefix; + } + + public Uri getUri() { + return Uri.parse(packageName); + } + } + + + +} diff --git a/src/main/java/data/model/Tag.java b/src/main/java/data/model/Tag.java new file mode 100755 index 0000000..9a8082a --- /dev/null +++ b/src/main/java/data/model/Tag.java @@ -0,0 +1,50 @@ +package data.model; + +import com.raizlabs.android.dbflow.annotation.Column; +import com.raizlabs.android.dbflow.annotation.PrimaryKey; +import com.raizlabs.android.dbflow.annotation.Table; +import com.raizlabs.android.dbflow.structure.BaseModel; + +import org.parceler.Parcel; +import org.parceler.ParcelConverter; + +/** + * Created by iran on 2015-07-03. + */ +@Parcel(analyze=Tag.class) +@Table(database = data.database.PoinilaDataBase.class) +public class Tag extends BaseModel implements data.model.Identifiable { + + @Column + @PrimaryKey + public int id; + + @Column + public String name; + + public transient boolean selected = false; + + public Tag(){} + + public Tag(int id, String name){ + this.id = id; + this.name = name; + } + + @Override + public String getId() { + return String.valueOf(id); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Tag tag = (Tag) o; + return id == tag.id; + } + + public static Tag invalidIdTag(String tagString) { + return new Tag(-1, tagString); + } +} diff --git a/src/main/java/data/model/Timed.java b/src/main/java/data/model/Timed.java new file mode 100755 index 0000000..fa3c6de --- /dev/null +++ b/src/main/java/data/model/Timed.java @@ -0,0 +1,8 @@ +package data.model; + +/** + * Created by iran on 2015-07-07. + */ +public interface Timed { + long getCreationTime(); +} diff --git a/src/main/java/data/model/Topic.java b/src/main/java/data/model/Topic.java new file mode 100755 index 0000000..b53c9d2 --- /dev/null +++ b/src/main/java/data/model/Topic.java @@ -0,0 +1,9 @@ +package data.model; +/** + * Created by iran on 2015-08-29. + */ +public class Topic extends data.model.Tag { + public Topic(int id, String name){ + super(id, name); + } +} diff --git a/src/main/java/data/model/ViewItem.java b/src/main/java/data/model/ViewItem.java new file mode 100755 index 0000000..19c913b --- /dev/null +++ b/src/main/java/data/model/ViewItem.java @@ -0,0 +1,7 @@ +package data.model; + +/** + * Created by iran on 5/14/2016. + */ +public interface ViewItem { +} diff --git a/src/main/java/data/model/notification/NCollection.java b/src/main/java/data/model/notification/NCollection.java new file mode 100755 index 0000000..297c4f0 --- /dev/null +++ b/src/main/java/data/model/notification/NCollection.java @@ -0,0 +1,9 @@ +package data.model.notification; + +/** + * Created by iran on 6/13/2016. + */ +public class NCollection { + private long id; + private long image; +} diff --git a/src/main/java/manager/DBFacade.java b/src/main/java/manager/DBFacade.java new file mode 100755 index 0000000..b66b853 --- /dev/null +++ b/src/main/java/manager/DBFacade.java @@ -0,0 +1,163 @@ +package manager; + +import android.support.annotation.NonNull; +import android.text.TextUtils; +import android.util.Log; + +import com.raizlabs.android.dbflow.config.FlowConfig; +import com.raizlabs.android.dbflow.config.FlowManager; +import com.raizlabs.android.dbflow.sql.language.Condition; +import com.raizlabs.android.dbflow.sql.language.CursorResult; +import com.raizlabs.android.dbflow.sql.language.Delete; +import com.raizlabs.android.dbflow.sql.language.NameAlias; +import com.raizlabs.android.dbflow.sql.language.SQLite; +import com.raizlabs.android.dbflow.sql.language.Select; +import com.raizlabs.android.dbflow.sql.language.Where; +import com.raizlabs.android.dbflow.sql.queriable.ModelQueriable; +import com.raizlabs.android.dbflow.structure.Model; +import com.raizlabs.android.dbflow.structure.database.transaction.FastStoreModelTransaction; +import com.raizlabs.android.dbflow.structure.database.transaction.ProcessModelTransaction; +import com.raizlabs.android.dbflow.structure.database.transaction.QueryTransaction; +import com.raizlabs.android.dbflow.structure.database.transaction.Transaction; +import com.shaya.poinila.android.presentation.PoinilaApplication; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.Logger; + +import java.util.ArrayList; +import java.util.List; + + +import data.database.PoinilaDataBase; +import data.event.DashboardEvent; +import data.model.Circle; +import data.model.Circle$Table; +import data.model.Collection; +import data.model.DefaultType; +import data.model.Frame; +import data.model.Member; +import data.model.Member$Table; +import data.model.Post; +import data.model.Post$Table; + +import static com.shaya.poinila.android.util.ContextHolder.getContext; + +/** + * Created by AlirezaF on 7/8/2015. + */ +public class DBFacade { + + public static Member getCachedMyInfo() { + Member me = new Select().from(Member.class).where(Condition.column(Member$Table.id.getNameAlias()). + is(DataRepository.getInstance().getMyId())).querySingle(); + + //Member me = Member.getTestItem(); + if (me != null && !TextUtils.isEmpty(me.jsonContent)) { + me = me.getModel(); + return me; + } + return null; + } + + public static List getMyCircles() { + // TODO: in chie akhe? gand zadi ba tarrahi!! + /* for (int i = 0; i < circles.size(); i++){ + circles.set(i, circles.get(i).getModel()); + }*/ + //BusProvider.getBus().post(new CirclesReceivedEvent(circles)); + return new Select().from(Circle.class).where( + Condition.column(Circle$Table.defaultType.getNameAlias()).isNot(DefaultType.DEFAULT)).queryList(); + } + + public static Circle getDefaultCircle() { + return new Select().from(Circle.class).where + (Condition.column(Circle$Table.defaultType.getNameAlias()).eq(DefaultType.DEFAULT)).querySingle(); + // Circle$Table.PRIVACY, PrivacyType.PUBLIC.name() + } + + + public static List getMyFrames() { + return new Select().from(Frame.class).queryList(); + /* for (int i = 0; i < frames.size(); i++){ + frames.set(i, frames.get(i).getModel()); + }*/ + //BusProvider.getBus().post(new MyFrameReceivedEvent(frames)); + } + + public static void getSuggestions(int cachedItems) { + + if(FlowManager.isDatabaseIntegrityOk(PoinilaDataBase.NAME)){ + FlowManager.getDatabaseForTable(Post.class) + .beginTransactionAsync(new QueryTransaction.Builder<>( + SQLite.select(Post$Table.jsonContent) + .from(Post.class) + .where() + .orderBy(Post$Table.creationTime, false) + .limit((int) ConstantsUtils.SUGGESTION_PER_REQUEST) + .offset(cachedItems)) + .queryResult(new QueryTransaction.QueryResultCallback() { + @Override + public void onQueryResult(QueryTransaction transaction, @NonNull CursorResult tResult) { + + List posts = tResult.toList(); + List data = new ArrayList<>(); + + int length = posts.size(); + for(int i=0 ; i< length ; i++) { + data.add(posts.get(i).getModel()); + } + + BusProvider.getBus().post(new DashboardEvent(data, true)); + + } + }).build()).build().execute(); + } + } + + + public static T loadModel(String modelID, Class clazz) { + return new Select().from(clazz).where(Condition.column(NameAlias.builder("id").build()).eq(modelID)).querySingle(); + } + + public static void saveModels(List models, Class mClass) { +// ProcessModelInfo pmi = ProcessModelInfo.withModels(models); +// TransactionManager.getInstance().addTransaction(new SaveModelTransaction<>(pmi)); + + int count = models.size(); + for (int i = 0; i < count; i++) { + models.get(i).save(); + } + + + } + + public static void saveModel(T model) { + model.save(); + } + + public static void updateModel(T model) { + model.update(); + } + + + public static List getMyCollections() { + return new Select().from(Collection.class).queryList(); + /*List collections = new Select().from(Collection.class).queryList(); + for (int i = 0; i < collections.size(); i++) { + collections.set(i, collections.get(i).getModel()); + } + return collections;*/ + } + + /** + * @param tables + * @see sql statements + */ + @SafeVarargs + public static void clearData(Class... tables) { + //FlowManager.getDatabase(PoinilaDataBase.NAME).reset(getContext()); + // TODO: it's a temporary workaround. the above line doesn't work currently. + Delete.tables(tables); + FlowManager.init(new FlowConfig.Builder(PoinilaApplication.getAppContext()).build()); + } +} diff --git a/src/main/java/manager/DataRepository.java b/src/main/java/manager/DataRepository.java new file mode 100755 index 0000000..1b5ddd0 --- /dev/null +++ b/src/main/java/manager/DataRepository.java @@ -0,0 +1,403 @@ +package manager; + +import android.content.Context; +import android.content.Intent; +import android.support.annotation.Nullable; +import android.support.v4.content.LocalBroadcastManager; +import android.util.Log; + +import com.raizlabs.android.dbflow.annotation.NotNull; +import com.raizlabs.android.dbflow.config.DatabaseDefinition; +import com.raizlabs.android.dbflow.config.FlowManager; +import com.raizlabs.android.dbflow.structure.database.transaction.ProcessModelTransaction; +import com.raizlabs.android.dbflow.structure.database.transaction.Transaction; +import com.shaya.poinila.android.util.BusProvider; +import com.shaya.poinila.android.util.ConnectionUitls; +import com.shaya.poinila.android.util.ConstantsUtils; +import com.shaya.poinila.android.util.ContextHolder; +import com.shaya.poinila.android.util.PoinilaPreferences; +import com.shaya.poinila.android.utils.PonilaAccountManager; +import com.squareup.otto.Subscribe; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import data.PoinilaNetService; +import data.database.PoinilaDataBase; +import data.event.BaseEvent; +import data.event.ContentReceivedEvent; +import data.event.ModelCreatedEvent; +import data.event.ModelDeletedEvent; +import data.event.ModelUpdatedEvent; +import data.event.MyInfoReceivedEvent; +import data.event.PostReceivedEvent; +import data.event.RemainedInvitesEvent; +import data.event.SystemPreferencesReceivedEvent; +import data.model.Circle; +import data.model.Collection; +import data.model.Content; +import data.model.Frame; +import data.model.Identifiable; +import data.model.Member; +import data.model.Post; +import data.model.SystemPreferences; + +/** + * Created by iran on 2015-07-03. + */ +public class DataRepository { + private static DataRepository instance; + private boolean shouldShowRatingToUser; + private int remainedInvites = 0; + private SystemPreferences systemPreferences; + private boolean isUserAnonymous = true; + + public DataRepository() { + BusProvider.getBus().register(this); + } + + public static DataRepository getInstance() { + if(instance == null ) + instance = new DataRepository(); + return instance; + } + + /* public void destroy(){ + BusProvider.getBus().unregister(this); + }*/ + + public void getSuggestions(boolean fromServer, boolean readCache, String bookmark, int cachedItems) { + if (readCache) + manager.DBFacade.getSuggestions(cachedItems); + if (fromServer){ + PoinilaNetService.getSuggestions(bookmark); + } + } + + public void saveSuggestions(List posts) { +// ProcessModelInfo pmi = ProcessModelInfo.withModels(posts); +// TransactionManager.getInstance().addTransaction(new SaveModelTransaction<>(pmi)); + DatabaseDefinition database = FlowManager.getDatabase(PoinilaDataBase.class); + +// for(Post post : posts) + + + ProcessModelTransaction processModelTransaction = + new ProcessModelTransaction.Builder<>(new ProcessModelTransaction.ProcessModel() { + @Override + public void processModel(Post model) { + // call some operation on model here + model.save(); +// model.insert(); // or +// model.delete(); // or + } + }).processListener(new ProcessModelTransaction.OnModelProcessListener() { + @Override + public void onModelProcessed(long current, long total, Post modifiedModel) { + + } + }).addAll(posts).build(); + Transaction transaction = database.beginTransactionAsync(processModelTransaction).build(); + transaction.execute(); + + } + + public void getPost(String postID, manager.RequestSource target, int requestId) { + if (target != manager.RequestSource.FORCE_ONLINE){ + Post post = DBFacade.loadModel(postID, Post.class); + if (post != null) + BusProvider.getBus().post(new PostReceivedEvent(post.getModel(), requestId)); + } + if (target != RequestSource.FORCE_OFFLINE && ConnectionUitls.isNetworkOnline()){ + PoinilaNetService.getPost(postID, requestId); + } + } + + public void getProfile(String memberID) { + //Member member = DBFacade.getItem(profileID, Member.class); + //if (member == null) { + PoinilaNetService.getMemberProfile(memberID); + // } + } + + public void getMyProfile(){ + PoinilaNetService.getMemberProfile(getMyId()); + + } + + public void getMyFollowedCollections(String frameID, String bookmark) { + PoinilaNetService.getFollowedCollections(getMyId(), frameID, bookmark, BaseEvent.ReceiverName.MyFollowedCollections); + } + + public void getTopics(){ + PoinilaNetService.getTopics(); + } + + // TODO: + public String getMyId() { + return PoinilaPreferences.getMyId(); + } + + public void getPeopleFollowingCollections(String memberId, String bookmark) { + PoinilaNetService.getFollowedCollections(memberId, null, bookmark, BaseEvent.ReceiverName.CollectionListFragment); + } + + public void getPostsWithQuery(List queries, String bookmark) { + PoinilaNetService.searchPostWithQuery(queries, bookmark); + } + + public void getCollectionsWithQuery(List queries, String bookmark) { + PoinilaNetService.searchCollectionsWithQuery(queries, bookmark); + } + + public void getMembersWithQuery(List queries, String bookmark) { + PoinilaNetService.searchMembersWithQuery(queries, bookmark); + } + + public List getFrames() { + // TODO: fetch from db if user is null, otherwise read from user; + /* if (user != null) + return user.frames;*/ + return new ArrayList<>(Arrays.asList(new Frame(1, "sport"), new Frame(2, "cinema"))); + } + + // TODO + public void getCollection(@NotNull String collectionIdOrName, @Nullable String userName, RequestSource target) { + /*if (target != RequestTarget.FORCE_ONLINE){ + Collection collection = DBFacade.loadModel(collectionID, Collection.class); + if (collection != null) + BusProvider.getBus().post(new CollectionReceivedEvent(collection.getModel())); + }*/ + if (target != RequestSource.FORCE_OFFLINE && ConnectionUitls.isNetworkOnline()){ + PoinilaNetService.getCollection(collectionIdOrName, userName); + } + } + + public void getPostContent(String contentUrl, int postID) { + Content content = DBFacade.loadModel(contentUrl, Content.class); + if (content != null) + BusProvider.getBus().post(new ContentReceivedEvent(content.text, postID)); + else + PoinilaNetService.getPostContent(contentUrl, postID); + } + + public static void getCollectionPosts(String collectionIdOrName, @Nullable String userName, + String bookmark, BaseEvent.ReceiverName receiverName) { + PoinilaNetService.getCollectionPosts(collectionIdOrName, userName, bookmark, receiverName, false); + } + + public static void getCollectionPostsImages(String collectionIdOrName, @Nullable String userName, + String bookmark, BaseEvent.ReceiverName receiverName) { + PoinilaNetService.getCollectionPosts(collectionIdOrName, userName, bookmark, receiverName, true); + } + + public void getMemberFriends(String memberID, String bookmark) { + PoinilaNetService.getMemberFriends(memberID, bookmark); + } + + public void getMyInfo(boolean fromServer, MyInfoReceivedEvent.MY_INFO_TYPE type){//boolean networkOnline) { + if (fromServer) + PoinilaNetService.getMyInfo(type); + else{ + BusProvider.getBus().post(new MyInfoReceivedEvent(DBFacade.getCachedMyInfo(), true, type)); + } + } + + + Object tempModel; + String tempModelID; + long serverTimeDifference = -1; + + public void putTempModel(Object model) { + tempModel = model; + if (model instanceof Identifiable) + tempModelID = ((Identifiable) model).getId(); + } + + public T getTempModel(Class clazz){ + if (clazz.isInstance(tempModel)) { //tempModel.getClass().getSimpleName().equals(clazz.getSimpleName()) + if (tempModel instanceof Identifiable && !tempModelID.equals(((Identifiable) tempModel).getId())) + return null; + return clazz.cast(tempModel); //clazz.cast(tempModel); + }return null; + } + + public void getMemberCollections(String memberID, String bookmark) { + PoinilaNetService.getMemberCollections(memberID, bookmark); + } + + public void getPostComments(String postID, String bookmark) { + PoinilaNetService.getPostComments(postID, bookmark); + } + + public void putServerTimeDifference(long timeDifference) { + this.serverTimeDifference = timeDifference; + PoinilaPreferences.putServerTime(serverTimeDifference); + } + + public long getServerTimeDifference(){ + return (serverTimeDifference == -1) ? PoinilaPreferences.getServerTimeDifference() : serverTimeDifference; + } + + public static void deleteCache(Context context) { + try { + File dir = context.getCacheDir(); + if (dir != null && dir.isDirectory()) { + deleteDir(dir); + } + } catch (Exception e) {} + } + + public static boolean deleteDir(File dir) { + if (dir != null && dir.isDirectory()) { + String[] children = dir.list(); + for (String aChildren : children) { + boolean success = deleteDir(new File(dir, aChildren)); + if (!success) { + return false; + } + } + } + return dir != null && dir.delete(); + } + + public static void clearDataOnLogout(){ + PonilaAccountManager.getInstance().removePonilaAccount(); + PoinilaPreferences.clearData(); + DBFacade.clearData(Member.class, Circle.class, Frame.class, Collection.class, Post.class); + deleteCache(ContextHolder.getContext()); + } + + @Subscribe public void keepSystemPreferences(SystemPreferencesReceivedEvent event){ + systemPreferences = event.systemPreferences; + } + + @Subscribe + public void saveModel(ModelCreatedEvent event){ + event.model.save(); + } + + @Subscribe + public void updateModel(ModelUpdatedEvent event){ + event.model.update(); + } + + @Subscribe + public void deleteModel(ModelDeletedEvent event){ + event.model.delete(); + } + + @Subscribe + public void setReminedInvites(RemainedInvitesEvent event){ + if (event.remained == -1) // Decrementing after using on invite. not wanted to define new method! :) + this.remainedInvites--; + else + this.remainedInvites = event.remained; + } + + public int getRemainedInvites(){ + return remainedInvites; + } + + public boolean isMe(int id) { + return String.valueOf(id).equals(instance.getMyId()); + } + + public static boolean isMyCollection(Collection collection) { + return collection.owner.getId().equals(getInstance().getMyId()); + //return DBFacade.getMyCollections().contains(collection); + } + + public static void calculateIsTimeToAskAboutRating() { + if (!userNeverWantsToRate() && enoughDays() && enoughOpenCount()){ + getInstance().shouldShowRatingToUser = true; + } + } + + private static boolean userNeverWantsToRate() { + return PoinilaPreferences.getAppOpenCountThreshold() == Integer.MAX_VALUE || + PoinilaPreferences.getRatingDaysThreshold() == Integer.MAX_VALUE; + } + + private static boolean enoughOpenCount() { + return PoinilaPreferences.getOpenApplicationCount() >= PoinilaPreferences.getAppOpenCountThreshold(); + } + + private static boolean enoughDays() { + return TimeUnit.MILLISECONDS.toDays( + Calendar.getInstance().getTimeInMillis() - PoinilaPreferences.getFirstLoginDateTime()) >= + PoinilaPreferences.getRatingDaysThreshold(); + } + + public static void updateAskRatingThreshold(boolean neverAskAgain){ + PoinilaPreferences.increaseRatingDaysThreshold(neverAskAgain); + PoinilaPreferences.increaseAppOpenCountThreshold(neverAskAgain); + getInstance().shouldShowRatingToUser = false; + } + + public static boolean shouldAskForRating(){ + return !isUserAnonymous() && + getInstance().shouldShowRatingToUser && + DataRepository.getDestinationMarket() != null; + + // for testing +// return true; + } + + public static SystemPreferences.MarketPackages getDestinationMarket() { + return instance.systemPreferences != null ? + instance.systemPreferences.rateDestinationMarket : null; + } + + public static void setSystemPreferences(SystemPreferences systemPreferences){ + instance.systemPreferences = systemPreferences; + } + + public static List getSMSProviderNumbers() { + List numbers = instance.systemPreferences != null ? + instance.systemPreferences.smsProviderNumbers : null; + return numbers != null ? numbers : new ArrayList(); + } + + public static void setUserAsAnonymous(boolean anonymous) { + //getInstance().isUserAnonymous = anonymous; + PoinilaPreferences.putUserAnonymity(anonymous); + } + + public static boolean isUserAnonymous() { + //return getInstance().isUserAnonymous; + return PoinilaPreferences.isUserAnonymous(); + } + + public static void syncWithMyInfoResponse(MyInfoReceivedEvent event) { + if (event.me != null) { + if (!event.fromCache) { + DataRepository.setUserAsAnonymous(event.me.isAnonymous); + if (DataRepository.isUserAnonymous()) + return; + + DBFacade.clearData(Circle.class, Frame.class, Collection.class, Member.class); + DBFacade.saveModels(event.me.circles, Circle.class); + DBFacade.saveModels(event.me.frames, Frame.class); + DBFacade.saveModels(event.me.owningCollections, Collection.class); + DBFacade.saveModel(event.me); + PoinilaPreferences.putMyId(event.me.getId()); + } + } + } + + public static void logout() { + if (!isUserAnonymous()) + PoinilaNetService.logout(); + DataRepository.setUserAsAnonymous(true); + clearDataOnLogout(); + } + + public static void logoutEvent() { + LocalBroadcastManager.getInstance(ContextHolder.getContext()).sendBroadcast(new Intent(ConstantsUtils.INTENT_FILTER_JWT)); + } +} \ No newline at end of file diff --git a/src/main/java/manager/RequestSource.java b/src/main/java/manager/RequestSource.java new file mode 100755 index 0000000..77466e8 --- /dev/null +++ b/src/main/java/manager/RequestSource.java @@ -0,0 +1,10 @@ +package manager; + +/** + * Created by iran on 2015-10-20. + */ +public enum RequestSource { + FORCE_OFFLINE, + FORCE_ONLINE, + ANY +} diff --git a/src/main/java/manager/dowload/NativeDLManager.java b/src/main/java/manager/dowload/NativeDLManager.java new file mode 100755 index 0000000..31a83f9 --- /dev/null +++ b/src/main/java/manager/dowload/NativeDLManager.java @@ -0,0 +1,61 @@ +//package manager.dowload; +// +//import com.shaya.dlm.core.DownloadManagerPro; +//import com.shaya.dlm.report.listener.DownloadManagerListener; +//import com.shaya.poinila.android.presentation.PoinilaApplication; +//import com.shaya.poinila.android.presentation.R; +//import static com.shaya.poinila.android.util.ResourceUtils.*; +// +///** +// * Created by hossein on 8/30/16. +// */ +//public class NativeDLManager implements DownloadManagerListener { +// +// // TODO: For read more go to https://github.com/majidgolshadi/Android-Download-Manager-Pro +// DownloadManagerPro dLManager; +// +// private NativeDLManager(){ +// dLManager = new DownloadManagerPro(PoinilaApplication.getAppContext()); +// dLManager.init(getString(R.string.app_name), 10, this); +// } +// +// @Override +// public void OnDownloadStarted(long taskId) { +// +// } +// +// @Override +// public void OnDownloadPaused(long taskId) { +// +// } +// +// @Override +// public void onDownloadProcess(long taskId, double percent, long downloadedLength) { +// +// } +// +// @Override +// public void OnDownloadFinished(long taskId) { +// +// } +// +// @Override +// public void OnDownloadRebuildStart(long taskId) { +// +// } +// +// @Override +// public void OnDownloadRebuildFinished(long taskId) { +// +// } +// +// @Override +// public void OnDownloadCompleted(long taskId) { +// +// } +// +// @Override +// public void connectionLost(long taskId) { +// +// } +//} diff --git a/src/main/java/manager/dowload/NotificationDLManager.java b/src/main/java/manager/dowload/NotificationDLManager.java new file mode 100755 index 0000000..e42ea2d --- /dev/null +++ b/src/main/java/manager/dowload/NotificationDLManager.java @@ -0,0 +1,81 @@ +package manager.dowload; + +import android.app.DownloadManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.IntentFilter; +import android.net.Uri; +import android.os.Environment; +import android.text.TextUtils; +import android.webkit.MimeTypeMap; + +import com.shaya.poinila.android.presentation.PoinilaApplication; +import com.shaya.poinila.android.presentation.R; +import com.shaya.poinila.android.util.Logger; + +import java.io.File; + +/** + * Created by hossein on 8/30/16. + * Read More : http://101apps.co.za/articles/using-the-downloadmanager-to-manage-your-downloads.html + */ +public class NotificationDLManager { + + private static NotificationDLManager instance; + + DownloadManager downloadManager; + private long myDownloadRefrence; + private BroadcastReceiver receiverDownloadComplete; + private BroadcastReceiver receiverNotificationClicked; + IntentFilter filter; + + private NotificationDLManager(){ + downloadManager = (DownloadManager) PoinilaApplication.getAppContext().getSystemService(Context.DOWNLOAD_SERVICE); +// filter = new IntentFilter(DownloadManager.ACTION_NOTIFICATION_CLICKED); +// receiverNotificationClicked = new BroadcastReceiver() { +// @Override +// public void onReceive(Context context, Intent intent) { +// String extraId = DownloadManager.EXTRA_NOTIFICATION_CLICK_DOWNLOAD_IDS; +// +// long[] refrences = intent.getLongArrayExtra(extraId); +// +// for(long refrence : refrences){ +// if(refrence == myDownloadRefrence){ +// +// } +// } +// +// PoinilaApplication.getAppContext().registerReceiver(receiverNotificationClicked, filter); +// } +// }; + } + + public static NotificationDLManager getInstance(){ + if(instance == null) + instance = new NotificationDLManager(); + return instance; + } + + public void download(String url, String name, String description){ + Uri uri = Uri.parse(url); + + DownloadManager.Request request = new DownloadManager.Request(uri); + + request + .setTitle(name) + .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) + .setDescription(description) + .setVisibleInDownloadsUi(true) +// .setDestinationInExternalFilesDir() this method is for save in package name directory + .setDestinationInExternalPublicDir( + Environment.DIRECTORY_DOWNLOADS, new File(url).getName()); + + try{ + downloadManager.enqueue(request); + }catch (Exception e){ + Logger.toastError(R.string.download_error); + } + + + } +} diff --git a/src/main/res/anim/fade_in.xml b/src/main/res/anim/fade_in.xml new file mode 100755 index 0000000..7bf9184 --- /dev/null +++ b/src/main/res/anim/fade_in.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/src/main/res/anim/fade_out.xml b/src/main/res/anim/fade_out.xml new file mode 100755 index 0000000..f7e137b --- /dev/null +++ b/src/main/res/anim/fade_out.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/src/main/res/color/black_disabled_grey.xml b/src/main/res/color/black_disabled_grey.xml new file mode 100755 index 0000000..8ad97fb --- /dev/null +++ b/src/main/res/color/black_disabled_grey.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/main/res/color/collection_follow.xml b/src/main/res/color/collection_follow.xml new file mode 100755 index 0000000..c6c1f48 --- /dev/null +++ b/src/main/res/color/collection_follow.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/main/res/color/input_text_color_state_list.xml b/src/main/res/color/input_text_color_state_list.xml new file mode 100755 index 0000000..b45ef36 --- /dev/null +++ b/src/main/res/color/input_text_color_state_list.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/main/res/color/option_text_color.xml b/src/main/res/color/option_text_color.xml new file mode 100755 index 0000000..38e7ad7 --- /dev/null +++ b/src/main/res/color/option_text_color.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/main/res/drawable-hdpi/action_follow_2_dark.png b/src/main/res/drawable-hdpi/action_follow_2_dark.png new file mode 100755 index 0000000..9116bb6 Binary files /dev/null and b/src/main/res/drawable-hdpi/action_follow_2_dark.png differ diff --git a/src/main/res/drawable-hdpi/action_follow_selected.png b/src/main/res/drawable-hdpi/action_follow_selected.png new file mode 100755 index 0000000..ddcb034 Binary files /dev/null and b/src/main/res/drawable-hdpi/action_follow_selected.png differ diff --git a/src/main/res/drawable-hdpi/action_help_default.png b/src/main/res/drawable-hdpi/action_help_default.png new file mode 100755 index 0000000..47eec1b Binary files /dev/null and b/src/main/res/drawable-hdpi/action_help_default.png differ diff --git a/src/main/res/drawable-hdpi/action_help_pressed.png b/src/main/res/drawable-hdpi/action_help_pressed.png new file mode 100755 index 0000000..1a93cfa Binary files /dev/null and b/src/main/res/drawable-hdpi/action_help_pressed.png differ diff --git a/src/main/res/drawable-hdpi/action_next.png b/src/main/res/drawable-hdpi/action_next.png new file mode 100755 index 0000000..5f11894 Binary files /dev/null and b/src/main/res/drawable-hdpi/action_next.png differ diff --git a/src/main/res/drawable-hdpi/action_report_orange.png b/src/main/res/drawable-hdpi/action_report_orange.png new file mode 100755 index 0000000..38e4b81 Binary files /dev/null and b/src/main/res/drawable-hdpi/action_report_orange.png differ diff --git a/src/main/res/drawable-hdpi/action_send_comment_orange.png b/src/main/res/drawable-hdpi/action_send_comment_orange.png new file mode 100755 index 0000000..ee6854d Binary files /dev/null and b/src/main/res/drawable-hdpi/action_send_comment_orange.png differ diff --git a/src/main/res/drawable-hdpi/action_send_comment_white.png b/src/main/res/drawable-hdpi/action_send_comment_white.png new file mode 100755 index 0000000..5b0a61c Binary files /dev/null and b/src/main/res/drawable-hdpi/action_send_comment_white.png differ diff --git a/src/main/res/drawable-hdpi/action_setting_new_default.png b/src/main/res/drawable-hdpi/action_setting_new_default.png new file mode 100755 index 0000000..a4ba850 Binary files /dev/null and b/src/main/res/drawable-hdpi/action_setting_new_default.png differ diff --git a/src/main/res/drawable-hdpi/action_setting_new_pressed.png b/src/main/res/drawable-hdpi/action_setting_new_pressed.png new file mode 100755 index 0000000..bd78767 Binary files /dev/null and b/src/main/res/drawable-hdpi/action_setting_new_pressed.png differ diff --git a/src/main/res/drawable-hdpi/add_collection_white.png b/src/main/res/drawable-hdpi/add_collection_white.png new file mode 100755 index 0000000..5624819 Binary files /dev/null and b/src/main/res/drawable-hdpi/add_collection_white.png differ diff --git a/src/main/res/drawable-hdpi/add_post_white.png b/src/main/res/drawable-hdpi/add_post_white.png new file mode 100755 index 0000000..eb9cd2f Binary files /dev/null and b/src/main/res/drawable-hdpi/add_post_white.png differ diff --git a/src/main/res/drawable-hdpi/add_white_48dp.png b/src/main/res/drawable-hdpi/add_white_48dp.png new file mode 100755 index 0000000..3a15191 Binary files /dev/null and b/src/main/res/drawable-hdpi/add_white_48dp.png differ diff --git a/src/main/res/drawable-hdpi/arrow_down_white_24dp.png b/src/main/res/drawable-hdpi/arrow_down_white_24dp.png new file mode 100755 index 0000000..74cffcf Binary files /dev/null and b/src/main/res/drawable-hdpi/arrow_down_white_24dp.png differ diff --git a/src/main/res/drawable-hdpi/arrow_left_48dp.png b/src/main/res/drawable-hdpi/arrow_left_48dp.png new file mode 100755 index 0000000..724e29a Binary files /dev/null and b/src/main/res/drawable-hdpi/arrow_left_48dp.png differ diff --git a/src/main/res/drawable-hdpi/arrow_right_white_48dp.png b/src/main/res/drawable-hdpi/arrow_right_white_48dp.png new file mode 100755 index 0000000..0127cd1 Binary files /dev/null and b/src/main/res/drawable-hdpi/arrow_right_white_48dp.png differ diff --git a/src/main/res/drawable-hdpi/arrow_up_white_24dp.png b/src/main/res/drawable-hdpi/arrow_up_white_24dp.png new file mode 100755 index 0000000..a0861f5 Binary files /dev/null and b/src/main/res/drawable-hdpi/arrow_up_white_24dp.png differ diff --git a/src/main/res/drawable-hdpi/browser.png b/src/main/res/drawable-hdpi/browser.png new file mode 100755 index 0000000..289df10 Binary files /dev/null and b/src/main/res/drawable-hdpi/browser.png differ diff --git a/src/main/res/drawable-hdpi/btn_google_signin_dark_normal.9.png b/src/main/res/drawable-hdpi/btn_google_signin_dark_normal.9.png new file mode 100755 index 0000000..4a5ddfb Binary files /dev/null and b/src/main/res/drawable-hdpi/btn_google_signin_dark_normal.9.png differ diff --git a/src/main/res/drawable-hdpi/btn_google_signin_dark_pressed.9.png b/src/main/res/drawable-hdpi/btn_google_signin_dark_pressed.9.png new file mode 100755 index 0000000..82d1642 Binary files /dev/null and b/src/main/res/drawable-hdpi/btn_google_signin_dark_pressed.9.png differ diff --git a/src/main/res/drawable-hdpi/button_library_normal.png b/src/main/res/drawable-hdpi/button_library_normal.png new file mode 100755 index 0000000..ba3d4cc Binary files /dev/null and b/src/main/res/drawable-hdpi/button_library_normal.png differ diff --git a/src/main/res/drawable-hdpi/button_library_pressed.png b/src/main/res/drawable-hdpi/button_library_pressed.png new file mode 100755 index 0000000..c63a6f2 Binary files /dev/null and b/src/main/res/drawable-hdpi/button_library_pressed.png differ diff --git a/src/main/res/drawable-hdpi/clear.png b/src/main/res/drawable-hdpi/clear.png new file mode 100755 index 0000000..1b79d0b Binary files /dev/null and b/src/main/res/drawable-hdpi/clear.png differ diff --git a/src/main/res/drawable-hdpi/close.png b/src/main/res/drawable-hdpi/close.png new file mode 100755 index 0000000..d2a1ca1 Binary files /dev/null and b/src/main/res/drawable-hdpi/close.png differ diff --git a/src/main/res/drawable-hdpi/collection_no_image.png b/src/main/res/drawable-hdpi/collection_no_image.png new file mode 100755 index 0000000..f8243fe Binary files /dev/null and b/src/main/res/drawable-hdpi/collection_no_image.png differ diff --git a/src/main/res/drawable-hdpi/comment_normal.png b/src/main/res/drawable-hdpi/comment_normal.png new file mode 100755 index 0000000..e668bcf Binary files /dev/null and b/src/main/res/drawable-hdpi/comment_normal.png differ diff --git a/src/main/res/drawable-hdpi/comment_pressed.png b/src/main/res/drawable-hdpi/comment_pressed.png new file mode 100755 index 0000000..abb02e2 Binary files /dev/null and b/src/main/res/drawable-hdpi/comment_pressed.png differ diff --git a/src/main/res/drawable-hdpi/crop_24dp.png b/src/main/res/drawable-hdpi/crop_24dp.png new file mode 100755 index 0000000..8546bdc Binary files /dev/null and b/src/main/res/drawable-hdpi/crop_24dp.png differ diff --git a/src/main/res/drawable-hdpi/done_24dp_black.png b/src/main/res/drawable-hdpi/done_24dp_black.png new file mode 100755 index 0000000..4c847bf Binary files /dev/null and b/src/main/res/drawable-hdpi/done_24dp_black.png differ diff --git a/src/main/res/drawable-hdpi/done_flamingo_24dp.png b/src/main/res/drawable-hdpi/done_flamingo_24dp.png new file mode 100755 index 0000000..e89ba95 Binary files /dev/null and b/src/main/res/drawable-hdpi/done_flamingo_24dp.png differ diff --git a/src/main/res/drawable-hdpi/done_white_48dp.png b/src/main/res/drawable-hdpi/done_white_48dp.png new file mode 100755 index 0000000..699d0b2 Binary files /dev/null and b/src/main/res/drawable-hdpi/done_white_48dp.png differ diff --git a/src/main/res/drawable-hdpi/email_48dp.png b/src/main/res/drawable-hdpi/email_48dp.png new file mode 100755 index 0000000..9371641 Binary files /dev/null and b/src/main/res/drawable-hdpi/email_48dp.png differ diff --git a/src/main/res/drawable-hdpi/go_36dp.png b/src/main/res/drawable-hdpi/go_36dp.png new file mode 100755 index 0000000..a9e83ca Binary files /dev/null and b/src/main/res/drawable-hdpi/go_36dp.png differ diff --git a/src/main/res/drawable-hdpi/icon_label_red2.png b/src/main/res/drawable-hdpi/icon_label_red2.png new file mode 100755 index 0000000..aca721c Binary files /dev/null and b/src/main/res/drawable-hdpi/icon_label_red2.png differ diff --git a/src/main/res/drawable-hdpi/icon_label_selected2.png b/src/main/res/drawable-hdpi/icon_label_selected2.png new file mode 100755 index 0000000..5c68d78 Binary files /dev/null and b/src/main/res/drawable-hdpi/icon_label_selected2.png differ diff --git a/src/main/res/drawable-hdpi/icon_label_white2.png b/src/main/res/drawable-hdpi/icon_label_white2.png new file mode 100755 index 0000000..9183326 Binary files /dev/null and b/src/main/res/drawable-hdpi/icon_label_white2.png differ diff --git a/src/main/res/drawable-hdpi/invisible_black_24dp.png b/src/main/res/drawable-hdpi/invisible_black_24dp.png new file mode 100755 index 0000000..0f6c51e Binary files /dev/null and b/src/main/res/drawable-hdpi/invisible_black_24dp.png differ diff --git a/src/main/res/drawable-hdpi/like_normal.png b/src/main/res/drawable-hdpi/like_normal.png new file mode 100755 index 0000000..60b75a0 Binary files /dev/null and b/src/main/res/drawable-hdpi/like_normal.png differ diff --git a/src/main/res/drawable-hdpi/like_pressed.png b/src/main/res/drawable-hdpi/like_pressed.png new file mode 100755 index 0000000..b326c6b Binary files /dev/null and b/src/main/res/drawable-hdpi/like_pressed.png differ diff --git a/src/main/res/drawable-hdpi/like_selected.png b/src/main/res/drawable-hdpi/like_selected.png new file mode 100755 index 0000000..1c9e0e1 Binary files /dev/null and b/src/main/res/drawable-hdpi/like_selected.png differ diff --git a/src/main/res/drawable-hdpi/menu_item_download.png b/src/main/res/drawable-hdpi/menu_item_download.png new file mode 100755 index 0000000..8cc4619 Binary files /dev/null and b/src/main/res/drawable-hdpi/menu_item_download.png differ diff --git a/src/main/res/drawable-hdpi/pending_friendship_request.png b/src/main/res/drawable-hdpi/pending_friendship_request.png new file mode 100755 index 0000000..8179ecb Binary files /dev/null and b/src/main/res/drawable-hdpi/pending_friendship_request.png differ diff --git a/src/main/res/drawable-hdpi/phone_48dp.png b/src/main/res/drawable-hdpi/phone_48dp.png new file mode 100755 index 0000000..f033119 Binary files /dev/null and b/src/main/res/drawable-hdpi/phone_48dp.png differ diff --git a/src/main/res/drawable-hdpi/play_button.png b/src/main/res/drawable-hdpi/play_button.png new file mode 100755 index 0000000..fb170f4 Binary files /dev/null and b/src/main/res/drawable-hdpi/play_button.png differ diff --git a/src/main/res/drawable-hdpi/poinila_24dp_white.png b/src/main/res/drawable-hdpi/poinila_24dp_white.png new file mode 100755 index 0000000..46287aa Binary files /dev/null and b/src/main/res/drawable-hdpi/poinila_24dp_white.png differ diff --git a/src/main/res/drawable-hdpi/post_no_image.png b/src/main/res/drawable-hdpi/post_no_image.png new file mode 100755 index 0000000..9125871 Binary files /dev/null and b/src/main/res/drawable-hdpi/post_no_image.png differ diff --git a/src/main/res/drawable-hdpi/remove_boulder_36dp.png b/src/main/res/drawable-hdpi/remove_boulder_36dp.png new file mode 100755 index 0000000..bef8bbd Binary files /dev/null and b/src/main/res/drawable-hdpi/remove_boulder_36dp.png differ diff --git a/src/main/res/drawable-hdpi/remove_white_36dp.png b/src/main/res/drawable-hdpi/remove_white_36dp.png new file mode 100755 index 0000000..cc99ace Binary files /dev/null and b/src/main/res/drawable-hdpi/remove_white_36dp.png differ diff --git a/src/main/res/drawable-hdpi/repost_normal.png b/src/main/res/drawable-hdpi/repost_normal.png new file mode 100755 index 0000000..b032f1f Binary files /dev/null and b/src/main/res/drawable-hdpi/repost_normal.png differ diff --git a/src/main/res/drawable-hdpi/repost_pressed.png b/src/main/res/drawable-hdpi/repost_pressed.png new file mode 100755 index 0000000..3a1f2be Binary files /dev/null and b/src/main/res/drawable-hdpi/repost_pressed.png differ diff --git a/src/main/res/drawable-hdpi/rotate_left_white_24dp.png b/src/main/res/drawable-hdpi/rotate_left_white_24dp.png new file mode 100755 index 0000000..2cc05d9 Binary files /dev/null and b/src/main/res/drawable-hdpi/rotate_left_white_24dp.png differ diff --git a/src/main/res/drawable-hdpi/rotate_right_white_24dp.png b/src/main/res/drawable-hdpi/rotate_right_white_24dp.png new file mode 100755 index 0000000..5d2ee07 Binary files /dev/null and b/src/main/res/drawable-hdpi/rotate_right_white_24dp.png differ diff --git a/src/main/res/drawable-hdpi/rsz_icon_label_red2.png b/src/main/res/drawable-hdpi/rsz_icon_label_red2.png new file mode 100755 index 0000000..8783e4b Binary files /dev/null and b/src/main/res/drawable-hdpi/rsz_icon_label_red2.png differ diff --git a/src/main/res/drawable-hdpi/rsz_icon_label_white2.png b/src/main/res/drawable-hdpi/rsz_icon_label_white2.png new file mode 100755 index 0000000..f5124b7 Binary files /dev/null and b/src/main/res/drawable-hdpi/rsz_icon_label_white2.png differ diff --git a/src/main/res/drawable-hdpi/search_normal.png b/src/main/res/drawable-hdpi/search_normal.png new file mode 100755 index 0000000..d60db3f Binary files /dev/null and b/src/main/res/drawable-hdpi/search_normal.png differ diff --git a/src/main/res/drawable-hdpi/search_pressed.png b/src/main/res/drawable-hdpi/search_pressed.png new file mode 100755 index 0000000..cf33a18 Binary files /dev/null and b/src/main/res/drawable-hdpi/search_pressed.png differ diff --git a/src/main/res/drawable-hdpi/share_mysin_24dp.png b/src/main/res/drawable-hdpi/share_mysin_24dp.png new file mode 100755 index 0000000..c96bd4b Binary files /dev/null and b/src/main/res/drawable-hdpi/share_mysin_24dp.png differ diff --git a/src/main/res/drawable-hdpi/tab_collection_light_nomargin.png b/src/main/res/drawable-hdpi/tab_collection_light_nomargin.png new file mode 100755 index 0000000..124596e Binary files /dev/null and b/src/main/res/drawable-hdpi/tab_collection_light_nomargin.png differ diff --git a/src/main/res/drawable-hdpi/tab_collection_selected_nomargin.png b/src/main/res/drawable-hdpi/tab_collection_selected_nomargin.png new file mode 100755 index 0000000..385f3bf Binary files /dev/null and b/src/main/res/drawable-hdpi/tab_collection_selected_nomargin.png differ diff --git a/src/main/res/drawable-hdpi/tab_dashboard_nomargin.png b/src/main/res/drawable-hdpi/tab_dashboard_nomargin.png new file mode 100755 index 0000000..a03561b Binary files /dev/null and b/src/main/res/drawable-hdpi/tab_dashboard_nomargin.png differ diff --git a/src/main/res/drawable-hdpi/tab_dashboard_selected_nomargin.png b/src/main/res/drawable-hdpi/tab_dashboard_selected_nomargin.png new file mode 100755 index 0000000..f59a31c Binary files /dev/null and b/src/main/res/drawable-hdpi/tab_dashboard_selected_nomargin.png differ diff --git a/src/main/res/drawable-hdpi/tab_notification_light_nomargin.png b/src/main/res/drawable-hdpi/tab_notification_light_nomargin.png new file mode 100755 index 0000000..b05ee5d Binary files /dev/null and b/src/main/res/drawable-hdpi/tab_notification_light_nomargin.png differ diff --git a/src/main/res/drawable-hdpi/tab_notification_selected_nomargin.png b/src/main/res/drawable-hdpi/tab_notification_selected_nomargin.png new file mode 100755 index 0000000..6c0ccf1 Binary files /dev/null and b/src/main/res/drawable-hdpi/tab_notification_selected_nomargin.png differ diff --git a/src/main/res/drawable-hdpi/tab_profile_light_nomargin.png b/src/main/res/drawable-hdpi/tab_profile_light_nomargin.png new file mode 100755 index 0000000..8a3b81d Binary files /dev/null and b/src/main/res/drawable-hdpi/tab_profile_light_nomargin.png differ diff --git a/src/main/res/drawable-hdpi/tab_profile_selected_nomargin.png b/src/main/res/drawable-hdpi/tab_profile_selected_nomargin.png new file mode 100755 index 0000000..efe2b26 Binary files /dev/null and b/src/main/res/drawable-hdpi/tab_profile_selected_nomargin.png differ diff --git a/src/main/res/drawable-hdpi/tab_search_light_nomargin.png b/src/main/res/drawable-hdpi/tab_search_light_nomargin.png new file mode 100755 index 0000000..22d0014 Binary files /dev/null and b/src/main/res/drawable-hdpi/tab_search_light_nomargin.png differ diff --git a/src/main/res/drawable-hdpi/tab_search_nomargin.png b/src/main/res/drawable-hdpi/tab_search_nomargin.png new file mode 100755 index 0000000..da68bee Binary files /dev/null and b/src/main/res/drawable-hdpi/tab_search_nomargin.png differ diff --git a/src/main/res/drawable-hdpi/tab_search_selected_nomargin.png b/src/main/res/drawable-hdpi/tab_search_selected_nomargin.png new file mode 100755 index 0000000..ebe50e0 Binary files /dev/null and b/src/main/res/drawable-hdpi/tab_search_selected_nomargin.png differ diff --git a/src/main/res/drawable-hdpi/toggle_invisible_nobel_32dp.png b/src/main/res/drawable-hdpi/toggle_invisible_nobel_32dp.png new file mode 100755 index 0000000..6662693 Binary files /dev/null and b/src/main/res/drawable-hdpi/toggle_invisible_nobel_32dp.png differ diff --git a/src/main/res/drawable-hdpi/toggle_visible_nobel_32dp.png b/src/main/res/drawable-hdpi/toggle_visible_nobel_32dp.png new file mode 100755 index 0000000..9b2a72d Binary files /dev/null and b/src/main/res/drawable-hdpi/toggle_visible_nobel_32dp.png differ diff --git a/src/main/res/drawable-hdpi/user_no_image.png b/src/main/res/drawable-hdpi/user_no_image.png new file mode 100755 index 0000000..b4de9cd Binary files /dev/null and b/src/main/res/drawable-hdpi/user_no_image.png differ diff --git a/src/main/res/drawable-hdpi/user_no_image_big.png b/src/main/res/drawable-hdpi/user_no_image_big.png new file mode 100755 index 0000000..e741733 Binary files /dev/null and b/src/main/res/drawable-hdpi/user_no_image_big.png differ diff --git a/src/main/res/drawable-hdpi/video_play.png b/src/main/res/drawable-hdpi/video_play.png new file mode 100755 index 0000000..67ae157 Binary files /dev/null and b/src/main/res/drawable-hdpi/video_play.png differ diff --git a/src/main/res/drawable-hdpi/video_type.png b/src/main/res/drawable-hdpi/video_type.png new file mode 100755 index 0000000..9e96f69 Binary files /dev/null and b/src/main/res/drawable-hdpi/video_type.png differ diff --git a/src/main/res/drawable-hdpi/visible_black_24dp.png b/src/main/res/drawable-hdpi/visible_black_24dp.png new file mode 100755 index 0000000..c23ea82 Binary files /dev/null and b/src/main/res/drawable-hdpi/visible_black_24dp.png differ diff --git a/src/main/res/drawable-hdpi/zoom_btn.png b/src/main/res/drawable-hdpi/zoom_btn.png new file mode 100755 index 0000000..fe761d3 Binary files /dev/null and b/src/main/res/drawable-hdpi/zoom_btn.png differ diff --git a/src/main/res/drawable-mdpi/action_follow_2_dark.png b/src/main/res/drawable-mdpi/action_follow_2_dark.png new file mode 100755 index 0000000..bc9f95a Binary files /dev/null and b/src/main/res/drawable-mdpi/action_follow_2_dark.png differ diff --git a/src/main/res/drawable-mdpi/action_follow_selected.png b/src/main/res/drawable-mdpi/action_follow_selected.png new file mode 100755 index 0000000..9d6df45 Binary files /dev/null and b/src/main/res/drawable-mdpi/action_follow_selected.png differ diff --git a/src/main/res/drawable-mdpi/action_help_default.png b/src/main/res/drawable-mdpi/action_help_default.png new file mode 100755 index 0000000..f926d56 Binary files /dev/null and b/src/main/res/drawable-mdpi/action_help_default.png differ diff --git a/src/main/res/drawable-mdpi/action_help_pressed.png b/src/main/res/drawable-mdpi/action_help_pressed.png new file mode 100755 index 0000000..2c95190 Binary files /dev/null and b/src/main/res/drawable-mdpi/action_help_pressed.png differ diff --git a/src/main/res/drawable-mdpi/action_next.png b/src/main/res/drawable-mdpi/action_next.png new file mode 100755 index 0000000..d3cafd6 Binary files /dev/null and b/src/main/res/drawable-mdpi/action_next.png differ diff --git a/src/main/res/drawable-mdpi/action_report_orange.png b/src/main/res/drawable-mdpi/action_report_orange.png new file mode 100755 index 0000000..c8a336b Binary files /dev/null and b/src/main/res/drawable-mdpi/action_report_orange.png differ diff --git a/src/main/res/drawable-mdpi/action_send_comment_orange.png b/src/main/res/drawable-mdpi/action_send_comment_orange.png new file mode 100755 index 0000000..9e2f73b Binary files /dev/null and b/src/main/res/drawable-mdpi/action_send_comment_orange.png differ diff --git a/src/main/res/drawable-mdpi/action_send_comment_white.png b/src/main/res/drawable-mdpi/action_send_comment_white.png new file mode 100755 index 0000000..baa296d Binary files /dev/null and b/src/main/res/drawable-mdpi/action_send_comment_white.png differ diff --git a/src/main/res/drawable-mdpi/action_setting_new_default.png b/src/main/res/drawable-mdpi/action_setting_new_default.png new file mode 100755 index 0000000..92d9204 Binary files /dev/null and b/src/main/res/drawable-mdpi/action_setting_new_default.png differ diff --git a/src/main/res/drawable-mdpi/action_setting_new_pressed.png b/src/main/res/drawable-mdpi/action_setting_new_pressed.png new file mode 100755 index 0000000..8c35e10 Binary files /dev/null and b/src/main/res/drawable-mdpi/action_setting_new_pressed.png differ diff --git a/src/main/res/drawable-mdpi/add_collection_white.png b/src/main/res/drawable-mdpi/add_collection_white.png new file mode 100755 index 0000000..bd32c87 Binary files /dev/null and b/src/main/res/drawable-mdpi/add_collection_white.png differ diff --git a/src/main/res/drawable-mdpi/add_post_white.png b/src/main/res/drawable-mdpi/add_post_white.png new file mode 100755 index 0000000..c42e710 Binary files /dev/null and b/src/main/res/drawable-mdpi/add_post_white.png differ diff --git a/src/main/res/drawable-mdpi/arrow_down_white_24dp.png b/src/main/res/drawable-mdpi/arrow_down_white_24dp.png new file mode 100755 index 0000000..e72148d Binary files /dev/null and b/src/main/res/drawable-mdpi/arrow_down_white_24dp.png differ diff --git a/src/main/res/drawable-mdpi/arrow_up_white_24dp.png b/src/main/res/drawable-mdpi/arrow_up_white_24dp.png new file mode 100755 index 0000000..c735c8c Binary files /dev/null and b/src/main/res/drawable-mdpi/arrow_up_white_24dp.png differ diff --git a/src/main/res/drawable-mdpi/btn_google_signin_dark_normal.9.png b/src/main/res/drawable-mdpi/btn_google_signin_dark_normal.9.png new file mode 100755 index 0000000..6b60191 Binary files /dev/null and b/src/main/res/drawable-mdpi/btn_google_signin_dark_normal.9.png differ diff --git a/src/main/res/drawable-mdpi/btn_google_signin_dark_pressed.9.png b/src/main/res/drawable-mdpi/btn_google_signin_dark_pressed.9.png new file mode 100755 index 0000000..8926740 Binary files /dev/null and b/src/main/res/drawable-mdpi/btn_google_signin_dark_pressed.9.png differ diff --git a/src/main/res/drawable-mdpi/close.png b/src/main/res/drawable-mdpi/close.png new file mode 100755 index 0000000..f5f88c5 Binary files /dev/null and b/src/main/res/drawable-mdpi/close.png differ diff --git a/src/main/res/drawable-mdpi/collection_no_image.png b/src/main/res/drawable-mdpi/collection_no_image.png new file mode 100755 index 0000000..0006bbc Binary files /dev/null and b/src/main/res/drawable-mdpi/collection_no_image.png differ diff --git a/src/main/res/drawable-mdpi/crop_24dp.png b/src/main/res/drawable-mdpi/crop_24dp.png new file mode 100755 index 0000000..330a228 Binary files /dev/null and b/src/main/res/drawable-mdpi/crop_24dp.png differ diff --git a/src/main/res/drawable-mdpi/done_24dp_black.png b/src/main/res/drawable-mdpi/done_24dp_black.png new file mode 100755 index 0000000..c2d00e5 Binary files /dev/null and b/src/main/res/drawable-mdpi/done_24dp_black.png differ diff --git a/src/main/res/drawable-mdpi/done_flamingo_24dp.png b/src/main/res/drawable-mdpi/done_flamingo_24dp.png new file mode 100755 index 0000000..903f021 Binary files /dev/null and b/src/main/res/drawable-mdpi/done_flamingo_24dp.png differ diff --git a/src/main/res/drawable-mdpi/email_48dp.png b/src/main/res/drawable-mdpi/email_48dp.png new file mode 100755 index 0000000..51523ee Binary files /dev/null and b/src/main/res/drawable-mdpi/email_48dp.png differ diff --git a/src/main/res/drawable-mdpi/go_36dp.png b/src/main/res/drawable-mdpi/go_36dp.png new file mode 100755 index 0000000..96a672b Binary files /dev/null and b/src/main/res/drawable-mdpi/go_36dp.png differ diff --git a/src/main/res/drawable-mdpi/icon_label_red2.png b/src/main/res/drawable-mdpi/icon_label_red2.png new file mode 100755 index 0000000..aca721c Binary files /dev/null and b/src/main/res/drawable-mdpi/icon_label_red2.png differ diff --git a/src/main/res/drawable-mdpi/icon_label_white2.png b/src/main/res/drawable-mdpi/icon_label_white2.png new file mode 100755 index 0000000..9183326 Binary files /dev/null and b/src/main/res/drawable-mdpi/icon_label_white2.png differ diff --git a/src/main/res/drawable-mdpi/invisible_black_24dp.png b/src/main/res/drawable-mdpi/invisible_black_24dp.png new file mode 100755 index 0000000..f14aa51 Binary files /dev/null and b/src/main/res/drawable-mdpi/invisible_black_24dp.png differ diff --git a/src/main/res/drawable-mdpi/menu_item_download.png b/src/main/res/drawable-mdpi/menu_item_download.png new file mode 100755 index 0000000..27c1cec Binary files /dev/null and b/src/main/res/drawable-mdpi/menu_item_download.png differ diff --git a/src/main/res/drawable-mdpi/pending_friendship_request.png b/src/main/res/drawable-mdpi/pending_friendship_request.png new file mode 100755 index 0000000..c9e01de Binary files /dev/null and b/src/main/res/drawable-mdpi/pending_friendship_request.png differ diff --git a/src/main/res/drawable-mdpi/phone_48dp.png b/src/main/res/drawable-mdpi/phone_48dp.png new file mode 100755 index 0000000..7ed6cb6 Binary files /dev/null and b/src/main/res/drawable-mdpi/phone_48dp.png differ diff --git a/src/main/res/drawable-mdpi/play_button.png b/src/main/res/drawable-mdpi/play_button.png new file mode 100755 index 0000000..8a34483 Binary files /dev/null and b/src/main/res/drawable-mdpi/play_button.png differ diff --git a/src/main/res/drawable-mdpi/poinila_24dp_white.png b/src/main/res/drawable-mdpi/poinila_24dp_white.png new file mode 100755 index 0000000..6453403 Binary files /dev/null and b/src/main/res/drawable-mdpi/poinila_24dp_white.png differ diff --git a/src/main/res/drawable-mdpi/post_no_image.png b/src/main/res/drawable-mdpi/post_no_image.png new file mode 100755 index 0000000..fbb1b52 Binary files /dev/null and b/src/main/res/drawable-mdpi/post_no_image.png differ diff --git a/src/main/res/drawable-mdpi/repost_normal.png b/src/main/res/drawable-mdpi/repost_normal.png new file mode 100755 index 0000000..f7644d4 Binary files /dev/null and b/src/main/res/drawable-mdpi/repost_normal.png differ diff --git a/src/main/res/drawable-mdpi/repost_pressed.png b/src/main/res/drawable-mdpi/repost_pressed.png new file mode 100755 index 0000000..10ea22c Binary files /dev/null and b/src/main/res/drawable-mdpi/repost_pressed.png differ diff --git a/src/main/res/drawable-mdpi/rsz_icon_label_red2.png b/src/main/res/drawable-mdpi/rsz_icon_label_red2.png new file mode 100755 index 0000000..8783e4b Binary files /dev/null and b/src/main/res/drawable-mdpi/rsz_icon_label_red2.png differ diff --git a/src/main/res/drawable-mdpi/rsz_icon_label_white2.png b/src/main/res/drawable-mdpi/rsz_icon_label_white2.png new file mode 100755 index 0000000..f5124b7 Binary files /dev/null and b/src/main/res/drawable-mdpi/rsz_icon_label_white2.png differ diff --git a/src/main/res/drawable-mdpi/share_mysin_24dp.png b/src/main/res/drawable-mdpi/share_mysin_24dp.png new file mode 100755 index 0000000..08bd067 Binary files /dev/null and b/src/main/res/drawable-mdpi/share_mysin_24dp.png differ diff --git a/src/main/res/drawable-mdpi/tab_collection_light_nomargin.png b/src/main/res/drawable-mdpi/tab_collection_light_nomargin.png new file mode 100755 index 0000000..4f306e7 Binary files /dev/null and b/src/main/res/drawable-mdpi/tab_collection_light_nomargin.png differ diff --git a/src/main/res/drawable-mdpi/tab_collection_selected_nomargin.png b/src/main/res/drawable-mdpi/tab_collection_selected_nomargin.png new file mode 100755 index 0000000..87b3433 Binary files /dev/null and b/src/main/res/drawable-mdpi/tab_collection_selected_nomargin.png differ diff --git a/src/main/res/drawable-mdpi/tab_dashboard_light_nomargin.png b/src/main/res/drawable-mdpi/tab_dashboard_light_nomargin.png new file mode 100755 index 0000000..bb223d8 Binary files /dev/null and b/src/main/res/drawable-mdpi/tab_dashboard_light_nomargin.png differ diff --git a/src/main/res/drawable-mdpi/tab_dashboard_normal.png b/src/main/res/drawable-mdpi/tab_dashboard_normal.png new file mode 100755 index 0000000..48ebda8 Binary files /dev/null and b/src/main/res/drawable-mdpi/tab_dashboard_normal.png differ diff --git a/src/main/res/drawable-mdpi/tab_dashboard_selected.png b/src/main/res/drawable-mdpi/tab_dashboard_selected.png new file mode 100755 index 0000000..8d14b0c Binary files /dev/null and b/src/main/res/drawable-mdpi/tab_dashboard_selected.png differ diff --git a/src/main/res/drawable-mdpi/tab_dashboard_selected_nomargin.png b/src/main/res/drawable-mdpi/tab_dashboard_selected_nomargin.png new file mode 100755 index 0000000..c2d3779 Binary files /dev/null and b/src/main/res/drawable-mdpi/tab_dashboard_selected_nomargin.png differ diff --git a/src/main/res/drawable-mdpi/tab_notification_light_nomargin.png b/src/main/res/drawable-mdpi/tab_notification_light_nomargin.png new file mode 100755 index 0000000..3353b3d Binary files /dev/null and b/src/main/res/drawable-mdpi/tab_notification_light_nomargin.png differ diff --git a/src/main/res/drawable-mdpi/tab_notification_selected_nomargin.png b/src/main/res/drawable-mdpi/tab_notification_selected_nomargin.png new file mode 100755 index 0000000..c53b4aa Binary files /dev/null and b/src/main/res/drawable-mdpi/tab_notification_selected_nomargin.png differ diff --git a/src/main/res/drawable-mdpi/tab_profile_light_nomargin.png b/src/main/res/drawable-mdpi/tab_profile_light_nomargin.png new file mode 100755 index 0000000..1b1ee56 Binary files /dev/null and b/src/main/res/drawable-mdpi/tab_profile_light_nomargin.png differ diff --git a/src/main/res/drawable-mdpi/tab_profile_selected_nomargin.png b/src/main/res/drawable-mdpi/tab_profile_selected_nomargin.png new file mode 100755 index 0000000..f9d68e3 Binary files /dev/null and b/src/main/res/drawable-mdpi/tab_profile_selected_nomargin.png differ diff --git a/src/main/res/drawable-mdpi/tab_search_light_nomargin.png b/src/main/res/drawable-mdpi/tab_search_light_nomargin.png new file mode 100755 index 0000000..63dc75c Binary files /dev/null and b/src/main/res/drawable-mdpi/tab_search_light_nomargin.png differ diff --git a/src/main/res/drawable-mdpi/tab_search_nomargin.png b/src/main/res/drawable-mdpi/tab_search_nomargin.png new file mode 100755 index 0000000..480ef9e Binary files /dev/null and b/src/main/res/drawable-mdpi/tab_search_nomargin.png differ diff --git a/src/main/res/drawable-mdpi/tab_search_selected_nomargin.png b/src/main/res/drawable-mdpi/tab_search_selected_nomargin.png new file mode 100755 index 0000000..d1227ef Binary files /dev/null and b/src/main/res/drawable-mdpi/tab_search_selected_nomargin.png differ diff --git a/src/main/res/drawable-mdpi/toggle_invisible_nobel_32dp.png b/src/main/res/drawable-mdpi/toggle_invisible_nobel_32dp.png new file mode 100755 index 0000000..353902e Binary files /dev/null and b/src/main/res/drawable-mdpi/toggle_invisible_nobel_32dp.png differ diff --git a/src/main/res/drawable-mdpi/toggle_visible_nobel_32dp.png b/src/main/res/drawable-mdpi/toggle_visible_nobel_32dp.png new file mode 100755 index 0000000..24b49d3 Binary files /dev/null and b/src/main/res/drawable-mdpi/toggle_visible_nobel_32dp.png differ diff --git a/src/main/res/drawable-mdpi/user_no_image.png b/src/main/res/drawable-mdpi/user_no_image.png new file mode 100755 index 0000000..16514fa Binary files /dev/null and b/src/main/res/drawable-mdpi/user_no_image.png differ diff --git a/src/main/res/drawable-mdpi/user_no_image_big.png b/src/main/res/drawable-mdpi/user_no_image_big.png new file mode 100755 index 0000000..2e84f6f Binary files /dev/null and b/src/main/res/drawable-mdpi/user_no_image_big.png differ diff --git a/src/main/res/drawable-mdpi/video_play.png b/src/main/res/drawable-mdpi/video_play.png new file mode 100755 index 0000000..9091738 Binary files /dev/null and b/src/main/res/drawable-mdpi/video_play.png differ diff --git a/src/main/res/drawable-mdpi/video_type.png b/src/main/res/drawable-mdpi/video_type.png new file mode 100755 index 0000000..a4e65b1 Binary files /dev/null and b/src/main/res/drawable-mdpi/video_type.png differ diff --git a/src/main/res/drawable-mdpi/visible_black_24dp.png b/src/main/res/drawable-mdpi/visible_black_24dp.png new file mode 100755 index 0000000..87e606b Binary files /dev/null and b/src/main/res/drawable-mdpi/visible_black_24dp.png differ diff --git a/src/main/res/drawable-mdpi/zoom_btn.png b/src/main/res/drawable-mdpi/zoom_btn.png new file mode 100755 index 0000000..0fc5c3e Binary files /dev/null and b/src/main/res/drawable-mdpi/zoom_btn.png differ diff --git a/src/main/res/drawable-nodpi/help_add_content.jpg b/src/main/res/drawable-nodpi/help_add_content.jpg new file mode 100755 index 0000000..f6ef6bf Binary files /dev/null and b/src/main/res/drawable-nodpi/help_add_content.jpg differ diff --git a/src/main/res/drawable-nodpi/help_collections.jpg b/src/main/res/drawable-nodpi/help_collections.jpg new file mode 100755 index 0000000..dc0191a Binary files /dev/null and b/src/main/res/drawable-nodpi/help_collections.jpg differ diff --git a/src/main/res/drawable-nodpi/help_dashboard.jpg b/src/main/res/drawable-nodpi/help_dashboard.jpg new file mode 100755 index 0000000..55d326b Binary files /dev/null and b/src/main/res/drawable-nodpi/help_dashboard.jpg differ diff --git a/src/main/res/drawable-nodpi/help_notifications.jpg b/src/main/res/drawable-nodpi/help_notifications.jpg new file mode 100755 index 0000000..fa05179 Binary files /dev/null and b/src/main/res/drawable-nodpi/help_notifications.jpg differ diff --git a/src/main/res/drawable-nodpi/help_profile.jpg b/src/main/res/drawable-nodpi/help_profile.jpg new file mode 100755 index 0000000..c5761b3 Binary files /dev/null and b/src/main/res/drawable-nodpi/help_profile.jpg differ diff --git a/src/main/res/drawable-nodpi/help_search.jpg b/src/main/res/drawable-nodpi/help_search.jpg new file mode 100755 index 0000000..dc86a18 Binary files /dev/null and b/src/main/res/drawable-nodpi/help_search.jpg differ diff --git a/src/main/res/drawable-nodpi/help_text_background.png b/src/main/res/drawable-nodpi/help_text_background.png new file mode 100755 index 0000000..151aa0e Binary files /dev/null and b/src/main/res/drawable-nodpi/help_text_background.png differ diff --git a/src/main/res/drawable-nodpi/login_background.png b/src/main/res/drawable-nodpi/login_background.png new file mode 100755 index 0000000..29b623a Binary files /dev/null and b/src/main/res/drawable-nodpi/login_background.png differ diff --git a/src/main/res/drawable-nodpi/ponila_logo_with_text.png b/src/main/res/drawable-nodpi/ponila_logo_with_text.png new file mode 100755 index 0000000..29c65ec Binary files /dev/null and b/src/main/res/drawable-nodpi/ponila_logo_with_text.png differ diff --git a/src/main/res/drawable-nodpi/splash_background.jpg b/src/main/res/drawable-nodpi/splash_background.jpg new file mode 100755 index 0000000..2873a26 Binary files /dev/null and b/src/main/res/drawable-nodpi/splash_background.jpg differ diff --git a/src/main/res/drawable-nodpi/texture.png b/src/main/res/drawable-nodpi/texture.png new file mode 100755 index 0000000..06d7b1a Binary files /dev/null and b/src/main/res/drawable-nodpi/texture.png differ diff --git a/src/main/res/drawable-xhdpi/action_add_circle.png b/src/main/res/drawable-xhdpi/action_add_circle.png new file mode 100755 index 0000000..91c9d9f Binary files /dev/null and b/src/main/res/drawable-xhdpi/action_add_circle.png differ diff --git a/src/main/res/drawable-xhdpi/action_check.png b/src/main/res/drawable-xhdpi/action_check.png new file mode 100755 index 0000000..b893ab1 Binary files /dev/null and b/src/main/res/drawable-xhdpi/action_check.png differ diff --git a/src/main/res/drawable-xhdpi/action_follow_2_dark.png b/src/main/res/drawable-xhdpi/action_follow_2_dark.png new file mode 100755 index 0000000..b45f5cf Binary files /dev/null and b/src/main/res/drawable-xhdpi/action_follow_2_dark.png differ diff --git a/src/main/res/drawable-xhdpi/action_follow_selected.png b/src/main/res/drawable-xhdpi/action_follow_selected.png new file mode 100755 index 0000000..c652cf6 Binary files /dev/null and b/src/main/res/drawable-xhdpi/action_follow_selected.png differ diff --git a/src/main/res/drawable-xhdpi/action_help_default.png b/src/main/res/drawable-xhdpi/action_help_default.png new file mode 100755 index 0000000..6dec86d Binary files /dev/null and b/src/main/res/drawable-xhdpi/action_help_default.png differ diff --git a/src/main/res/drawable-xhdpi/action_help_pressed.png b/src/main/res/drawable-xhdpi/action_help_pressed.png new file mode 100755 index 0000000..d74e989 Binary files /dev/null and b/src/main/res/drawable-xhdpi/action_help_pressed.png differ diff --git a/src/main/res/drawable-xhdpi/action_next.png b/src/main/res/drawable-xhdpi/action_next.png new file mode 100755 index 0000000..374916f Binary files /dev/null and b/src/main/res/drawable-xhdpi/action_next.png differ diff --git a/src/main/res/drawable-xhdpi/action_report_orange.png b/src/main/res/drawable-xhdpi/action_report_orange.png new file mode 100755 index 0000000..f53cd94 Binary files /dev/null and b/src/main/res/drawable-xhdpi/action_report_orange.png differ diff --git a/src/main/res/drawable-xhdpi/action_repost_normal.png b/src/main/res/drawable-xhdpi/action_repost_normal.png new file mode 100755 index 0000000..78e2969 Binary files /dev/null and b/src/main/res/drawable-xhdpi/action_repost_normal.png differ diff --git a/src/main/res/drawable-xhdpi/action_send_comment_orange.png b/src/main/res/drawable-xhdpi/action_send_comment_orange.png new file mode 100755 index 0000000..c8f669d Binary files /dev/null and b/src/main/res/drawable-xhdpi/action_send_comment_orange.png differ diff --git a/src/main/res/drawable-xhdpi/action_send_comment_white.png b/src/main/res/drawable-xhdpi/action_send_comment_white.png new file mode 100755 index 0000000..a9e4e6a Binary files /dev/null and b/src/main/res/drawable-xhdpi/action_send_comment_white.png differ diff --git a/src/main/res/drawable-xhdpi/action_setting_new_default.png b/src/main/res/drawable-xhdpi/action_setting_new_default.png new file mode 100755 index 0000000..70065fc Binary files /dev/null and b/src/main/res/drawable-xhdpi/action_setting_new_default.png differ diff --git a/src/main/res/drawable-xhdpi/action_setting_new_pressed.png b/src/main/res/drawable-xhdpi/action_setting_new_pressed.png new file mode 100755 index 0000000..e6b763c Binary files /dev/null and b/src/main/res/drawable-xhdpi/action_setting_new_pressed.png differ diff --git a/src/main/res/drawable-xhdpi/add_collection_white.png b/src/main/res/drawable-xhdpi/add_collection_white.png new file mode 100755 index 0000000..8e60f42 Binary files /dev/null and b/src/main/res/drawable-xhdpi/add_collection_white.png differ diff --git a/src/main/res/drawable-xhdpi/add_friend.png b/src/main/res/drawable-xhdpi/add_friend.png new file mode 100755 index 0000000..94e0e90 Binary files /dev/null and b/src/main/res/drawable-xhdpi/add_friend.png differ diff --git a/src/main/res/drawable-xhdpi/add_friend_selected.png b/src/main/res/drawable-xhdpi/add_friend_selected.png new file mode 100755 index 0000000..4318656 Binary files /dev/null and b/src/main/res/drawable-xhdpi/add_friend_selected.png differ diff --git a/src/main/res/drawable-xhdpi/add_icon.png b/src/main/res/drawable-xhdpi/add_icon.png new file mode 100755 index 0000000..acec217 Binary files /dev/null and b/src/main/res/drawable-xhdpi/add_icon.png differ diff --git a/src/main/res/drawable-xhdpi/add_post_white.png b/src/main/res/drawable-xhdpi/add_post_white.png new file mode 100755 index 0000000..b200731 Binary files /dev/null and b/src/main/res/drawable-xhdpi/add_post_white.png differ diff --git a/src/main/res/drawable-xhdpi/arrow_down_white_24dp.png b/src/main/res/drawable-xhdpi/arrow_down_white_24dp.png new file mode 100755 index 0000000..5b6136f Binary files /dev/null and b/src/main/res/drawable-xhdpi/arrow_down_white_24dp.png differ diff --git a/src/main/res/drawable-xhdpi/arrow_up_white_24dp.png b/src/main/res/drawable-xhdpi/arrow_up_white_24dp.png new file mode 100755 index 0000000..0008f33 Binary files /dev/null and b/src/main/res/drawable-xhdpi/arrow_up_white_24dp.png differ diff --git a/src/main/res/drawable-xhdpi/btn_google_signin_dark_normal.9.png b/src/main/res/drawable-xhdpi/btn_google_signin_dark_normal.9.png new file mode 100755 index 0000000..e48e5a2 Binary files /dev/null and b/src/main/res/drawable-xhdpi/btn_google_signin_dark_normal.9.png differ diff --git a/src/main/res/drawable-xhdpi/btn_google_signin_dark_pressed.9.png b/src/main/res/drawable-xhdpi/btn_google_signin_dark_pressed.9.png new file mode 100755 index 0000000..b1ed3e2 Binary files /dev/null and b/src/main/res/drawable-xhdpi/btn_google_signin_dark_pressed.9.png differ diff --git a/src/main/res/drawable-xhdpi/camera_normal.png b/src/main/res/drawable-xhdpi/camera_normal.png new file mode 100755 index 0000000..16b79ac Binary files /dev/null and b/src/main/res/drawable-xhdpi/camera_normal.png differ diff --git a/src/main/res/drawable-xhdpi/camera_pressed.png b/src/main/res/drawable-xhdpi/camera_pressed.png new file mode 100755 index 0000000..188f743 Binary files /dev/null and b/src/main/res/drawable-xhdpi/camera_pressed.png differ diff --git a/src/main/res/drawable-xhdpi/checkbox_off.png b/src/main/res/drawable-xhdpi/checkbox_off.png new file mode 100755 index 0000000..d43c68f Binary files /dev/null and b/src/main/res/drawable-xhdpi/checkbox_off.png differ diff --git a/src/main/res/drawable-xhdpi/checkbox_off_pressed.png b/src/main/res/drawable-xhdpi/checkbox_off_pressed.png new file mode 100755 index 0000000..f969da9 Binary files /dev/null and b/src/main/res/drawable-xhdpi/checkbox_off_pressed.png differ diff --git a/src/main/res/drawable-xhdpi/checkbox_on.png b/src/main/res/drawable-xhdpi/checkbox_on.png new file mode 100755 index 0000000..ba56dc0 Binary files /dev/null and b/src/main/res/drawable-xhdpi/checkbox_on.png differ diff --git a/src/main/res/drawable-xhdpi/clear.png b/src/main/res/drawable-xhdpi/clear.png new file mode 100755 index 0000000..336a631 Binary files /dev/null and b/src/main/res/drawable-xhdpi/clear.png differ diff --git a/src/main/res/drawable-xhdpi/close.png b/src/main/res/drawable-xhdpi/close.png new file mode 100755 index 0000000..f3d0556 Binary files /dev/null and b/src/main/res/drawable-xhdpi/close.png differ diff --git a/src/main/res/drawable-xhdpi/collection_no_image.png b/src/main/res/drawable-xhdpi/collection_no_image.png new file mode 100755 index 0000000..e810451 Binary files /dev/null and b/src/main/res/drawable-xhdpi/collection_no_image.png differ diff --git a/src/main/res/drawable-xhdpi/collection_white.png b/src/main/res/drawable-xhdpi/collection_white.png new file mode 100755 index 0000000..fee729a Binary files /dev/null and b/src/main/res/drawable-xhdpi/collection_white.png differ diff --git a/src/main/res/drawable-xhdpi/crop_24dp.png b/src/main/res/drawable-xhdpi/crop_24dp.png new file mode 100755 index 0000000..e5db6cc Binary files /dev/null and b/src/main/res/drawable-xhdpi/crop_24dp.png differ diff --git a/src/main/res/drawable-xhdpi/delete.png b/src/main/res/drawable-xhdpi/delete.png new file mode 100755 index 0000000..8d322aa Binary files /dev/null and b/src/main/res/drawable-xhdpi/delete.png differ diff --git a/src/main/res/drawable-xhdpi/delete_icon.png b/src/main/res/drawable-xhdpi/delete_icon.png new file mode 100755 index 0000000..332998a Binary files /dev/null and b/src/main/res/drawable-xhdpi/delete_icon.png differ diff --git a/src/main/res/drawable-xhdpi/delete_icon_pressed.png b/src/main/res/drawable-xhdpi/delete_icon_pressed.png new file mode 100755 index 0000000..d87cd7e Binary files /dev/null and b/src/main/res/drawable-xhdpi/delete_icon_pressed.png differ diff --git a/src/main/res/drawable-xhdpi/done_24dp_black.png b/src/main/res/drawable-xhdpi/done_24dp_black.png new file mode 100755 index 0000000..18650ca Binary files /dev/null and b/src/main/res/drawable-xhdpi/done_24dp_black.png differ diff --git a/src/main/res/drawable-xhdpi/done_flamingo_24dp.png b/src/main/res/drawable-xhdpi/done_flamingo_24dp.png new file mode 100755 index 0000000..eedf8f9 Binary files /dev/null and b/src/main/res/drawable-xhdpi/done_flamingo_24dp.png differ diff --git a/src/main/res/drawable-xhdpi/edit_small.png b/src/main/res/drawable-xhdpi/edit_small.png new file mode 100755 index 0000000..849f04b Binary files /dev/null and b/src/main/res/drawable-xhdpi/edit_small.png differ diff --git a/src/main/res/drawable-xhdpi/edit_white.png b/src/main/res/drawable-xhdpi/edit_white.png new file mode 100755 index 0000000..90bbad4 Binary files /dev/null and b/src/main/res/drawable-xhdpi/edit_white.png differ diff --git a/src/main/res/drawable-xhdpi/email_48dp.png b/src/main/res/drawable-xhdpi/email_48dp.png new file mode 100755 index 0000000..f17d261 Binary files /dev/null and b/src/main/res/drawable-xhdpi/email_48dp.png differ diff --git a/src/main/res/drawable-xhdpi/form_login_password.png b/src/main/res/drawable-xhdpi/form_login_password.png new file mode 100755 index 0000000..f9ad95d Binary files /dev/null and b/src/main/res/drawable-xhdpi/form_login_password.png differ diff --git a/src/main/res/drawable-xhdpi/form_login_user.png b/src/main/res/drawable-xhdpi/form_login_user.png new file mode 100755 index 0000000..76ac22a Binary files /dev/null and b/src/main/res/drawable-xhdpi/form_login_user.png differ diff --git a/src/main/res/drawable-xhdpi/friends.png b/src/main/res/drawable-xhdpi/friends.png new file mode 100755 index 0000000..0810ce1 Binary files /dev/null and b/src/main/res/drawable-xhdpi/friends.png differ diff --git a/src/main/res/drawable-xhdpi/friends_white.png b/src/main/res/drawable-xhdpi/friends_white.png new file mode 100755 index 0000000..c423373 Binary files /dev/null and b/src/main/res/drawable-xhdpi/friends_white.png differ diff --git a/src/main/res/drawable-xhdpi/gallery_normal.png b/src/main/res/drawable-xhdpi/gallery_normal.png new file mode 100755 index 0000000..3d73606 Binary files /dev/null and b/src/main/res/drawable-xhdpi/gallery_normal.png differ diff --git a/src/main/res/drawable-xhdpi/gallery_pressed.png b/src/main/res/drawable-xhdpi/gallery_pressed.png new file mode 100755 index 0000000..d14bd46 Binary files /dev/null and b/src/main/res/drawable-xhdpi/gallery_pressed.png differ diff --git a/src/main/res/drawable-xhdpi/go_36dp.png b/src/main/res/drawable-xhdpi/go_36dp.png new file mode 100755 index 0000000..1056c75 Binary files /dev/null and b/src/main/res/drawable-xhdpi/go_36dp.png differ diff --git a/src/main/res/drawable-xhdpi/ic_media_fullscreen_shrink.png b/src/main/res/drawable-xhdpi/ic_media_fullscreen_shrink.png new file mode 100755 index 0000000..eab123a Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_media_fullscreen_shrink.png differ diff --git a/src/main/res/drawable-xhdpi/ic_media_fullscreen_stretch.png b/src/main/res/drawable-xhdpi/ic_media_fullscreen_stretch.png new file mode 100755 index 0000000..137bb04 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_media_fullscreen_stretch.png differ diff --git a/src/main/res/drawable-xhdpi/ic_media_pause.png b/src/main/res/drawable-xhdpi/ic_media_pause.png new file mode 100755 index 0000000..1d465a4 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_media_pause.png differ diff --git a/src/main/res/drawable-xhdpi/ic_media_play.png b/src/main/res/drawable-xhdpi/ic_media_play.png new file mode 100755 index 0000000..2746d17 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_media_play.png differ diff --git a/src/main/res/drawable-xhdpi/icon_label_red2.png b/src/main/res/drawable-xhdpi/icon_label_red2.png new file mode 100755 index 0000000..aca721c Binary files /dev/null and b/src/main/res/drawable-xhdpi/icon_label_red2.png differ diff --git a/src/main/res/drawable-xhdpi/icon_label_white2.png b/src/main/res/drawable-xhdpi/icon_label_white2.png new file mode 100755 index 0000000..9183326 Binary files /dev/null and b/src/main/res/drawable-xhdpi/icon_label_white2.png differ diff --git a/src/main/res/drawable-xhdpi/invisible_black_24dp.png b/src/main/res/drawable-xhdpi/invisible_black_24dp.png new file mode 100755 index 0000000..a8e5e76 Binary files /dev/null and b/src/main/res/drawable-xhdpi/invisible_black_24dp.png differ diff --git a/src/main/res/drawable-xhdpi/menu_item_download.png b/src/main/res/drawable-xhdpi/menu_item_download.png new file mode 100755 index 0000000..f43c7c7 Binary files /dev/null and b/src/main/res/drawable-xhdpi/menu_item_download.png differ diff --git a/src/main/res/drawable-xhdpi/pending_friendship_request.png b/src/main/res/drawable-xhdpi/pending_friendship_request.png new file mode 100755 index 0000000..ee0e412 Binary files /dev/null and b/src/main/res/drawable-xhdpi/pending_friendship_request.png differ diff --git a/src/main/res/drawable-xhdpi/phone_48dp.png b/src/main/res/drawable-xhdpi/phone_48dp.png new file mode 100755 index 0000000..708fee9 Binary files /dev/null and b/src/main/res/drawable-xhdpi/phone_48dp.png differ diff --git a/src/main/res/drawable-xhdpi/play_button.png b/src/main/res/drawable-xhdpi/play_button.png new file mode 100755 index 0000000..a4e117e Binary files /dev/null and b/src/main/res/drawable-xhdpi/play_button.png differ diff --git a/src/main/res/drawable-xhdpi/poinila_24dp_white.png b/src/main/res/drawable-xhdpi/poinila_24dp_white.png new file mode 100755 index 0000000..6802071 Binary files /dev/null and b/src/main/res/drawable-xhdpi/poinila_24dp_white.png differ diff --git a/src/main/res/drawable-xhdpi/post_no_image.png b/src/main/res/drawable-xhdpi/post_no_image.png new file mode 100755 index 0000000..273aa5e Binary files /dev/null and b/src/main/res/drawable-xhdpi/post_no_image.png differ diff --git a/src/main/res/drawable-xhdpi/radiobutton_off.png b/src/main/res/drawable-xhdpi/radiobutton_off.png new file mode 100755 index 0000000..18310c3 Binary files /dev/null and b/src/main/res/drawable-xhdpi/radiobutton_off.png differ diff --git a/src/main/res/drawable-xhdpi/radiobutton_on.png b/src/main/res/drawable-xhdpi/radiobutton_on.png new file mode 100755 index 0000000..31a9c10 Binary files /dev/null and b/src/main/res/drawable-xhdpi/radiobutton_on.png differ diff --git a/src/main/res/drawable-xhdpi/radiobutton_on_pressed.png b/src/main/res/drawable-xhdpi/radiobutton_on_pressed.png new file mode 100755 index 0000000..3e7cea2 Binary files /dev/null and b/src/main/res/drawable-xhdpi/radiobutton_on_pressed.png differ diff --git a/src/main/res/drawable-xhdpi/repost_normal.png b/src/main/res/drawable-xhdpi/repost_normal.png new file mode 100755 index 0000000..b8b42c8 Binary files /dev/null and b/src/main/res/drawable-xhdpi/repost_normal.png differ diff --git a/src/main/res/drawable-xhdpi/repost_pressed.png b/src/main/res/drawable-xhdpi/repost_pressed.png new file mode 100755 index 0000000..29d1485 Binary files /dev/null and b/src/main/res/drawable-xhdpi/repost_pressed.png differ diff --git a/src/main/res/drawable-xhdpi/rsz_icon_label_red2.png b/src/main/res/drawable-xhdpi/rsz_icon_label_red2.png new file mode 100755 index 0000000..8783e4b Binary files /dev/null and b/src/main/res/drawable-xhdpi/rsz_icon_label_red2.png differ diff --git a/src/main/res/drawable-xhdpi/rsz_icon_label_white2.png b/src/main/res/drawable-xhdpi/rsz_icon_label_white2.png new file mode 100755 index 0000000..f5124b7 Binary files /dev/null and b/src/main/res/drawable-xhdpi/rsz_icon_label_white2.png differ diff --git a/src/main/res/drawable-xhdpi/search_tag_active.png b/src/main/res/drawable-xhdpi/search_tag_active.png new file mode 100755 index 0000000..9737faa Binary files /dev/null and b/src/main/res/drawable-xhdpi/search_tag_active.png differ diff --git a/src/main/res/drawable-xhdpi/search_tag_inactive.png b/src/main/res/drawable-xhdpi/search_tag_inactive.png new file mode 100755 index 0000000..a317e6b Binary files /dev/null and b/src/main/res/drawable-xhdpi/search_tag_inactive.png differ diff --git a/src/main/res/drawable-xhdpi/send_normal.png b/src/main/res/drawable-xhdpi/send_normal.png new file mode 100755 index 0000000..164b9d9 Binary files /dev/null and b/src/main/res/drawable-xhdpi/send_normal.png differ diff --git a/src/main/res/drawable-xhdpi/send_pressed.png b/src/main/res/drawable-xhdpi/send_pressed.png new file mode 100755 index 0000000..cb2d137 Binary files /dev/null and b/src/main/res/drawable-xhdpi/send_pressed.png differ diff --git a/src/main/res/drawable-xhdpi/share_mysin_24dp.png b/src/main/res/drawable-xhdpi/share_mysin_24dp.png new file mode 100755 index 0000000..209e540 Binary files /dev/null and b/src/main/res/drawable-xhdpi/share_mysin_24dp.png differ diff --git a/src/main/res/drawable-xhdpi/spinner.9.png b/src/main/res/drawable-xhdpi/spinner.9.png new file mode 100755 index 0000000..4144871 Binary files /dev/null and b/src/main/res/drawable-xhdpi/spinner.9.png differ diff --git a/src/main/res/drawable-xhdpi/tab_collection_light_nomargin.png b/src/main/res/drawable-xhdpi/tab_collection_light_nomargin.png new file mode 100755 index 0000000..5cdaf71 Binary files /dev/null and b/src/main/res/drawable-xhdpi/tab_collection_light_nomargin.png differ diff --git a/src/main/res/drawable-xhdpi/tab_collection_selected_nomargin.png b/src/main/res/drawable-xhdpi/tab_collection_selected_nomargin.png new file mode 100755 index 0000000..923ff84 Binary files /dev/null and b/src/main/res/drawable-xhdpi/tab_collection_selected_nomargin.png differ diff --git a/src/main/res/drawable-xhdpi/tab_dashboard_nomargin.png b/src/main/res/drawable-xhdpi/tab_dashboard_nomargin.png new file mode 100755 index 0000000..7687e91 Binary files /dev/null and b/src/main/res/drawable-xhdpi/tab_dashboard_nomargin.png differ diff --git a/src/main/res/drawable-xhdpi/tab_dashboard_selected_nomargin.png b/src/main/res/drawable-xhdpi/tab_dashboard_selected_nomargin.png new file mode 100755 index 0000000..f50f6ad Binary files /dev/null and b/src/main/res/drawable-xhdpi/tab_dashboard_selected_nomargin.png differ diff --git a/src/main/res/drawable-xhdpi/tab_notification_light_nomargin.png b/src/main/res/drawable-xhdpi/tab_notification_light_nomargin.png new file mode 100755 index 0000000..30f8e29 Binary files /dev/null and b/src/main/res/drawable-xhdpi/tab_notification_light_nomargin.png differ diff --git a/src/main/res/drawable-xhdpi/tab_notification_selected_nomargin.png b/src/main/res/drawable-xhdpi/tab_notification_selected_nomargin.png new file mode 100755 index 0000000..38602fc Binary files /dev/null and b/src/main/res/drawable-xhdpi/tab_notification_selected_nomargin.png differ diff --git a/src/main/res/drawable-xhdpi/tab_profile_light_nomargin.png b/src/main/res/drawable-xhdpi/tab_profile_light_nomargin.png new file mode 100755 index 0000000..0ed5858 Binary files /dev/null and b/src/main/res/drawable-xhdpi/tab_profile_light_nomargin.png differ diff --git a/src/main/res/drawable-xhdpi/tab_profile_selected_nomargin.png b/src/main/res/drawable-xhdpi/tab_profile_selected_nomargin.png new file mode 100755 index 0000000..c4be2ed Binary files /dev/null and b/src/main/res/drawable-xhdpi/tab_profile_selected_nomargin.png differ diff --git a/src/main/res/drawable-xhdpi/tab_search_light_nomargin.png b/src/main/res/drawable-xhdpi/tab_search_light_nomargin.png new file mode 100755 index 0000000..44ad8a9 Binary files /dev/null and b/src/main/res/drawable-xhdpi/tab_search_light_nomargin.png differ diff --git a/src/main/res/drawable-xhdpi/tab_search_nomargin.png b/src/main/res/drawable-xhdpi/tab_search_nomargin.png new file mode 100755 index 0000000..4c1d830 Binary files /dev/null and b/src/main/res/drawable-xhdpi/tab_search_nomargin.png differ diff --git a/src/main/res/drawable-xhdpi/tab_search_selected_nomargin.png b/src/main/res/drawable-xhdpi/tab_search_selected_nomargin.png new file mode 100755 index 0000000..abb6f16 Binary files /dev/null and b/src/main/res/drawable-xhdpi/tab_search_selected_nomargin.png differ diff --git a/src/main/res/drawable-xhdpi/toggle_invisible_nobel_32dp.png b/src/main/res/drawable-xhdpi/toggle_invisible_nobel_32dp.png new file mode 100755 index 0000000..aa40203 Binary files /dev/null and b/src/main/res/drawable-xhdpi/toggle_invisible_nobel_32dp.png differ diff --git a/src/main/res/drawable-xhdpi/toggle_visible_nobel_32dp.png b/src/main/res/drawable-xhdpi/toggle_visible_nobel_32dp.png new file mode 100755 index 0000000..61a2a04 Binary files /dev/null and b/src/main/res/drawable-xhdpi/toggle_visible_nobel_32dp.png differ diff --git a/src/main/res/drawable-xhdpi/user_no_image.png b/src/main/res/drawable-xhdpi/user_no_image.png new file mode 100755 index 0000000..4bb8159 Binary files /dev/null and b/src/main/res/drawable-xhdpi/user_no_image.png differ diff --git a/src/main/res/drawable-xhdpi/user_no_image_big.png b/src/main/res/drawable-xhdpi/user_no_image_big.png new file mode 100755 index 0000000..0c08e93 Binary files /dev/null and b/src/main/res/drawable-xhdpi/user_no_image_big.png differ diff --git a/src/main/res/drawable-xhdpi/video_play.png b/src/main/res/drawable-xhdpi/video_play.png new file mode 100755 index 0000000..4c77fa0 Binary files /dev/null and b/src/main/res/drawable-xhdpi/video_play.png differ diff --git a/src/main/res/drawable-xhdpi/video_type.png b/src/main/res/drawable-xhdpi/video_type.png new file mode 100755 index 0000000..f99596c Binary files /dev/null and b/src/main/res/drawable-xhdpi/video_type.png differ diff --git a/src/main/res/drawable-xhdpi/visible_black_24dp.png b/src/main/res/drawable-xhdpi/visible_black_24dp.png new file mode 100755 index 0000000..ee6dbe6 Binary files /dev/null and b/src/main/res/drawable-xhdpi/visible_black_24dp.png differ diff --git a/src/main/res/drawable-xhdpi/zoom_btn.png b/src/main/res/drawable-xhdpi/zoom_btn.png new file mode 100755 index 0000000..fb5f5ff Binary files /dev/null and b/src/main/res/drawable-xhdpi/zoom_btn.png differ diff --git a/src/main/res/drawable-xxhdpi/action_follow_2_dark.png b/src/main/res/drawable-xxhdpi/action_follow_2_dark.png new file mode 100755 index 0000000..c7cec67 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/action_follow_2_dark.png differ diff --git a/src/main/res/drawable-xxhdpi/action_follow_selected.png b/src/main/res/drawable-xxhdpi/action_follow_selected.png new file mode 100755 index 0000000..3d2239c Binary files /dev/null and b/src/main/res/drawable-xxhdpi/action_follow_selected.png differ diff --git a/src/main/res/drawable-xxhdpi/action_help_default.png b/src/main/res/drawable-xxhdpi/action_help_default.png new file mode 100755 index 0000000..161ed15 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/action_help_default.png differ diff --git a/src/main/res/drawable-xxhdpi/action_help_pressed.png b/src/main/res/drawable-xxhdpi/action_help_pressed.png new file mode 100755 index 0000000..20c8791 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/action_help_pressed.png differ diff --git a/src/main/res/drawable-xxhdpi/action_next.png b/src/main/res/drawable-xxhdpi/action_next.png new file mode 100755 index 0000000..7fd5456 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/action_next.png differ diff --git a/src/main/res/drawable-xxhdpi/action_report_orange.png b/src/main/res/drawable-xxhdpi/action_report_orange.png new file mode 100755 index 0000000..549dea6 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/action_report_orange.png differ diff --git a/src/main/res/drawable-xxhdpi/action_send_comment_orange.png b/src/main/res/drawable-xxhdpi/action_send_comment_orange.png new file mode 100755 index 0000000..597d4db Binary files /dev/null and b/src/main/res/drawable-xxhdpi/action_send_comment_orange.png differ diff --git a/src/main/res/drawable-xxhdpi/action_send_comment_white.png b/src/main/res/drawable-xxhdpi/action_send_comment_white.png new file mode 100755 index 0000000..83b546a Binary files /dev/null and b/src/main/res/drawable-xxhdpi/action_send_comment_white.png differ diff --git a/src/main/res/drawable-xxhdpi/action_setting_new_default.png b/src/main/res/drawable-xxhdpi/action_setting_new_default.png new file mode 100755 index 0000000..2a2f916 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/action_setting_new_default.png differ diff --git a/src/main/res/drawable-xxhdpi/action_setting_new_pressed.png b/src/main/res/drawable-xxhdpi/action_setting_new_pressed.png new file mode 100755 index 0000000..6c60f0e Binary files /dev/null and b/src/main/res/drawable-xxhdpi/action_setting_new_pressed.png differ diff --git a/src/main/res/drawable-xxhdpi/add_collection_white.png b/src/main/res/drawable-xxhdpi/add_collection_white.png new file mode 100755 index 0000000..63e0746 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/add_collection_white.png differ diff --git a/src/main/res/drawable-xxhdpi/add_post_white.png b/src/main/res/drawable-xxhdpi/add_post_white.png new file mode 100755 index 0000000..f4ef3e9 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/add_post_white.png differ diff --git a/src/main/res/drawable-xxhdpi/add_white_48dp.png b/src/main/res/drawable-xxhdpi/add_white_48dp.png new file mode 100755 index 0000000..944441d Binary files /dev/null and b/src/main/res/drawable-xxhdpi/add_white_48dp.png differ diff --git a/src/main/res/drawable-xxhdpi/arrow_down_white_24dp.png b/src/main/res/drawable-xxhdpi/arrow_down_white_24dp.png new file mode 100755 index 0000000..9173968 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/arrow_down_white_24dp.png differ diff --git a/src/main/res/drawable-xxhdpi/arrow_left_48dp.png b/src/main/res/drawable-xxhdpi/arrow_left_48dp.png new file mode 100755 index 0000000..84d9fb9 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/arrow_left_48dp.png differ diff --git a/src/main/res/drawable-xxhdpi/arrow_right_white_48dp.png b/src/main/res/drawable-xxhdpi/arrow_right_white_48dp.png new file mode 100755 index 0000000..481971b Binary files /dev/null and b/src/main/res/drawable-xxhdpi/arrow_right_white_48dp.png differ diff --git a/src/main/res/drawable-xxhdpi/arrow_up_white_24dp.png b/src/main/res/drawable-xxhdpi/arrow_up_white_24dp.png new file mode 100755 index 0000000..0e07727 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/arrow_up_white_24dp.png differ diff --git a/src/main/res/drawable-xxhdpi/browser.png b/src/main/res/drawable-xxhdpi/browser.png new file mode 100755 index 0000000..4f10056 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/browser.png differ diff --git a/src/main/res/drawable-xxhdpi/btn_google_signin_dark_normal.9.png b/src/main/res/drawable-xxhdpi/btn_google_signin_dark_normal.9.png new file mode 100755 index 0000000..3fb4c67 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/btn_google_signin_dark_normal.9.png differ diff --git a/src/main/res/drawable-xxhdpi/btn_google_signin_dark_pressed.9.png b/src/main/res/drawable-xxhdpi/btn_google_signin_dark_pressed.9.png new file mode 100755 index 0000000..c5096f7 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/btn_google_signin_dark_pressed.9.png differ diff --git a/src/main/res/drawable-xxhdpi/button_library_normal.png b/src/main/res/drawable-xxhdpi/button_library_normal.png new file mode 100755 index 0000000..9466ba8 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/button_library_normal.png differ diff --git a/src/main/res/drawable-xxhdpi/button_library_pressed.png b/src/main/res/drawable-xxhdpi/button_library_pressed.png new file mode 100755 index 0000000..f5ff7cd Binary files /dev/null and b/src/main/res/drawable-xxhdpi/button_library_pressed.png differ diff --git a/src/main/res/drawable-xxhdpi/clear.png b/src/main/res/drawable-xxhdpi/clear.png new file mode 100755 index 0000000..6e484ec Binary files /dev/null and b/src/main/res/drawable-xxhdpi/clear.png differ diff --git a/src/main/res/drawable-xxhdpi/close.png b/src/main/res/drawable-xxhdpi/close.png new file mode 100755 index 0000000..fcbdbb9 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/close.png differ diff --git a/src/main/res/drawable-xxhdpi/collection_no_image.png b/src/main/res/drawable-xxhdpi/collection_no_image.png new file mode 100755 index 0000000..99a0dc0 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/collection_no_image.png differ diff --git a/src/main/res/drawable-xxhdpi/comment_normal.png b/src/main/res/drawable-xxhdpi/comment_normal.png new file mode 100755 index 0000000..def24da Binary files /dev/null and b/src/main/res/drawable-xxhdpi/comment_normal.png differ diff --git a/src/main/res/drawable-xxhdpi/comment_pressed.png b/src/main/res/drawable-xxhdpi/comment_pressed.png new file mode 100755 index 0000000..98d52c7 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/comment_pressed.png differ diff --git a/src/main/res/drawable-xxhdpi/crop_24dp.png b/src/main/res/drawable-xxhdpi/crop_24dp.png new file mode 100755 index 0000000..093ca50 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/crop_24dp.png differ diff --git a/src/main/res/drawable-xxhdpi/done_24dp_black.png b/src/main/res/drawable-xxhdpi/done_24dp_black.png new file mode 100755 index 0000000..e9818d9 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/done_24dp_black.png differ diff --git a/src/main/res/drawable-xxhdpi/done_flamingo_24dp.png b/src/main/res/drawable-xxhdpi/done_flamingo_24dp.png new file mode 100755 index 0000000..799f2a0 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/done_flamingo_24dp.png differ diff --git a/src/main/res/drawable-xxhdpi/done_white_48dp.png b/src/main/res/drawable-xxhdpi/done_white_48dp.png new file mode 100755 index 0000000..226553b Binary files /dev/null and b/src/main/res/drawable-xxhdpi/done_white_48dp.png differ diff --git a/src/main/res/drawable-xxhdpi/email_48dp.png b/src/main/res/drawable-xxhdpi/email_48dp.png new file mode 100755 index 0000000..dc58481 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/email_48dp.png differ diff --git a/src/main/res/drawable-xxhdpi/go_36dp.png b/src/main/res/drawable-xxhdpi/go_36dp.png new file mode 100755 index 0000000..cae428f Binary files /dev/null and b/src/main/res/drawable-xxhdpi/go_36dp.png differ diff --git a/src/main/res/drawable-xxhdpi/icon_label_red2.png b/src/main/res/drawable-xxhdpi/icon_label_red2.png new file mode 100755 index 0000000..aca721c Binary files /dev/null and b/src/main/res/drawable-xxhdpi/icon_label_red2.png differ diff --git a/src/main/res/drawable-xxhdpi/icon_label_white2.png b/src/main/res/drawable-xxhdpi/icon_label_white2.png new file mode 100755 index 0000000..9183326 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/icon_label_white2.png differ diff --git a/src/main/res/drawable-xxhdpi/invisible_black_24dp.png b/src/main/res/drawable-xxhdpi/invisible_black_24dp.png new file mode 100755 index 0000000..33a0299 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/invisible_black_24dp.png differ diff --git a/src/main/res/drawable-xxhdpi/like_normal.png b/src/main/res/drawable-xxhdpi/like_normal.png new file mode 100755 index 0000000..3c643c6 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/like_normal.png differ diff --git a/src/main/res/drawable-xxhdpi/like_pressed.png b/src/main/res/drawable-xxhdpi/like_pressed.png new file mode 100755 index 0000000..ddc806d Binary files /dev/null and b/src/main/res/drawable-xxhdpi/like_pressed.png differ diff --git a/src/main/res/drawable-xxhdpi/like_selected.png b/src/main/res/drawable-xxhdpi/like_selected.png new file mode 100755 index 0000000..519fbc2 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/like_selected.png differ diff --git a/src/main/res/drawable-xxhdpi/menu_item_download.png b/src/main/res/drawable-xxhdpi/menu_item_download.png new file mode 100755 index 0000000..2097e2f Binary files /dev/null and b/src/main/res/drawable-xxhdpi/menu_item_download.png differ diff --git a/src/main/res/drawable-xxhdpi/pending_friendship_request.png b/src/main/res/drawable-xxhdpi/pending_friendship_request.png new file mode 100755 index 0000000..f0a495f Binary files /dev/null and b/src/main/res/drawable-xxhdpi/pending_friendship_request.png differ diff --git a/src/main/res/drawable-xxhdpi/phone_48dp.png b/src/main/res/drawable-xxhdpi/phone_48dp.png new file mode 100755 index 0000000..8807c86 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/phone_48dp.png differ diff --git a/src/main/res/drawable-xxhdpi/play_button.png b/src/main/res/drawable-xxhdpi/play_button.png new file mode 100755 index 0000000..cfcb88c Binary files /dev/null and b/src/main/res/drawable-xxhdpi/play_button.png differ diff --git a/src/main/res/drawable-xxhdpi/poinila_24dp_white.png b/src/main/res/drawable-xxhdpi/poinila_24dp_white.png new file mode 100755 index 0000000..31447fa Binary files /dev/null and b/src/main/res/drawable-xxhdpi/poinila_24dp_white.png differ diff --git a/src/main/res/drawable-xxhdpi/post_no_image.png b/src/main/res/drawable-xxhdpi/post_no_image.png new file mode 100755 index 0000000..d4f7593 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/post_no_image.png differ diff --git a/src/main/res/drawable-xxhdpi/remove_boulder_36dp.png b/src/main/res/drawable-xxhdpi/remove_boulder_36dp.png new file mode 100755 index 0000000..5359d32 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/remove_boulder_36dp.png differ diff --git a/src/main/res/drawable-xxhdpi/remove_white_36dp.png b/src/main/res/drawable-xxhdpi/remove_white_36dp.png new file mode 100755 index 0000000..36d1c7d Binary files /dev/null and b/src/main/res/drawable-xxhdpi/remove_white_36dp.png differ diff --git a/src/main/res/drawable-xxhdpi/repost_normal.png b/src/main/res/drawable-xxhdpi/repost_normal.png new file mode 100755 index 0000000..1b43083 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/repost_normal.png differ diff --git a/src/main/res/drawable-xxhdpi/repost_pressed.png b/src/main/res/drawable-xxhdpi/repost_pressed.png new file mode 100755 index 0000000..0c01b57 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/repost_pressed.png differ diff --git a/src/main/res/drawable-xxhdpi/rotate_left_white_24dp.png b/src/main/res/drawable-xxhdpi/rotate_left_white_24dp.png new file mode 100755 index 0000000..5d2c2e4 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/rotate_left_white_24dp.png differ diff --git a/src/main/res/drawable-xxhdpi/rotate_right_white_24dp.png b/src/main/res/drawable-xxhdpi/rotate_right_white_24dp.png new file mode 100755 index 0000000..91ab8e4 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/rotate_right_white_24dp.png differ diff --git a/src/main/res/drawable-xxhdpi/rsz_icon_label_red2.png b/src/main/res/drawable-xxhdpi/rsz_icon_label_red2.png new file mode 100755 index 0000000..8783e4b Binary files /dev/null and b/src/main/res/drawable-xxhdpi/rsz_icon_label_red2.png differ diff --git a/src/main/res/drawable-xxhdpi/rsz_icon_label_white2.png b/src/main/res/drawable-xxhdpi/rsz_icon_label_white2.png new file mode 100755 index 0000000..f5124b7 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/rsz_icon_label_white2.png differ diff --git a/src/main/res/drawable-xxhdpi/search_normal.png b/src/main/res/drawable-xxhdpi/search_normal.png new file mode 100755 index 0000000..1cb3329 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/search_normal.png differ diff --git a/src/main/res/drawable-xxhdpi/search_pressed.png b/src/main/res/drawable-xxhdpi/search_pressed.png new file mode 100755 index 0000000..49fcd31 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/search_pressed.png differ diff --git a/src/main/res/drawable-xxhdpi/share_mysin_24dp.png b/src/main/res/drawable-xxhdpi/share_mysin_24dp.png new file mode 100755 index 0000000..22a5d58 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/share_mysin_24dp.png differ diff --git a/src/main/res/drawable-xxhdpi/tab_collection_light_nomargin.png b/src/main/res/drawable-xxhdpi/tab_collection_light_nomargin.png new file mode 100755 index 0000000..dc9dc22 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/tab_collection_light_nomargin.png differ diff --git a/src/main/res/drawable-xxhdpi/tab_collection_selected_nomargin.png b/src/main/res/drawable-xxhdpi/tab_collection_selected_nomargin.png new file mode 100755 index 0000000..4e34a83 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/tab_collection_selected_nomargin.png differ diff --git a/src/main/res/drawable-xxhdpi/tab_dashboard_nomargin.png b/src/main/res/drawable-xxhdpi/tab_dashboard_nomargin.png new file mode 100755 index 0000000..5e90a68 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/tab_dashboard_nomargin.png differ diff --git a/src/main/res/drawable-xxhdpi/tab_dashboard_selected_nomargin.png b/src/main/res/drawable-xxhdpi/tab_dashboard_selected_nomargin.png new file mode 100755 index 0000000..39413ba Binary files /dev/null and b/src/main/res/drawable-xxhdpi/tab_dashboard_selected_nomargin.png differ diff --git a/src/main/res/drawable-xxhdpi/tab_notification_light_nomargin.png b/src/main/res/drawable-xxhdpi/tab_notification_light_nomargin.png new file mode 100755 index 0000000..0179fd1 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/tab_notification_light_nomargin.png differ diff --git a/src/main/res/drawable-xxhdpi/tab_notification_selected_nomargin.png b/src/main/res/drawable-xxhdpi/tab_notification_selected_nomargin.png new file mode 100755 index 0000000..82d69b0 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/tab_notification_selected_nomargin.png differ diff --git a/src/main/res/drawable-xxhdpi/tab_profile_light_nomargin.png b/src/main/res/drawable-xxhdpi/tab_profile_light_nomargin.png new file mode 100755 index 0000000..01cf0c7 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/tab_profile_light_nomargin.png differ diff --git a/src/main/res/drawable-xxhdpi/tab_profile_selected_nomargin.png b/src/main/res/drawable-xxhdpi/tab_profile_selected_nomargin.png new file mode 100755 index 0000000..641f19a Binary files /dev/null and b/src/main/res/drawable-xxhdpi/tab_profile_selected_nomargin.png differ diff --git a/src/main/res/drawable-xxhdpi/tab_search_light_nomargin.png b/src/main/res/drawable-xxhdpi/tab_search_light_nomargin.png new file mode 100755 index 0000000..676f538 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/tab_search_light_nomargin.png differ diff --git a/src/main/res/drawable-xxhdpi/tab_search_nomargin.png b/src/main/res/drawable-xxhdpi/tab_search_nomargin.png new file mode 100755 index 0000000..6fe7b15 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/tab_search_nomargin.png differ diff --git a/src/main/res/drawable-xxhdpi/tab_search_selected_nomargin.png b/src/main/res/drawable-xxhdpi/tab_search_selected_nomargin.png new file mode 100755 index 0000000..fbabea2 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/tab_search_selected_nomargin.png differ diff --git a/src/main/res/drawable-xxhdpi/toggle_invisible_nobel_32dp.png b/src/main/res/drawable-xxhdpi/toggle_invisible_nobel_32dp.png new file mode 100755 index 0000000..5bc3f85 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/toggle_invisible_nobel_32dp.png differ diff --git a/src/main/res/drawable-xxhdpi/toggle_visible_nobel_32dp.png b/src/main/res/drawable-xxhdpi/toggle_visible_nobel_32dp.png new file mode 100755 index 0000000..79b5cd2 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/toggle_visible_nobel_32dp.png differ diff --git a/src/main/res/drawable-xxhdpi/user_no_image.png b/src/main/res/drawable-xxhdpi/user_no_image.png new file mode 100755 index 0000000..ba20b1f Binary files /dev/null and b/src/main/res/drawable-xxhdpi/user_no_image.png differ diff --git a/src/main/res/drawable-xxhdpi/user_no_image_big.png b/src/main/res/drawable-xxhdpi/user_no_image_big.png new file mode 100755 index 0000000..2aad2ee Binary files /dev/null and b/src/main/res/drawable-xxhdpi/user_no_image_big.png differ diff --git a/src/main/res/drawable-xxhdpi/video_play.png b/src/main/res/drawable-xxhdpi/video_play.png new file mode 100755 index 0000000..9cf2309 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/video_play.png differ diff --git a/src/main/res/drawable-xxhdpi/video_type.png b/src/main/res/drawable-xxhdpi/video_type.png new file mode 100755 index 0000000..081ac0b Binary files /dev/null and b/src/main/res/drawable-xxhdpi/video_type.png differ diff --git a/src/main/res/drawable-xxhdpi/visible_black_24dp.png b/src/main/res/drawable-xxhdpi/visible_black_24dp.png new file mode 100755 index 0000000..3cdf16c Binary files /dev/null and b/src/main/res/drawable-xxhdpi/visible_black_24dp.png differ diff --git a/src/main/res/drawable-xxhdpi/zoom_btn.png b/src/main/res/drawable-xxhdpi/zoom_btn.png new file mode 100755 index 0000000..2bb7283 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/zoom_btn.png differ diff --git a/src/main/res/drawable/action_follow_collection.xml b/src/main/res/drawable/action_follow_collection.xml new file mode 100755 index 0000000..7d83c26 --- /dev/null +++ b/src/main/res/drawable/action_follow_collection.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/action_help_setting.xml b/src/main/res/drawable/action_help_setting.xml new file mode 100755 index 0000000..0961c3d --- /dev/null +++ b/src/main/res/drawable/action_help_setting.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/action_settings.xml b/src/main/res/drawable/action_settings.xml new file mode 100755 index 0000000..a941ab3 --- /dev/null +++ b/src/main/res/drawable/action_settings.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/add_friend_selector.xml b/src/main/res/drawable/add_friend_selector.xml new file mode 100755 index 0000000..5ae63f2 --- /dev/null +++ b/src/main/res/drawable/add_friend_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/add_remove_checkbox_selector.xml b/src/main/res/drawable/add_remove_checkbox_selector.xml new file mode 100755 index 0000000..9ed084e --- /dev/null +++ b/src/main/res/drawable/add_remove_checkbox_selector.xml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/bg_texture.xml b/src/main/res/drawable/bg_texture.xml new file mode 100755 index 0000000..a5f0438 --- /dev/null +++ b/src/main/res/drawable/bg_texture.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/src/main/res/drawable/bordered_focusable.xml b/src/main/res/drawable/bordered_focusable.xml new file mode 100755 index 0000000..641573b --- /dev/null +++ b/src/main/res/drawable/bordered_focusable.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/bordered_focusable_float_hint_margin.xml b/src/main/res/drawable/bordered_focusable_float_hint_margin.xml new file mode 100755 index 0000000..c8f972f --- /dev/null +++ b/src/main/res/drawable/bordered_focusable_float_hint_margin.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/bordered_input.xml b/src/main/res/drawable/bordered_input.xml new file mode 100755 index 0000000..068e20c --- /dev/null +++ b/src/main/res/drawable/bordered_input.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/bordered_rounded_rect.xml b/src/main/res/drawable/bordered_rounded_rect.xml new file mode 100755 index 0000000..94a67d9 --- /dev/null +++ b/src/main/res/drawable/bordered_rounded_rect.xml @@ -0,0 +1,23 @@ + + + + + + + diff --git a/src/main/res/drawable/bordered_rounded_rect_checked.xml b/src/main/res/drawable/bordered_rounded_rect_checked.xml new file mode 100755 index 0000000..7382ad8 --- /dev/null +++ b/src/main/res/drawable/bordered_rounded_rect_checked.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/bordered_rounded_rect_disabled.xml b/src/main/res/drawable/bordered_rounded_rect_disabled.xml new file mode 100755 index 0000000..ff69744 --- /dev/null +++ b/src/main/res/drawable/bordered_rounded_rect_disabled.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/bottom_bar_remove_background.xml b/src/main/res/drawable/bottom_bar_remove_background.xml new file mode 100755 index 0000000..b217e79 --- /dev/null +++ b/src/main/res/drawable/bottom_bar_remove_background.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/boulder_rounded_button.xml b/src/main/res/drawable/boulder_rounded_button.xml new file mode 100755 index 0000000..f5dc9f8 --- /dev/null +++ b/src/main/res/drawable/boulder_rounded_button.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/button_library.xml b/src/main/res/drawable/button_library.xml new file mode 100755 index 0000000..bb09b5e --- /dev/null +++ b/src/main/res/drawable/button_library.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/res/drawable/camera_selector.xml b/src/main/res/drawable/camera_selector.xml new file mode 100755 index 0000000..2b7ed10 --- /dev/null +++ b/src/main/res/drawable/camera_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/checkbox_selector.xml b/src/main/res/drawable/checkbox_selector.xml new file mode 100755 index 0000000..e0ec3aa --- /dev/null +++ b/src/main/res/drawable/checkbox_selector.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/circle_black_transparent.xml b/src/main/res/drawable/circle_black_transparent.xml new file mode 100755 index 0000000..2752710 --- /dev/null +++ b/src/main/res/drawable/circle_black_transparent.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/main/res/drawable/collection_bottom_bar.xml b/src/main/res/drawable/collection_bottom_bar.xml new file mode 100755 index 0000000..d5bc28e --- /dev/null +++ b/src/main/res/drawable/collection_bottom_bar.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/collection_detail_owned_delete_button.xml b/src/main/res/drawable/collection_detail_owned_delete_button.xml new file mode 100755 index 0000000..16166e3 --- /dev/null +++ b/src/main/res/drawable/collection_detail_owned_delete_button.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/collection_detail_owned_edit_button.xml b/src/main/res/drawable/collection_detail_owned_edit_button.xml new file mode 100755 index 0000000..5e02454 --- /dev/null +++ b/src/main/res/drawable/collection_detail_owned_edit_button.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/collection_follow.xml b/src/main/res/drawable/collection_follow.xml new file mode 100755 index 0000000..1a6f404 --- /dev/null +++ b/src/main/res/drawable/collection_follow.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/comment_field_background.xml b/src/main/res/drawable/comment_field_background.xml new file mode 100755 index 0000000..ae07767 --- /dev/null +++ b/src/main/res/drawable/comment_field_background.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/comment_selector.xml b/src/main/res/drawable/comment_selector.xml new file mode 100755 index 0000000..37beb05 --- /dev/null +++ b/src/main/res/drawable/comment_selector.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/res/drawable/fab_label_background.xml b/src/main/res/drawable/fab_label_background.xml new file mode 100755 index 0000000..75f6288 --- /dev/null +++ b/src/main/res/drawable/fab_label_background.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/favorite_selector.xml b/src/main/res/drawable/favorite_selector.xml new file mode 100755 index 0000000..cafcfdd --- /dev/null +++ b/src/main/res/drawable/favorite_selector.xml @@ -0,0 +1,30 @@ + + + + + + + + diff --git a/src/main/res/drawable/favorite_selector_list.xml b/src/main/res/drawable/favorite_selector_list.xml new file mode 100755 index 0000000..6c045c4 --- /dev/null +++ b/src/main/res/drawable/favorite_selector_list.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/flamingo_rounded_button.xml b/src/main/res/drawable/flamingo_rounded_button.xml new file mode 100755 index 0000000..09163ed --- /dev/null +++ b/src/main/res/drawable/flamingo_rounded_button.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/gallery_selector.xml b/src/main/res/drawable/gallery_selector.xml new file mode 100755 index 0000000..b2d0fee --- /dev/null +++ b/src/main/res/drawable/gallery_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/google_signin_btn.xml b/src/main/res/drawable/google_signin_btn.xml new file mode 100755 index 0000000..3871038 --- /dev/null +++ b/src/main/res/drawable/google_signin_btn.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/image_caption_text.xml b/src/main/res/drawable/image_caption_text.xml new file mode 100755 index 0000000..b5d82a2 --- /dev/null +++ b/src/main/res/drawable/image_caption_text.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/interest_btn.xml b/src/main/res/drawable/interest_btn.xml new file mode 100755 index 0000000..f0ebc7c --- /dev/null +++ b/src/main/res/drawable/interest_btn.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/interest_frame.xml b/src/main/res/drawable/interest_frame.xml new file mode 100755 index 0000000..0c45202 --- /dev/null +++ b/src/main/res/drawable/interest_frame.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/interest_image_frame.xml b/src/main/res/drawable/interest_image_frame.xml new file mode 100755 index 0000000..a89fc50 --- /dev/null +++ b/src/main/res/drawable/interest_image_frame.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/mercury_bottom_rounded.xml b/src/main/res/drawable/mercury_bottom_rounded.xml new file mode 100755 index 0000000..0ee6399 --- /dev/null +++ b/src/main/res/drawable/mercury_bottom_rounded.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/mysin_rounded_button.xml b/src/main/res/drawable/mysin_rounded_button.xml new file mode 100755 index 0000000..57fc1af --- /dev/null +++ b/src/main/res/drawable/mysin_rounded_button.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/notif_tab.xml b/src/main/res/drawable/notif_tab.xml new file mode 100755 index 0000000..44ff461 --- /dev/null +++ b/src/main/res/drawable/notif_tab.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/option_drawable.xml b/src/main/res/drawable/option_drawable.xml new file mode 100755 index 0000000..d0d6b80 --- /dev/null +++ b/src/main/res/drawable/option_drawable.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/option_left_drawable.xml b/src/main/res/drawable/option_left_drawable.xml new file mode 100755 index 0000000..9481b7c --- /dev/null +++ b/src/main/res/drawable/option_left_drawable.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/option_right_drawable.xml b/src/main/res/drawable/option_right_drawable.xml new file mode 100755 index 0000000..49be119 --- /dev/null +++ b/src/main/res/drawable/option_right_drawable.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/options_background.xml b/src/main/res/drawable/options_background.xml new file mode 100755 index 0000000..23d29e6 --- /dev/null +++ b/src/main/res/drawable/options_background.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/outlined_boulder_button.xml b/src/main/res/drawable/outlined_boulder_button.xml new file mode 100755 index 0000000..13568b7 --- /dev/null +++ b/src/main/res/drawable/outlined_boulder_button.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/poinila_frame.xml b/src/main/res/drawable/poinila_frame.xml new file mode 100755 index 0000000..24b8f47 --- /dev/null +++ b/src/main/res/drawable/poinila_frame.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/post_general_image.xml b/src/main/res/drawable/post_general_image.xml new file mode 100755 index 0000000..9c4e7f7 --- /dev/null +++ b/src/main/res/drawable/post_general_image.xml @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/profile_frame.xml b/src/main/res/drawable/profile_frame.xml new file mode 100755 index 0000000..5c56570 --- /dev/null +++ b/src/main/res/drawable/profile_frame.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/profile_info_circle.xml b/src/main/res/drawable/profile_info_circle.xml new file mode 100755 index 0000000..248b8ae --- /dev/null +++ b/src/main/res/drawable/profile_info_circle.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/radio_button_selector.xml b/src/main/res/drawable/radio_button_selector.xml new file mode 100755 index 0000000..4e820ae --- /dev/null +++ b/src/main/res/drawable/radio_button_selector.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/repost_selector.xml b/src/main/res/drawable/repost_selector.xml new file mode 100755 index 0000000..126bc7f --- /dev/null +++ b/src/main/res/drawable/repost_selector.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/res/drawable/rounded_rect_noble_tag.xml b/src/main/res/drawable/rounded_rect_noble_tag.xml new file mode 100755 index 0000000..545f643 --- /dev/null +++ b/src/main/res/drawable/rounded_rect_noble_tag.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/src/main/res/drawable/search_btn_background.xml b/src/main/res/drawable/search_btn_background.xml new file mode 100755 index 0000000..ac520ab --- /dev/null +++ b/src/main/res/drawable/search_btn_background.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/search_field_background.xml b/src/main/res/drawable/search_field_background.xml new file mode 100755 index 0000000..54e0b42 --- /dev/null +++ b/src/main/res/drawable/search_field_background.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/search_selector.xml b/src/main/res/drawable/search_selector.xml new file mode 100755 index 0000000..9997c30 --- /dev/null +++ b/src/main/res/drawable/search_selector.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/send_comment_selector_background.xml b/src/main/res/drawable/send_comment_selector_background.xml new file mode 100755 index 0000000..052480c --- /dev/null +++ b/src/main/res/drawable/send_comment_selector_background.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/send_selector.xml b/src/main/res/drawable/send_selector.xml new file mode 100755 index 0000000..c28041b --- /dev/null +++ b/src/main/res/drawable/send_selector.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/snackbar_action.xml b/src/main/res/drawable/snackbar_action.xml new file mode 100755 index 0000000..0b19a98 --- /dev/null +++ b/src/main/res/drawable/snackbar_action.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/tab_collection_selector.xml b/src/main/res/drawable/tab_collection_selector.xml new file mode 100755 index 0000000..908d0bc --- /dev/null +++ b/src/main/res/drawable/tab_collection_selector.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/res/drawable/tab_dashboard_selector.xml b/src/main/res/drawable/tab_dashboard_selector.xml new file mode 100755 index 0000000..9baa36b --- /dev/null +++ b/src/main/res/drawable/tab_dashboard_selector.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/res/drawable/tab_notification_number.xml b/src/main/res/drawable/tab_notification_number.xml new file mode 100755 index 0000000..200e786 --- /dev/null +++ b/src/main/res/drawable/tab_notification_number.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/tab_notification_selector.xml b/src/main/res/drawable/tab_notification_selector.xml new file mode 100755 index 0000000..64e8e15 --- /dev/null +++ b/src/main/res/drawable/tab_notification_selector.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/res/drawable/tab_profile_selector.xml b/src/main/res/drawable/tab_profile_selector.xml new file mode 100755 index 0000000..2d13da9 --- /dev/null +++ b/src/main/res/drawable/tab_profile_selector.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/res/drawable/tab_search_selector.xml b/src/main/res/drawable/tab_search_selector.xml new file mode 100755 index 0000000..f7e10e0 --- /dev/null +++ b/src/main/res/drawable/tab_search_selector.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/src/main/res/drawable/video_play_btn.xml b/src/main/res/drawable/video_play_btn.xml new file mode 100755 index 0000000..5953c6b --- /dev/null +++ b/src/main/res/drawable/video_play_btn.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/video_play_btn_background.xml b/src/main/res/drawable/video_play_btn_background.xml new file mode 100755 index 0000000..fa1f1f0 --- /dev/null +++ b/src/main/res/drawable/video_play_btn_background.xml @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/video_progress.xml b/src/main/res/drawable/video_progress.xml new file mode 100755 index 0000000..74cb631 --- /dev/null +++ b/src/main/res/drawable/video_progress.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/src/main/res/drawable/west_side_rounded_button.xml b/src/main/res/drawable/west_side_rounded_button.xml new file mode 100755 index 0000000..dd29c6c --- /dev/null +++ b/src/main/res/drawable/west_side_rounded_button.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/layout/activity_change_password.xml b/src/main/res/layout/activity_change_password.xml new file mode 100755 index 0000000..c7ad16c --- /dev/null +++ b/src/main/res/layout/activity_change_password.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/layout/activity_circle_management.xml b/src/main/res/layout/activity_circle_management.xml new file mode 100755 index 0000000..dd8c66a --- /dev/null +++ b/src/main/res/layout/activity_circle_management.xml @@ -0,0 +1,15 @@ + + + + diff --git a/src/main/res/layout/activity_comments.xml b/src/main/res/layout/activity_comments.xml new file mode 100755 index 0000000..cd53e36 --- /dev/null +++ b/src/main/res/layout/activity_comments.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/res/layout/activity_crop_image.xml b/src/main/res/layout/activity_crop_image.xml new file mode 100755 index 0000000..84624b4 --- /dev/null +++ b/src/main/res/layout/activity_crop_image.xml @@ -0,0 +1,79 @@ + + + + +