Merge branch 'master' of ssh://float.beyondsoft.de/git/beyondSoft/Chat

This commit is contained in:
Waldi
2017-10-06 12:10:46 +02:00
347 changed files with 18054 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
*.iml
.gradle
/local.properties
.DS_Store
/.idea
/build
/captures
.externalNativeBuild

View File

@@ -0,0 +1 @@
/build

View File

@@ -0,0 +1,142 @@
def getGitSha() {
return 'git rev-parse --short HEAD'.execute([], project.rootDir).text.trim();
}
buildscript {
repositories {
mavenCentral()
maven { url 'https://maven.fabric.io/public' }
}
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
classpath 'io.fabric.tools:gradle:1.+'
}
}
apply plugin: 'com.android.application'
apply plugin: 'io.fabric'
repositories {
maven { url 'https://maven.fabric.io/public' }
maven { url "https://dl.bintray.com/hani-momanii/maven" }
maven { url "https://jitpack.io" }
}
apply plugin: 'com.neenbedankt.android-apt'
apply plugin: 'me.tatarka.retrolambda'
apply plugin: 'realm-android'
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
applicationId "com.p3.bewoplanner"
minSdkVersion 16
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
signingConfigs {
release {
storeFile file('../keystore.jks')
storePassword 'Qva/F637QEDfM)Z]'
keyAlias 'bewoplaner-chat'
keyPassword 'q7vWk(V&R6+NVjvg'
}
}
buildTypes {
debug {
ext.enableCrashlytics = false
}
release {
signingConfig signingConfigs.release
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
buildConfigField "String", "GIT_VERSION_SHA", "\"" + getGitSha() + "\""
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
lintOptions {
abortOnError false
disable 'InvalidPackage'
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
testCompile 'junit:junit:4.12'
// Android support libraries
compile 'com.android.support:appcompat-v7:25.3.0'
compile 'com.android.support:design:25.3.0'
// Butter Knife
compile 'com.jakewharton:butterknife:8.4.0'
apt 'com.jakewharton:butterknife-compiler:8.4.0'
// Dagger
compile 'com.google.dagger:dagger:2.7'
apt 'com.google.dagger:dagger-compiler:2.7'
provided 'javax.annotation:jsr250-api:1.0'
// Rx Java
compile 'io.reactivex:rxjava:1.1.6'
compile 'io.reactivex:rxandroid:1.2.1'
compile 'com.jakewharton.rxbinding:rxbinding:0.4.0'
// Retrofit
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
// OkHttp
compile 'com.squareup.okhttp3:okhttp:3.4.1'
compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
// Glide
compile 'com.github.bumptech.glide:glide:3.7.0'
// Circle ImageView
compile 'de.hdodenhof:circleimageview:2.1.0'
compile('com.crashlytics.sdk.android:crashlytics:2.6.6@aar') {
transitive = true;
}
// Secure Preferences
compile 'com.scottyab:secure-preferences-lib:0.1.4'
// Firebase
compile 'com.google.firebase:firebase-messaging:10.2.0'
// emojis
compile 'hani.momanii.supernova_emoji_library:supernova-emoji-library:0.0.2'
// circle progress
compile 'com.github.lzyzsd:circleprogress:1.2.1'
// event bus
compile 'org.greenrobot:eventbus:3.0.0'
}
task adp2upload(type: Exec, dependsOn: "build") {
ext {
channel = "bewoplaner-chat"
slug = "bewoplaner-chat"
versionCode = rootProject.versionCode
identifier = "com.p3.bewoplaner"
file = "build/outputs/apk/app-release.apk"
notes = "Nightly build for BeWoPlaner Chat"
}
commandLine 'upload_build.sh', "-c", "$channel", "-v", "$versionCode", "-p", "android", "-a", "$slug", "-f", "$file", "-i", "$identifier", "-n", "$notes"
}
apply plugin: 'com.google.gms.google-services'

View File

@@ -0,0 +1,3 @@
#Contains API Secret used to validate your application. Commit to internal source control; avoid making secret public.
#Fri Feb 24 13:59:15 EET 2017
apiSecret=29cdc918e050df28723e54a0bcd505ed7475be554b1ef8c4f8a30800f3e89a89

View File

@@ -0,0 +1,63 @@
{
"project_info": {
"project_number": "1021949586645",
"firebase_url": "https://bewoplaner-chat-android.firebaseio.com",
"project_id": "bewoplaner-chat-android",
"storage_bucket": "bewoplaner-chat-android.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:1021949586645:android:113c5b5fc97fb0bb",
"android_client_info": {
"package_name": "com.p3.bewoplanner"
}
},
"oauth_client": [
{
"client_id": "1021949586645-vnidi5tjof2kgsal6qgqrbokel0q0hvi.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.p3.bewoplanner",
"certificate_hash": "349feb470feaf535c1074c91721714ced3d81f0c"
}
},
{
"client_id": "1021949586645-ttle1pv465pp7gdigqh93futan4cmn8p.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.p3.bewoplanner",
"certificate_hash": "458abe7a8928c32f3ad3fbcc575373b613070aae"
}
},
{
"client_id": "1021949586645-lu3hc8hjsul10dhpj22o3k6vfcg6ol9v.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyAh9aJq_Hxjp9z9_Q3h5qNUg-l_Z4Grr98"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 2,
"other_platform_oauth_client": [
{
"client_id": "1021949586645-lu3hc8hjsul10dhpj22o3k6vfcg6ol9v.apps.googleusercontent.com",
"client_type": 3
}
]
},
"ads_service": {
"status": 2
}
}
}
],
"configuration_version": "1"
}

View File

@@ -0,0 +1,111 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/rerash/Documents/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 *;
#}
-keepattributes EnclosingMethod
### retrofit
-dontwarn retrofit2.**
-keep class retrofit2.** { *; }
-keepattributes Signature
-keepattributes Exceptions
# Okio
-keep class sun.misc.Unsafe { *; }
-dontwarn java.nio.file.*
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
-dontwarn okio.**
# OkHttp
-keepattributes Signature
-keepattributes *Annotation*
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-dontwarn okhttp3.**
### butterknife
-keep class butterknife.** { *; }
-dontwarn butterknife.compiler.**
-keep class **$$ViewBinder { *; }
-keepclasseswithmembernames class * {
@butterknife.* <fields>;
}
-keepclasseswithmembernames class * {
@butterknife.* <methods>;
}
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature
# For using GSON @Expose annotation
-keepattributes *Annotation*
-keepattributes EnclosingMethod
# Gson specific classes
-keep class sun.misc.Unsafe { *; }
-keep class com.google.gson.stream.** { *; }
# For Crashlytics
-keepattributes SourceFile,LineNumberTable
-keep class com.crashlytics.** { *; }
-dontwarn com.crashlytics.**
# For RxAndroid
-dontwarn rx.internal.util.unsafe.**
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
long producerIndex;
long consumerIndex;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
long producerNode;
long consumerNode;
}
# For retrolambda
-dontwarn java.lang.invoke.*
# auto
-dontwarn com.google.auto.**
-dontwarn com.squareup.javapoet.**
-dontwarn com.google.common.**
# EventBus
-keepattributes *Annotation*
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# Glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
-keep class com.bumptech.glide.integration.okhttp3.OkHttpGlideModule
# Realm
-keep class io.realm.annotations.RealmModule
-keep @io.realm.annotations.RealmModule class *
-keep class io.realm.internal.Keep
-keep @io.realm.internal.Keep class *
-dontwarn javax.**
-dontwarn io.realm.**

View File

@@ -0,0 +1,26 @@
package com.p3.bewoplaner;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumentation test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.p3.bewoplanner", appContext.getPackageName());
}
}

View File

@@ -0,0 +1,105 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.p3.bewoplaner">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:name="com.p3.bewoplaner.BewoplanerApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name="com.p3.bewoplaner.page.splash.SplashActivity"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.FullBackground">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".page.login.LoginActivity"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.FullBackground" />
<activity
android:name=".page.main.MainActivity"
android:launchMode="singleTask"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".page.impressum.ImpressumActivity"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".page.chat.SingleChatActivity"
android:label=""
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:theme="@style/ChatStyle" />
<activity
android:name=".page.chat.GroupChatActivity"
android:label=""
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:theme="@style/ChatStyle" />
<activity
android:name=".page.chatmembers.ChatMembersActivity"
android:label=""
android:screenOrientation="portrait"
android:theme="@style/ChatStyle" />
<meta-data
android:name="io.fabric.ApiKey"
android:value="1dcc8d0ead50f1b328778876834fea3392c6fc22" />
<service android:name=".data.firebase.MessagingInstanceIdService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>
<service android:name=".utils.AppStoppedService" android:stopWithTask="false"/>
<service android:name=".data.firebase.HandleMessagesService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
<receiver
android:name=".data.firebase.ReplyNotificationReceiver"
android:enabled="true"
android:exported="false" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/icon_notification_transparent" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/colorDefault" />
</application>
</manifest>

View File

@@ -0,0 +1,33 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!--<title>Terms</title>
-->
<style type="text/css">
body {margin:20px; font-family: "Roboto", Arial, "sans-serif"; font-size:17px; line-height:1.4em}
h1, h2, h3 {font-size:20px; color:#FF5E00; text-transform:uppercase; text-align:center;}
h2 {font-size:17px; color:#000; text-align:left;}
h3 {font-size:17px; color:#FF5E00; text-align:left;}
h4 {
font-size: 17px;
color: #FF5E00;
text-align: left;
text-transform: none;
margin-top: 40px;
margin-bottom: 20px;
}
a { color: #FF5E00;
}
</style>
</head>
<body >
<br>
<p><b>beyondSoft GmbH</b> | Hochstadenstr. 1-3 | 50674 Köln<br>fon <a href="tel:+49221606052-0">+49 221 606052-0</a></br> fax <a href="tel:+49221606052-29">+49 221 606052-29</a> <br> <a href="mailto:info@beyondsoft.de">info@beyondsoft.de</a><br>&nbsp;<br><b>Geschäftsführung</b> | <b>Mike Gahn</b><br>Christian Bäurle<br>Markus Bäurle<br>Kojo Bauer<br><br><b>Handelsregister</b> | Amtsgericht Köln, HRB 36720<br><br><b>Steuernummer</b> | DE214315054<br><br>Haftungsrechtliche Hinweise<br>beyondSoft prüft und aktualisiert die Informationen auf ihren Webseiten ständig. Trotz aller Sorgfalt können sich Daten und Informationen jeglicher Art inzwischen verändert haben. Eine Haftung, Garantie oder sonstiges Einstehen für die Aktualität, Richtigkeit und Vollständigkeit der zur Verfügung gestellten Informationen kann daher nicht übernommen werden. Gleiches gilt auch für alle Webseiten, auf die direkt mittels Hyperlink oder in sonstiger Weise verwiesen wird. BeyondSoft GmbH ist für den Inhalt der Webseiten, die aufgrund einer solchen Verbindung oder in sonstiger Weise erreicht werden, nicht verantwortlich. BeyondSoft GmbH übernimmt ausdrücklich keine Haftung gleich welcher Art. Dies gilt insbesondere für die Vertragshaftung, Deliktshaftung, Gefährdungshaftung oder sonstige Haftung für direkten oder indirekten Schadensersatz, Ersatz des beiläufig entstandenen Schadens, Strafen einschließlich Schadensersatz oder konkrete Schäden, die daraus resultieren oder in Zusammenhang damit stehen, dass Seiten der BeyondSoft GmbH aufgerufen, benutzt oder nicht benutzt werden können. Dies gilt auch bei einem Leistungsausfall, einer Unterbrechung, einem Defekt, einer Übertragungsverzögerung, einem Computervirus oder sonstigen schädlichen Elementen oder einem Leitungs- und Systemausfall in Zusammenhang mit den Webseiten der BeyondSoft GmbH, unabhängig davon, ob BeyondSoft GmbH sich der Möglichkeiten solcher Schäden bewusst war oder nicht. Des Weiteren behält sich BeyondSoft GmbH das Recht vor, jederzeit Änderungen oder Ergänzungen der bereitgestellten Informationen vorzunehmen.<br><br>Urheberrechtshinweise<br>Alle Texte, Bilder, Grafiken sowie das Layout unserer Webseiten unterliegen weltweitem Urheberrecht. Die unerlaubte Verwendung, Reproduktion oder Weitergabe einzelner Inhalte oder vollständiger Seiten werden sowohl straf- als auch zivilrechtlich verfolgt. Dabei sind nach deutschem Zivilrecht Unterlassung und Schadenersatz, Überlassung oder Vernichtung der Vervielfältigungsvorrichtungen sowie die öffentliche Bekanntmachung des Urteils gegen den Urheberrechtsverletzer möglich. Unterlassungsansprüche werden vom Gericht mit Ordnungsgeldern bis zu Euro 250.000,00 oder Ordnungshaft bis zu sechs Monaten abgesichert. Bei strafrechtlicher Verfolgung drohen dem Verletzer im Einzelfall Freiheitsstrafen bis zu fünf Jahren. Die Rechtsverfolgung nach den Gesetzgebungen anderer Länder wird hiermit angedroht und kann noch schärfere Konsequenzen nach sich ziehen. Die Zitierung von Inhalten ist gem. § 51 UrhG nur zulässig, soweit sie zur Erläuterung eigener Ausführungen dient und diese nicht ersetzt. Bei Zitaten ist gem. § 63 UrhG als Quelle unser Impressum anzugeben.</p></body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,51 @@
package com.p3.bewoplaner;
import com.crashlytics.android.Crashlytics;
import com.p3.bewoplaner.data.firebase.MessagingInstanceIdService;
import com.p3.bewoplaner.di.ApplicationComponent;
import com.p3.bewoplaner.di.ApplicationModule;
import com.p3.bewoplaner.di.DaggerApplicationComponent;
import com.p3.bewoplaner.di.InjectionHelper;
import android.app.Application;
import io.fabric.sdk.android.Fabric;
import io.realm.Realm;
/**
* Created by imarneanu on 2/9/17.
*/
public class BewoplanerApplication extends Application {
private ApplicationComponent mApplicationComponent;
@Override
public void onCreate() {
super.onCreate();
Realm.init(this);
if (!BuildConfig.DEBUG) {
Fabric.with(this, new Crashlytics());
}
buildGraphAndInject();
MessagingInstanceIdService.setApplicationContext(this);
}
private void buildGraphAndInject() {
final ApplicationModule applicationModule = new ApplicationModule(this);
mApplicationComponent = DaggerApplicationComponent.builder()
.applicationModule(applicationModule)
.build();
mApplicationComponent.inject(this);
}
public ApplicationComponent getApplicationComponent() {
return mApplicationComponent;
}
public void resetApplicationComponent() {
mApplicationComponent = null;
InjectionHelper.destroyMainComponents();
buildGraphAndInject();
}
}

View File

@@ -0,0 +1,18 @@
package com.p3.bewoplaner.data;
/**
* Created by imarneanu on 2/10/17.
*/
public interface AppPrefsConstants {
String LOGIN_TOKEN = "login_token";
String SAVE_PASSWORD = "save_password";
String API_ENDPOINT = "api_endpoint";
String CHAT_CODE = "chat_code";
String ACCOUNT_NO = "account_no";
String USER_ID = "user_id";
String AVATAR = "avatar";
String NAME = "name";
String PHONE_NUMBER = "phone_number";
String CHAT_OPENED = "chat_opened";
String APP_KILLED = "app_killed";
}

View File

@@ -0,0 +1,154 @@
package com.p3.bewoplaner.data.api;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.p3.bewoplaner.BewoplanerApplication;
import com.p3.bewoplaner.BuildConfig;
import com.p3.bewoplaner.data.AppPrefsConstants;
import com.p3.bewoplaner.data.prefs.StringPreference;
import com.p3.bewoplaner.di.P3DSEndpoint;
import com.p3.bewoplaner.di.VoucherEndpoint;
import com.p3.bewoplaner.di.scopes.ApplicationScope;
import com.p3.bewoplaner.utils.Constants;
import java.io.File;
import java.util.concurrent.TimeUnit;
import javax.inject.Named;
import dagger.Module;
import dagger.Provides;
import okhttp3.Cache;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.CallAdapter;
import retrofit2.Converter;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import rx.schedulers.Schedulers;
/**
* Created by laszloffiferenc on 2/21/17.
*/
@Module
public class ApiModule {
@Provides
@ApplicationScope
Cache provideCache(BewoplanerApplication app) {
File httpCacheDirectory = new File(app.getCacheDir().getAbsolutePath(), "HttpCache");
return new Cache(httpCacheDirectory, Constants.DISK_CACHE_SIZE);
}
@Provides
@ApplicationScope
HttpLoggingInterceptor provideLoggingInterceptor() {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
if (BuildConfig.DEBUG) {
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
} else {
interceptor.setLevel(HttpLoggingInterceptor.Level.NONE);
}
return interceptor;
}
@Provides
@ApplicationScope
Interceptor provideHeaderInterceptor(@Named(AppPrefsConstants.LOGIN_TOKEN) final StringPreference token, @Named(AppPrefsConstants.ACCOUNT_NO) final StringPreference customerId) {
return chain -> {
final boolean isHeaderSet = token.isSet() || customerId.isSet();
Request original = chain.request();
Request.Builder builder = original.newBuilder();
if (token.isSet()) {
builder.addHeader("Token", token.get());
}
if (customerId.isSet()) {
builder.addHeader("CustomerID", customerId.get());
}
if (isHeaderSet) {
return chain.proceed(builder.build());
}
return chain.proceed(original.newBuilder().build());
};
}
@Provides
@ApplicationScope
@P3DSEndpoint
OkHttpClient provideP3DSOkHttpClient(Cache cache,
HttpLoggingInterceptor loggingInterceptor,
Interceptor interceptor) {
return new OkHttpClient.Builder()
.cache(cache)
.connectTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addInterceptor(loggingInterceptor)
.addInterceptor(interceptor)
.build();
}
@Provides
@ApplicationScope
@VoucherEndpoint
OkHttpClient provideVoucherkHttpClient(Cache cache,
HttpLoggingInterceptor loggingInterceptor) {
return new OkHttpClient.Builder()
.cache(cache)
.addInterceptor(loggingInterceptor)
.build();
}
@Provides
@ApplicationScope
CallAdapter.Factory provideCallAdapterFactory() {
return RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io());
}
@Provides
@ApplicationScope
Gson provideGson() {
return new GsonBuilder()
.setDateFormat("yyyy-MM-dd HH:mm:ss")
.create();
}
@Provides
@ApplicationScope
Converter.Factory provideConverterFactory(Gson gson) {
return GsonConverterFactory.create(gson);
}
@Provides
@ApplicationScope
@P3DSEndpoint
Retrofit provideP3DSRetrofit(@P3DSEndpoint final OkHttpClient okHttpClient,
@Named(AppPrefsConstants.API_ENDPOINT) final StringPreference endpoint,
final Converter.Factory converter,
final CallAdapter.Factory adapterFactory) {
return new Retrofit.Builder()
.baseUrl(endpoint.get() == null ? Constants.BEWOPLANER_ENDPOINT : endpoint.get())
.addCallAdapterFactory(adapterFactory)
.addConverterFactory(converter)
.client(okHttpClient)
.build();
}
@Provides
@ApplicationScope
@VoucherEndpoint
Retrofit provideVoucherRetrofit(@VoucherEndpoint final OkHttpClient okHttpClient,
final Converter.Factory converter,
final CallAdapter.Factory adapterFactory) {
return new Retrofit.Builder()
.baseUrl(Constants.VOUCHER_ENDPOINT)
.addCallAdapterFactory(adapterFactory)
.addConverterFactory(converter)
.client(okHttpClient)
.build();
}
}

View File

@@ -0,0 +1,28 @@
package com.p3.bewoplaner.data.api.common;
import com.p3.bewoplaner.data.events.InvalidTokenEvent;
import com.p3.bewoplaner.model.common.BaseResponse;
import org.greenrobot.eventbus.EventBus;
import rx.Observable;
import rx.functions.Func1;
/**
* Created by laszloffiferenc on 2/21/17.
*/
public class ErrorHandlingResponseConverter<T extends BaseResponse>
implements Func1<T, Observable<T>> {
@Override
public Observable<T> call(T t) {
if (!t.success) {
if (t.errors.token != null) {
EventBus.getDefault().post(new InvalidTokenEvent());
return Observable.error(new ErrorThrowable(ErrorType.INVALID_TOKEN, t.errors));
}
return Observable.error(new ErrorThrowable(ErrorType.DATA_INPUT, t.errors));
}
return Observable.just(t);
}
}

View File

@@ -0,0 +1,27 @@
package com.p3.bewoplaner.data.api.common;
import com.p3.bewoplaner.model.common.BaseResponse;
/**
* Created by laszloffiferenc on 2/21/17.
*/
public class ErrorThrowable extends RuntimeException {
private final ErrorType mErrorType;
private final BaseResponse.Error mError;
ErrorThrowable(ErrorType errorType, BaseResponse.Error error) {
super();
mError = error;
mErrorType = errorType;
}
public BaseResponse.Error getError() {
return mError;
}
public ErrorType getErrorType() {
return mErrorType;
}
}

View File

@@ -0,0 +1,22 @@
package com.p3.bewoplaner.data.api.common;
/**
* Created by laszloffiferenc on 2/21/17.
*/
public enum ErrorType {
/**
* Unexpected/unknown error
*/
UNKNOWN,
/**
* Wrong query parameters
*/
DATA_INPUT,
/**
* Expired/invalid token
*/
INVALID_TOKEN
}

View File

@@ -0,0 +1,20 @@
package com.p3.bewoplaner.data.api.common;
/**
* Created by laszloffiferenc on 2/23/17.
*/
public class ErrorUtils {
private ErrorUtils() {
}
public static ErrorType getErrorType(Throwable e) {
ErrorType errorType;
if (e instanceof ErrorThrowable) {
errorType = ((ErrorThrowable) e).getErrorType();
} else {
errorType = ErrorType.UNKNOWN;
}
return errorType;
}
}

View File

@@ -0,0 +1,21 @@
package com.p3.bewoplaner.data.api.responses;
import com.google.gson.annotations.SerializedName;
import com.p3.bewoplaner.model.Group;
import com.p3.bewoplaner.model.common.BaseResponse;
import java.util.ArrayList;
/**
* Created by imarneanu on 2/28/17.
*/
public class GroupResponse extends BaseResponse {
@SerializedName("response")
public Response response;
public static class Response {
@SerializedName("groups")
public ArrayList<Group> groups;
}
}

View File

@@ -0,0 +1,21 @@
package com.p3.bewoplaner.data.api.responses;
import com.google.gson.annotations.SerializedName;
import com.p3.bewoplaner.model.common.BaseResponse;
/**
* Created by Rares Teodorescu on 08/03/2017.
*/
public class MessageResponse extends BaseResponse {
@SerializedName("response")
public Response response;
public static class Response {
@SerializedName("message")
public com.p3.bewoplaner.model.Message message;
}
}

View File

@@ -0,0 +1,25 @@
package com.p3.bewoplaner.data.api.responses;
import com.google.gson.annotations.SerializedName;
import com.p3.bewoplaner.model.common.BaseResponse;
import java.util.ArrayList;
/**
* Created by imarneanu on 3/1/17.
*/
public class MessagesResponse extends BaseResponse {
@SerializedName("response")
public Response response;
public static class Response {
@SerializedName("messages")
public ArrayList<com.p3.bewoplaner.model.Message> messages;
@SerializedName("hasMorePages")
public boolean hasMorePages;
}
}

View File

@@ -0,0 +1,21 @@
package com.p3.bewoplaner.data.api.responses;
import com.google.gson.annotations.SerializedName;
import com.p3.bewoplaner.model.common.BaseResponse;
import com.p3.bewoplaner.model.User;
/**
* Created by imarneanu on 2/23/17.
*/
public class ProfileResponse extends BaseResponse {
@SerializedName("response")
public Response response;
public static class Response {
@SerializedName("user")
public User user;
}
}

View File

@@ -0,0 +1,14 @@
package com.p3.bewoplaner.data.api.responses;
import com.google.gson.annotations.SerializedName;
import com.p3.bewoplaner.model.common.BaseResponse;
/**
* Created by laszloffiferenc on 2/28/17.
*/
public class RegisterResponse extends BaseResponse {
@SerializedName("response")
public String response;
}

View File

@@ -0,0 +1,20 @@
package com.p3.bewoplaner.data.api.responses;
import com.google.gson.annotations.SerializedName;
import com.p3.bewoplaner.model.common.BaseResponse;
/**
* Created by Vlad on 28/02/2017.
*/
public class UploadImageResponse extends BaseResponse {
@SerializedName("response")
public Response response;
public static class Response {
@SerializedName("picture")
public String picture;
}
}

View File

@@ -0,0 +1,43 @@
package com.p3.bewoplaner.data.api.services;
import com.p3.bewoplaner.data.api.responses.GroupResponse;
import com.p3.bewoplaner.data.api.responses.MessageResponse;
import com.p3.bewoplaner.data.api.responses.MessagesResponse;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
import retrofit2.http.Query;
import rx.Observable;
/**
* Created by imarneanu on 3/1/17.
*/
public interface ChatService {
@GET("api/chat/groups")
Observable<GroupResponse> getGroups();
@GET("api/chat/messages")
Observable<MessagesResponse> getMessages(@Query("groupid") long groupId,
@Query("page") int page);
@Multipart
@POST("api/chat/messages/send")
Observable<MessageResponse> sendFile(
@Part("groupid") RequestBody groupid,
@Part MultipartBody.Part file,
@Part("text") RequestBody text
);
@FormUrlEncoded
@POST("api/chat/messages/send")
Observable<MessageResponse> sendMessage(
@Field("groupid") long groupId,
@Field("text") String text);
}

View File

@@ -0,0 +1,20 @@
package com.p3.bewoplaner.data.api.services;
import com.p3.bewoplaner.data.api.responses.ProfileResponse;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.POST;
import rx.Observable;
/**
* Created by laszloffiferenc on 2/21/17.
*/
public interface LoginService {
@FormUrlEncoded
@POST("api/user/login")
Observable<ProfileResponse> login(
@Field("username") String username,
@Field("password") String password
);
}

View File

@@ -0,0 +1,29 @@
package com.p3.bewoplaner.data.api.services;
import com.p3.bewoplaner.data.api.responses.ProfileResponse;
import com.p3.bewoplaner.data.api.responses.UploadImageResponse;
import okhttp3.MultipartBody;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
import rx.Observable;
/**
* Created by imarneanu on 2/23/17.
*/
public interface ProfileService {
@GET("api/user/profile")
Observable<ProfileResponse> getProfile();
@FormUrlEncoded
@POST("api/user/profile")
Observable<ProfileResponse> updateProfile(@Field("mobile") String phoneNumber);
@POST("api/user/profile/image")
@Multipart
Observable<UploadImageResponse> uploadProfileImage(@Part MultipartBody.Part filePart);
}

View File

@@ -0,0 +1,25 @@
package com.p3.bewoplaner.data.api.services;
import com.p3.bewoplaner.data.api.responses.RegisterResponse;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Query;
import rx.Observable;
/**
* Created by laszloffiferenc on 3/2/17.
*/
public interface RegisterService {
@FormUrlEncoded
@POST("api/user/register-device")
Observable<RegisterResponse> registerDevice(
@Field("device_id") String deviceId,
@Field("fcm_token") String token
);
@GET("api/user/logout")
Observable<RegisterResponse> unregisterDevice(@Query("device_id") String deviceId);
}

View File

@@ -0,0 +1,19 @@
package com.p3.bewoplaner.data.api.services;
import com.google.gson.JsonObject;
import retrofit2.http.GET;
import retrofit2.http.Query;
import rx.Observable;
/**
* Created by Rares Teodorescu on 24/03/2017.
*/
public interface VoucherService {
@GET("voucher_check.php")
Observable<JsonObject> checkVoucher(
@Query("k") String customerId,
@Query("Voucher") String voucher);
}

View File

@@ -0,0 +1,7 @@
package com.p3.bewoplaner.data.events;
/**
* Created by Rares Teodorescu on 31/03/2017.
*/
public class GroupUpdatedEvent {
}

View File

@@ -0,0 +1,7 @@
package com.p3.bewoplaner.data.events;
/**
* Created by Rares Teodorescu on 27/03/2017.
*/
public class InvalidTokenEvent {
}

View File

@@ -0,0 +1,4 @@
package com.p3.bewoplaner.data.events;
public class LogoutDirectlyEvent {
}

View File

@@ -0,0 +1,266 @@
package com.p3.bewoplaner.data.firebase;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
import com.p3.bewoplaner.R;
import com.p3.bewoplaner.data.AppPrefsConstants;
import com.p3.bewoplaner.data.prefs.LongPreference;
import com.p3.bewoplaner.data.repository.GroupRealmRepository;
import com.p3.bewoplaner.data.repository.MessageRealmRepository;
import com.p3.bewoplaner.data.repository.specifications.GroupByIdSpecification;
import com.p3.bewoplaner.data.repository.specifications.MessageByIdSpecification;
import com.p3.bewoplaner.di.InjectionHelper;
import com.p3.bewoplaner.model.Group;
import com.p3.bewoplaner.model.Message;
import com.p3.bewoplaner.page.main.MainActivity;
import com.p3.bewoplaner.utils.Constants;
import com.p3.bewoplaner.utils.LoggedOutUtils;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
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.os.Build;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.RemoteInput;
import android.support.v4.content.ContextCompat;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Named;
import static android.graphics.BitmapFactory.decodeStream;
import static com.p3.bewoplaner.utils.Constants.CHAT_IS_ALREADY_OPEN;
/**
* Created by laszloffiferenc on 3/1/17.
*/
public class HandleMessagesService extends FirebaseMessagingService {
@Inject
MessageRealmRepository mMessageRealmRepository;
@Inject
GroupRealmRepository mGroupRealmRepository;
@Inject
@Named(AppPrefsConstants.USER_ID)
LongPreference mUserIdReference;
@Inject
LoggedOutUtils mLoggedOutUtils;
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
InjectionHelper.getMessagingComponent(this).inject(HandleMessagesService.this);
if (mLoggedOutUtils.checkLoggedOutPrefs()) {
return;
}
customizeNotification(remoteMessage.getData());
}
private static Bitmap getBitmapFromUrl(String pictureUrl) {
try {
URL url = new URL(pictureUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
Bitmap bitmap = decodeStream(connection.getInputStream());
return Bitmap.createScaledBitmap(bitmap, 240, 240, false);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private void customizeNotification(Map<String, String> data) {
Bundle bundle = new Bundle();
bundle.putString(Constants.MESSAGE_ID, data.get(Constants.MESSAGE_ID));
bundle.putLong(Constants.GROUP_ID, Long.parseLong(data.get(Constants.GROUP_ID)));
bundle.putLong(Constants.CREATED_AT, Long.parseLong(data.get(Constants.CREATED_AT)));
bundle.putString(Constants.MESSAGE_TEXT, data.get(Constants.MESSAGE_TEXT));
bundle.putString(Constants.FILE, data.get(Constants.FILE));
bundle.putString(Constants.SMALLER_IMAGE, data.get(Constants.SMALLER_IMAGE));
bundle.putString(Constants.FILENAME, data.get(Constants.FILENAME));
bundle.putLong(Constants.USER_ID, Long.parseLong(data.get(Constants.USER_ID)));
bundle.putString(Constants.USERNAME, data.get(Constants.USERNAME));
bundle.putString(Constants.USER_AVATAR, data.get(Constants.USER_AVATAR));
final Bitmap bitmap = getBitmapFromUrl(data.get(Constants.USER_AVATAR));
Intent intent = new Intent();
intent.putExtra(Constants.MESSAGE, bundle);
intent.setAction(Constants.NOTIFICATION_BROADCAST);
sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Bundle results = getResultExtras(true);
if (!results.getBoolean(CHAT_IS_ALREADY_OPEN, false)) {
int groupId = (int) bundle.getLong(Constants.GROUP_ID);
int numberOfUsers = Integer.parseInt(data.get(Constants.GROUP_NR_USERS));
intent = MainActivity.createIntent(context, bundle, numberOfUsers, groupId);
PendingIntent pendingIntent = PendingIntent.getActivity(context,
groupId,
intent,
PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Action action = createAction(context, numberOfUsers, groupId, bundle);
Bitmap bitmapToDisplay = bitmap;
if (bitmapToDisplay == null) {
bitmapToDisplay = BitmapFactory.decodeResource(context.getResources(), R.drawable.avatar);
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setSmallIcon(getNotificationIcon())
.setContentTitle(setNotificationTitle(data, numberOfUsers))
.setContentText(getTextFromNotification(data))
.setAutoCancel(true)
.setStyle(new NotificationCompat.BigTextStyle().bigText(getTextFromNotification(data)))
.setPriority(Notification.PRIORITY_MAX)
.setContentIntent(pendingIntent)
.setLargeIcon(getCircleBitmap(bitmapToDisplay))
.setGroup(String.valueOf(groupId))
.setGroupSummary(true)
.setDefaults(Notification.DEFAULT_ALL)
.setColor(ContextCompat.getColor(context, R.color.colorDefaultBright));
if (action != null) {
builder.addAction(action);
}
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(groupId, builder.build());
Message message = new Message(bundle.getString(Constants.MESSAGE_ID),
groupId,
bundle.getLong(Constants.CREATED_AT),
bundle.getString(Constants.MESSAGE_TEXT),
bundle.getString(Constants.FILE),
bundle.getString(Constants.SMALLER_IMAGE),
bundle.getString(Constants.FILENAME),
null,
bundle.getLong(Constants.USER_ID),
bundle.getString(Constants.USERNAME),
bundle.getString(Constants.USER_AVATAR),
Message.Status.DELIVERED,
false,
mUserIdReference.get(),
false);
storeMessageLocally(message);
storeGroupLocally(groupId, message);
}
}
}, null, Activity.RESULT_OK, null, null);
}
private Bitmap getCircleBitmap(Bitmap bitmap) {
final Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
bitmap.getHeight(), Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(output);
final int color = Color.RED;
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
final RectF rectF = new RectF(rect);
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawOval(rectF, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
bitmap.recycle();
return output;
}
private void storeGroupLocally(long groupId, Message message) {
Group group = new Group();
group.id = groupId;
group.lastMessage = message;
mGroupRealmRepository.updateGroupMessage(group, new GroupByIdSpecification(mUserIdReference.get())).subscribe();
}
private void storeMessageLocally(Message message) {
MessageByIdSpecification messageByIdSpecification = new MessageByIdSpecification(mUserIdReference.get());
messageByIdSpecification.setMessageId(message.id);
mMessageRealmRepository.update(Collections.singletonList(message), messageByIdSpecification).subscribe();
}
private String setNotificationTitle(Map<String, String> data, int numberOfUsers) {
if (numberOfUsers > 2) {
return String.valueOf(data.get(Constants.USERNAME) + " @ " + data.get(Constants.GROUP_NAME));
} else {
return String.valueOf(data.get(Constants.USERNAME));
}
}
private NotificationCompat.Action createAction(Context context, int noOfUsers, int groupId, Bundle bundle) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
return null;
}
Intent replyIntent = ReplyNotificationReceiver.createReplyIntent(context, bundle, noOfUsers, groupId);
PendingIntent replyPendingIntent = PendingIntent.getBroadcast(getApplicationContext(),
groupId,
replyIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
String replyLabel = getResources().getString(R.string.chat_reply_label);
RemoteInput remoteInput = new RemoteInput.Builder(Constants.KEY_TEXT_REPLY)
.setLabel(replyLabel)
.build();
return new NotificationCompat.Action.Builder(R.drawable.send_enabled,
getString(R.string.chat_reply_label), replyPendingIntent)
.addRemoteInput(remoteInput)
.build();
}
private String getTextFromNotification(Map<String, String> data) {
String message = null;
if (data.get(Constants.MESSAGE_TEXT) != null) {
message = String.valueOf(data.get(Constants.MESSAGE_TEXT));
}
if (!data.get(Constants.FILE).equals("")) {
message = getString(R.string.chat_file_received);
}
return message;
}
public static int getNotificationIcon() {
boolean useWhiteIcon = (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP);
return useWhiteIcon ? R.drawable.icon_notification_transparent : R.drawable.notification_color;
}
public static CharSequence getReplyMessage(Intent intent) {
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
if (remoteInput != null) {
return remoteInput.getCharSequence(Constants.KEY_TEXT_REPLY);
}
return null;
}
}

View File

@@ -0,0 +1,21 @@
package com.p3.bewoplaner.data.firebase;
import com.p3.bewoplaner.di.ApplicationComponent;
import com.p3.bewoplaner.di.scopes.ActivityScope;
import com.p3.bewoplaner.page.chat.ChatModule;
import com.p3.bewoplaner.page.login.LoginModule;
import dagger.Component;
/**
* Created by laszloffiferenc on 3/2/17.
*/
@ActivityScope
@Component(dependencies = ApplicationComponent.class, modules = {LoginModule.class, ChatModule.class})
public interface MessagingComponent {
void inject(MessagingInstanceIdService messagingInstanceIdService);
void inject(HandleMessagesService handleMessagesService);
void inject(ReplyNotificationReceiver replyNotificationReceiver);
}

View File

@@ -0,0 +1,62 @@
package com.p3.bewoplaner.data.firebase;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.FirebaseInstanceIdService;
import com.p3.bewoplaner.BewoplanerApplication;
import com.p3.bewoplaner.data.api.responses.RegisterResponse;
import com.p3.bewoplaner.data.api.services.RegisterService;
import com.p3.bewoplaner.di.InjectionHelper;
import android.provider.Settings;
import android.util.Log;
import javax.inject.Inject;
import rx.Subscriber;
import rx.schedulers.Schedulers;
/**
* Created by laszloffiferenc on 3/1/17.
*/
public class MessagingInstanceIdService extends FirebaseInstanceIdService {
private static String TAG = "messaging";
private static BewoplanerApplication sAndroidContext;
@Inject
RegisterService mRegisterService;
public static void setApplicationContext(BewoplanerApplication androidContext) {
sAndroidContext = androidContext;
}
@Override
public void onTokenRefresh() {
InjectionHelper.getMessagingComponent(sAndroidContext).inject(this);
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Refreshed token: " + refreshedToken);
sendRegistrationToServer(refreshedToken);
}
private void sendRegistrationToServer(String token) {
mRegisterService.registerDevice(Settings.Secure.getString(sAndroidContext.getContentResolver(), Settings.Secure.ANDROID_ID), token)
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<RegisterResponse>() {
@Override
public void onCompleted() {
Log.d(TAG, "onCompleted: " + token);
}
@Override
public void onError(Throwable e) {
Log.d(TAG, "onError: ");
}
@Override
public void onNext(RegisterResponse registerResponse) {
Log.d(TAG, "onNext: ");
}
});
}
}

View File

@@ -0,0 +1,182 @@
package com.p3.bewoplaner.data.firebase;
import com.p3.bewoplaner.R;
import com.p3.bewoplaner.data.AppPrefsConstants;
import com.p3.bewoplaner.data.api.common.ErrorHandlingResponseConverter;
import com.p3.bewoplaner.data.api.responses.MessageResponse;
import com.p3.bewoplaner.data.api.services.ChatService;
import com.p3.bewoplaner.data.prefs.LongPreference;
import com.p3.bewoplaner.data.prefs.StringPreference;
import com.p3.bewoplaner.data.repository.GroupRealmRepository;
import com.p3.bewoplaner.data.repository.MessageRealmRepository;
import com.p3.bewoplaner.data.repository.RealmProvider;
import com.p3.bewoplaner.data.repository.specifications.GroupByIdSpecification;
import com.p3.bewoplaner.data.repository.specifications.MessageByIdSpecification;
import com.p3.bewoplaner.di.InjectionHelper;
import com.p3.bewoplaner.model.Group;
import com.p3.bewoplaner.model.Message;
import com.p3.bewoplaner.page.chat.ChatPresenter;
import com.p3.bewoplaner.utils.Constants;
import com.p3.bewoplaner.utils.rx.RxUtils;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v4.content.ContextCompat;
import android.widget.Toast;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Named;
import rx.Observable;
import rx.Subscriber;
import static com.p3.bewoplaner.utils.Constants.GROUP_ID;
/**
* Created by laszloffiferenc on 3/20/17.
*/
public class ReplyNotificationReceiver extends BroadcastReceiver {
private static final String BUNDLE = "bundle";
@Inject
ChatService mChatService;
@Inject
@Named(AppPrefsConstants.USER_ID)
LongPreference mUserIdPreference;
@Inject
@Named(AppPrefsConstants.AVATAR)
StringPreference mAvatarPreference;
@Inject
@Named(AppPrefsConstants.NAME)
StringPreference mProfileNamePreference;
@Inject
RealmProvider mRealmProvider;
@Inject
MessageRealmRepository mMessageRealmRepository;
@Inject
GroupRealmRepository mGroupRealmRepository;
@Inject
@Named(AppPrefsConstants.USER_ID)
LongPreference mUserIdReference;
public ReplyNotificationReceiver() {
}
public static Intent createReplyIntent(Context context, Bundle bundle, int numberOfUsers, int groupId) {
Intent intent = new Intent(context, ReplyNotificationReceiver.class);
intent.putExtra(GROUP_ID, groupId);
intent.putExtra(BUNDLE, bundle);
intent.putExtra(Constants.GROUP_NR_USERS, numberOfUsers);
return intent;
}
@Override
public void onReceive(Context context, Intent intent) {
InjectionHelper.getMessagingComponent(context).inject(this);
CharSequence replyMessage = HandleMessagesService.getReplyMessage(intent);
Bundle bundle = intent.getBundleExtra(BUNDLE);
long groupId = bundle.getLong(Constants.GROUP_ID);
if (replyMessage != null && replyMessage instanceof String) {
sendMessage(context, (String) replyMessage, (int) groupId);
}
}
private void updateNotificationOnReply(Context context, String replyMessage, int groupId) {
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setSmallIcon(HandleMessagesService.getNotificationIcon())
.setColor(ContextCompat.getColor(context, R.color.colorDefaultBright))
.setContentTitle(context.getResources().getString(R.string.chat_you_name))
.setContentText(replyMessage);
notificationManager.notify(groupId, builder.build());
Observable.timer(2, TimeUnit.SECONDS).subscribe(aLong -> notificationManager.cancel(groupId));
}
public void sendMessage(Context context, String replyMessage, int groupId) {
mChatService.sendMessage(groupId, replyMessage)
.compose(RxUtils.provideDefaultTransformer())
.flatMap(new ErrorHandlingResponseConverter<>())
.subscribe(new Subscriber<MessageResponse>() {
@Override
public void onCompleted() {
updateNotificationOnReply(context, replyMessage, groupId);
}
@Override
public void onError(Throwable e) {
Message message = new Message(ChatPresenter.findUniqueRandomId(mRealmProvider, mUserIdPreference.get()),
groupId,
System.currentTimeMillis() / 1000,
replyMessage,
null,
null,
null,
null,
mUserIdPreference.get(),
mProfileNamePreference.get(),
mAvatarPreference.get(),
Message.Status.FAILED,
true,
mUserIdPreference.get(),
false);
storeMessageLocally(message);
storeGroupLocally(groupId, message);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
notificationManager.cancel(groupId);
Toast.makeText(context, R.string.error_message_not_sent, Toast.LENGTH_SHORT).show();
}
@Override
public void onNext(MessageResponse baseResponse) {
Message message = baseResponse.response.message;
message.status = Message.Status.DELIVERED;
storeMessageLocally(message);
storeGroupLocally(groupId, message);
Bundle bundle = new Bundle();
bundle.putString(Constants.MESSAGE_ID, message.id);
bundle.putLong(Constants.GROUP_ID, message.groupId);
bundle.putLong(Constants.CREATED_AT, message.createdAt);
bundle.putString(Constants.MESSAGE_TEXT, message.message);
bundle.putString(Constants.FILE, message.filePath);
bundle.putLong(Constants.USER_ID, message.senderId);
bundle.putString(Constants.USERNAME, message.senderName);
bundle.putString(Constants.USER_AVATAR, message.senderAvatar);
Intent intent = new Intent();
intent.putExtra(Constants.MESSAGE, bundle);
intent.setAction(Constants.NOTIFICATION_BROADCAST);
context.sendBroadcast(intent);
}
});
}
private void storeGroupLocally(long groupId, Message message) {
Group group = new Group();
group.id = groupId;
group.lastMessage = message;
mGroupRealmRepository.updateGroupMessage(group, new GroupByIdSpecification(mUserIdPreference.get())).subscribe();
}
private void storeMessageLocally(Message message) {
MessageByIdSpecification messageByIdSpecification = new MessageByIdSpecification(mUserIdPreference.get());
messageByIdSpecification.setMessageId(message.id);
mMessageRealmRepository.update(Collections.singletonList(message), messageByIdSpecification)
.subscribe();
}
}

View File

@@ -0,0 +1,14 @@
package com.p3.bewoplaner.data.prefs;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Qualifier;
/**
* Created by imarneanu on 2/10/17.
*/
@Qualifier
@Retention(RetentionPolicy.SOURCE)
public @interface AppPrefs {
}

View File

@@ -0,0 +1,113 @@
package com.p3.bewoplaner.data.prefs;
import com.p3.bewoplaner.BewoplanerApplication;
import com.p3.bewoplaner.R;
import com.p3.bewoplaner.data.AppPrefsConstants;
import com.p3.bewoplaner.di.scopes.ApplicationScope;
import com.securepreferences.SecurePreferences;
import android.content.Context;
import android.content.SharedPreferences;
import javax.inject.Named;
import dagger.Module;
import dagger.Provides;
/**
* Created by imarneanu on 2/10/17.
*/
@Module
public class AppPrefsModule {
@Provides
@ApplicationScope
@AppPrefs
SharedPreferences provideSharedPreferences(BewoplanerApplication app) {
return app.getSharedPreferences(app.getString(R.string.app_name), Context.MODE_PRIVATE);
}
@Provides
@ApplicationScope
@Named(AppPrefsConstants.API_ENDPOINT)
StringPreference provideApiEndpoint(@AppPrefs SharedPreferences sharedPreferences) {
return new StringPreference(sharedPreferences, AppPrefsConstants.API_ENDPOINT);
}
@Provides
@ApplicationScope
@AppPrefs
SecurePreferences provideSecuredPrefs(BewoplanerApplication application) {
return new SecurePreferences(application, "", "my_prefs_1000.xml");
}
@Provides
@ApplicationScope
@Named(AppPrefsConstants.LOGIN_TOKEN)
StringPreference provideLoginToken(@AppPrefs SharedPreferences sharedPreferences) {
return new StringPreference(sharedPreferences, AppPrefsConstants.LOGIN_TOKEN);
}
@Provides
@ApplicationScope
@Named(AppPrefsConstants.SAVE_PASSWORD)
BooleanPreference provideSavePassword(@AppPrefs SecurePreferences sharedPreferences) {
return new BooleanPreference(sharedPreferences, AppPrefsConstants.SAVE_PASSWORD);
}
@Provides
@ApplicationScope
@Named(AppPrefsConstants.CHAT_CODE)
StringPreference provideChatCode(@AppPrefs SecurePreferences sharedPreferences) {
return new StringPreference(sharedPreferences, AppPrefsConstants.CHAT_CODE);
}
@Provides
@ApplicationScope
@Named(AppPrefsConstants.ACCOUNT_NO)
StringPreference provideAccountNumber(@AppPrefs SecurePreferences sharedPreferences) {
return new StringPreference(sharedPreferences, AppPrefsConstants.ACCOUNT_NO);
}
@Provides
@ApplicationScope
@Named(AppPrefsConstants.USER_ID)
LongPreference provideUserId(@AppPrefs SharedPreferences sharedPreferences) {
return new LongPreference(sharedPreferences, AppPrefsConstants.USER_ID);
}
@Provides
@ApplicationScope
@Named(AppPrefsConstants.AVATAR)
StringPreference provideAvatar(@AppPrefs SharedPreferences sharedPreferences) {
return new StringPreference(sharedPreferences, AppPrefsConstants.AVATAR);
}
@Provides
@ApplicationScope
@Named(AppPrefsConstants.NAME)
StringPreference provideProfileName(@AppPrefs SharedPreferences sharedPreferences) {
return new StringPreference(sharedPreferences, AppPrefsConstants.NAME);
}
@Provides
@ApplicationScope
@Named(AppPrefsConstants.PHONE_NUMBER)
StringPreference providePhoneNumber(@AppPrefs SharedPreferences sharedPreferences) {
return new StringPreference(sharedPreferences, AppPrefsConstants.PHONE_NUMBER);
}
@Provides
@ApplicationScope
@Named(AppPrefsConstants.CHAT_OPENED)
BooleanPreference provideChatOpened(@AppPrefs SharedPreferences sharedPreferences) {
return new BooleanPreference(sharedPreferences, AppPrefsConstants.CHAT_OPENED);
}
@Provides
@ApplicationScope
@Named(AppPrefsConstants.APP_KILLED)
BooleanPreference provideAppKilled(@AppPrefs SharedPreferences sharedPreferences) {
return new BooleanPreference(sharedPreferences, AppPrefsConstants.APP_KILLED);
}
}

View File

@@ -0,0 +1,57 @@
package com.p3.bewoplaner.data.prefs;
import com.securepreferences.SecurePreferences;
import android.content.SharedPreferences;
import java.util.Map;
/**
* Created by imarneanu on 2/10/17.
*/
abstract class BasePreference<T> {
final SharedPreferences mPreferences;
final String mKey;
final T mDefaultValue;
BasePreference(SharedPreferences preferences, String key, T defaultValue) {
mPreferences = preferences;
mKey = key;
mDefaultValue = defaultValue;
}
BasePreference(SecurePreferences preferences, String key, T defaultValue) {
mPreferences = preferences;
mKey = key;
mDefaultValue = defaultValue;
}
public boolean isSet() {
return mPreferences.contains(mKey);
}
public void delete() {
mPreferences.edit().remove(mKey).apply();
}
/**
* Method needs to be called when {@link SecurePreferences} are used.
*/
public void deleteFromSecurePreference() {
String secureKey = SecurePreferences.hashPrefKey(mKey);
for (Map.Entry<String, ?> entry : ((SecurePreferences) mPreferences).getAll().entrySet()) {
final String key = entry.getKey();
//noinspection ConstantConditions
if (key.equals(secureKey)) {
mPreferences.edit().remove(mKey).apply();
return;
}
}
}
public abstract T get();
public abstract void set(T value);
}

View File

@@ -0,0 +1,34 @@
package com.p3.bewoplaner.data.prefs;
import com.securepreferences.SecurePreferences;
import android.content.SharedPreferences;
import android.support.annotation.NonNull;
/**
* Created by Rares Teodorescu on 27/03/2017.
*/
public class BooleanPreference extends BasePreference<Boolean> {
BooleanPreference(@NonNull SharedPreferences preferences, @NonNull String key) {
this(preferences, key, false);
}
BooleanPreference(SharedPreferences preferences, String key, Boolean defaultValue) {
super(preferences, key, defaultValue);
}
BooleanPreference(SecurePreferences preferences, String key, Boolean defaultValue) {
super(preferences, key, defaultValue);
}
@Override
public Boolean get() {
return mPreferences.getBoolean(mKey, mDefaultValue);
}
@Override
public void set(Boolean value) {
mPreferences.edit().putBoolean(mKey, value).apply();
}
}

View File

@@ -0,0 +1,30 @@
package com.p3.bewoplaner.data.prefs;
import android.content.SharedPreferences;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
/**
* Created by imarneanu on 3/2/17.
*/
public class LongPreference extends BasePreference<Long> {
public LongPreference(@NonNull SharedPreferences preferences, @NonNull String key) {
this(preferences, key, (long) 0);
}
public LongPreference(@NonNull SharedPreferences preferences, @NonNull String key, @Nullable Long defaultValue) {
super(preferences, key, defaultValue);
}
@Override
public Long get() {
return mPreferences.getLong(mKey, mDefaultValue);
}
@Override
public void set(Long value) {
mPreferences.edit().putLong(mKey, value).apply();
}
}

View File

@@ -0,0 +1,42 @@
package com.p3.bewoplaner.data.prefs;
import android.content.SharedPreferences;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import com.securepreferences.SecurePreferences;
/**
* Created by imarneanu on 2/10/17.
*/
public class StringPreference extends BasePreference<String> {
public StringPreference(@NonNull SharedPreferences preferences, @NonNull String key) {
this(preferences, key, null);
}
public StringPreference(@NonNull SecurePreferences preferences, @NonNull String key) {
this(preferences, key, null);
}
public StringPreference(@NonNull SharedPreferences preferences, @NonNull String key, @Nullable String defaultValue) {
super(preferences, key, defaultValue);
}
public StringPreference(@NonNull SecurePreferences preferences, @NonNull String key, @Nullable String defaultValue) {
super(preferences, key, defaultValue);
}
@Override
public String get() {
return mPreferences.getString(mKey, mDefaultValue);
}
@Override
public void set(String value) {
if (!TextUtils.isEmpty(value)) {
mPreferences.edit().putString(mKey, value).apply();
}
}
}

View File

@@ -0,0 +1,264 @@
package com.p3.bewoplaner.data.repository;
import com.p3.bewoplaner.data.repository.mapper.Mapper;
import com.p3.bewoplaner.data.repository.models.GroupRealm;
import com.p3.bewoplaner.data.repository.models.UserRealm;
import com.p3.bewoplaner.data.repository.specifications.GroupByIdSpecification;
import com.p3.bewoplaner.data.repository.specifications.RealmSpecification;
import com.p3.bewoplaner.data.repository.specifications.Specification;
import com.p3.bewoplaner.model.Group;
import com.p3.bewoplaner.model.Message;
import com.p3.bewoplaner.model.User;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import io.realm.RealmList;
import io.realm.Sort;
import rx.Completable;
import rx.Observable;
import rx.functions.Func1;
/**
* Created by imarneanu on 3/6/17.
*/
public class GroupRealmRepository implements Repository<Group> {
private final RealmProvider mRealmProvider;
private final Mapper<Group, GroupRealm> mToRealmMapper;
private final Mapper<GroupRealm, Group> mToGroupMapper;
public GroupRealmRepository(RealmProvider realmProvider,
Mapper<Group, GroupRealm> toRealmMapper,
Mapper<GroupRealm, Group> toMessageMapper) {
mRealmProvider = realmProvider;
mToRealmMapper = toRealmMapper;
mToGroupMapper = toMessageMapper;
}
@Override
public Completable add(Iterable<Group> items) {
final RealmWrapper realmWrapper = new RealmWrapper();
return Observable.from(items)
.doOnSubscribe(() -> {
realmWrapper.realm = mRealmProvider.provide();
realmWrapper.realm.beginTransaction();
})
.map(mToRealmMapper::from)
.doOnNext(groupRealm -> realmWrapper.realm.insert(groupRealm))
.doOnError(throwable -> {
realmWrapper.realm.cancelTransaction();
realmWrapper.realm.close();
})
.doOnCompleted(() -> {
realmWrapper.realm.commitTransaction();
realmWrapper.realm.close();
})
.toCompletable();
}
@Override
public Completable update(Group item, Specification specification) {
RealmWrapper realmWrapper = new RealmWrapper();
GroupByIdSpecification realmSpecification = (GroupByIdSpecification) specification;
return Observable.just(item)
.doOnSubscribe(() -> {
realmWrapper.realm = mRealmProvider.provide();
realmWrapper.realm.beginTransaction();
})
.map(mToRealmMapper::from)
.doOnNext(newGroup -> {
realmSpecification.setGroupId(newGroup.getId());
GroupRealm groupToUpdate =
realmSpecification.toRealmQuery(realmWrapper.realm).findFirst();
if (groupToUpdate != null) {
newGroup.setUniqueId(groupToUpdate.getUniqueId());
}
realmWrapper.realm.copyToRealmOrUpdate(newGroup);
})
.doOnError(throwable -> {
realmWrapper.realm.cancelTransaction();
realmWrapper.realm.close();
})
.doOnCompleted(() -> {
realmWrapper.realm.commitTransaction();
realmWrapper.realm.close();
})
.toCompletable();
}
@Override
public Completable update(Iterable<Group> items, Specification specification) {
final RealmWrapper realmWrapper = new RealmWrapper();
GroupByIdSpecification realmSpecification = (GroupByIdSpecification) specification;
return Observable.from(items)
.doOnSubscribe(() -> {
realmWrapper.realm = mRealmProvider.provide();
realmWrapper.realm.beginTransaction();
})
.map(mToRealmMapper::from)
.doOnNext(newGroup -> {
realmSpecification.setGroupId(newGroup.getId());
GroupRealm groupToUpdate =
realmSpecification.toRealmQuery(realmWrapper.realm).findFirst();
if (groupToUpdate != null) {
if (newGroup.getMessage() != null && groupToUpdate.getMessage() != null &&
newGroup.getMessage().getCreatedAt() < groupToUpdate.getMessage().getCreatedAt()) {
return;
}
newGroup.setUniqueId(groupToUpdate.getUniqueId());
if (groupToUpdate.getMessage() != null
&& newGroup.getMessage() != null
&& groupToUpdate.getMessage().getUniqueId().equals(newGroup.getMessage().getUniqueId())
&& groupToUpdate.getMessage().isRead()) {
newGroup.getMessage().setRead(true);
}
}
realmWrapper.realm.copyToRealmOrUpdate(newGroup);
})
.doOnError(throwable -> {
realmWrapper.realm.cancelTransaction();
realmWrapper.realm.close();
})
.doOnCompleted(() -> {
realmWrapper.realm.commitTransaction();
realmWrapper.realm.close();
})
.toCompletable();
}
@Override
public Completable remove(Specification specification) {
return null;
}
@Override
public Completable removeAll() {
RealmWrapper realmWrapper = new RealmWrapper();
return Observable.fromCallable(new Callable<Void>() {
@Override
public Void call() throws Exception {
realmWrapper.realm.delete(GroupRealm.class);
return null;
}
})
.doOnSubscribe(() -> {
realmWrapper.realm = mRealmProvider.provide();
realmWrapper.realm.beginTransaction();
})
.doOnError(throwable -> {
realmWrapper.realm.cancelTransaction();
realmWrapper.realm.close();
})
.doOnCompleted(() -> {
realmWrapper.realm.commitTransaction();
realmWrapper.realm.close();
})
.toCompletable();
}
@Override
public Observable<Group> queryForOneById(String id, Specification specification) {
RealmWrapper realmWrapper = new RealmWrapper();
GroupByIdSpecification realmSpecification = (GroupByIdSpecification) specification;
((GroupByIdSpecification) specification).setGroupId(Integer.parseInt(id));
return Observable.fromCallable(() -> realmSpecification.toRealmQuery(realmWrapper.realm).findFirst())
.map(mToGroupMapper::from)
.doOnSubscribe(() -> {
realmWrapper.realm = mRealmProvider.provide();
realmWrapper.realm.beginTransaction();
})
.doOnError(throwable -> {
realmWrapper.realm.cancelTransaction();
realmWrapper.realm.close();
})
.doOnCompleted(() -> {
realmWrapper.realm.commitTransaction();
realmWrapper.realm.close();
});
}
@Override
public Observable<List<Group>> queryForAll(Specification specification) {
RealmWrapper realmWrapper = new RealmWrapper();
RealmSpecification<GroupRealm> realmSpecification = (RealmSpecification) specification;
return Observable.fromCallable(() -> realmSpecification.toRealmQuery(realmWrapper.realm)
.findAllSorted(GroupRealm.CREATED_AT, Sort.DESCENDING))
.flatMapIterable(groupsRealms -> groupsRealms)
.flatMap(new Func1<GroupRealm, Observable<Group>>() {
@Override
public Observable<Group> call(GroupRealm groupRealm) {
return Observable.just(mToGroupMapper.from(groupRealm));
}
})
.toList()
.doOnSubscribe(() -> {
realmWrapper.realm = mRealmProvider.provide();
realmWrapper.realm.beginTransaction();
})
.doOnError(throwable -> {
realmWrapper.realm.cancelTransaction();
realmWrapper.realm.close();
})
.doOnCompleted(() -> {
realmWrapper.realm.commitTransaction();
realmWrapper.realm.close();
});
}
public Completable updateGroupMessage(Group group, Specification specification) {
final RealmWrapper realmWrapper = new RealmWrapper();
GroupByIdSpecification realmSpecification = (GroupByIdSpecification) specification;
return Observable.just(group)
.doOnSubscribe(() -> {
realmWrapper.realm = mRealmProvider.provide();
realmWrapper.realm.beginTransaction();
})
.map(mToRealmMapper::from)
.doOnNext(newGroup -> {
realmSpecification.setGroupId(newGroup.getId());
GroupRealm groupToUpdate =
realmSpecification.toRealmQuery(realmWrapper.realm).findFirst();
if (groupToUpdate == null) {
return;
}
if (groupToUpdate.getMessage() != null &&
group.lastMessage.createdAt < (groupToUpdate.getMessage().getCreatedAt() / 1000)) {
return;
}
RealmList<UserRealm> userRealms = groupToUpdate.getUserIdList();
ArrayList<User> users = null;
if (userRealms != null) {
users = new ArrayList<>(userRealms.size());
for (int i = 0, size = userRealms.size(); i < size; i++) {
users.add(User.createFromUserRealm(userRealms.get(i)));
}
}
Message message = group.lastMessage;
if (groupToUpdate.getMessage() != null
&& groupToUpdate.getMessage().getUniqueId().equals(message.getUniqueId())
&& groupToUpdate.getMessage().isRead()) {
message.isRead = true;
}
newGroup = new GroupRealm(groupToUpdate.getId(),
groupToUpdate.getName(),
groupToUpdate.getAvatar(),
group.lastMessage,
groupToUpdate.getMobile(),
groupToUpdate.isOnlyEmployees(),
users,
groupToUpdate.getUserId());
realmWrapper.realm.copyToRealmOrUpdate(newGroup);
})
.doOnError(throwable -> {
realmWrapper.realm.cancelTransaction();
realmWrapper.realm.close();
})
.doOnCompleted(() -> {
realmWrapper.realm.commitTransaction();
realmWrapper.realm.close();
})
.toCompletable();
}
}

View File

@@ -0,0 +1,254 @@
package com.p3.bewoplaner.data.repository;
import com.p3.bewoplaner.data.repository.mapper.Mapper;
import com.p3.bewoplaner.data.repository.models.MessageRealm;
import com.p3.bewoplaner.data.repository.specifications.MessageByIdSpecification;
import com.p3.bewoplaner.data.repository.specifications.RealmSpecification;
import com.p3.bewoplaner.data.repository.specifications.Specification;
import com.p3.bewoplaner.model.Message;
import java.util.List;
import java.util.concurrent.Callable;
import io.realm.Sort;
import rx.Completable;
import rx.Observable;
import rx.functions.Func1;
/**
* Created by Rares Teodorescu on 22/02/2017.
*/
public class MessageRealmRepository implements Repository<Message> {
private final RealmProvider mRealmProvider;
private final Mapper<Message, MessageRealm> mToRealmMapper;
private final Mapper<MessageRealm, Message> mToMessageMapper;
public MessageRealmRepository(RealmProvider realmProvider,
Mapper<Message, MessageRealm> toRealmMapper,
Mapper<MessageRealm, Message> toMessageMapper) {
mRealmProvider = realmProvider;
mToRealmMapper = toRealmMapper;
mToMessageMapper = toMessageMapper;
}
@Override
public Completable add(Iterable<Message> items) {
final RealmWrapper realmWrapper = new RealmWrapper();
return Observable.from(items)
.doOnSubscribe(() -> {
realmWrapper.realm = mRealmProvider.provide();
realmWrapper.realm.beginTransaction();
})
.map(mToRealmMapper::from)
.doOnNext(messageRealm -> realmWrapper.realm.insert(messageRealm))
.doOnError(throwable -> {
realmWrapper.realm.cancelTransaction();
realmWrapper.realm.close();
})
.doOnCompleted(() -> {
realmWrapper.realm.commitTransaction();
realmWrapper.realm.close();
})
.toCompletable();
}
@Override
public Completable update(Message item, Specification specification) {
RealmWrapper realmWrapper = new RealmWrapper();
MessageByIdSpecification realmSpecification = (MessageByIdSpecification) specification;
return Observable.just(item)
.doOnSubscribe(() -> {
realmWrapper.realm = mRealmProvider.provide();
realmWrapper.realm.beginTransaction();
})
.map(mToRealmMapper::from)
.doOnNext(newMessage -> {
realmSpecification.setMessageId(newMessage.getId());
MessageRealm messageToUpdate =
realmSpecification.toRealmQuery(realmWrapper.realm).findFirst();
if (messageToUpdate != null) {
newMessage.setUniqueId(messageToUpdate.getUniqueId());
}
realmWrapper.realm.copyToRealmOrUpdate(newMessage);
})
.doOnError(throwable -> {
realmWrapper.realm.cancelTransaction();
realmWrapper.realm.close();
})
.doOnCompleted(() -> {
realmWrapper.realm.commitTransaction();
realmWrapper.realm.close();
})
.toCompletable();
}
@Override
public Completable update(Iterable<Message> items, Specification specification) {
final RealmWrapper realmWrapper = new RealmWrapper();
MessageByIdSpecification realmSpecification = (MessageByIdSpecification) specification;
return Observable.from(items)
.doOnSubscribe(() -> {
realmWrapper.realm = mRealmProvider.provide();
realmWrapper.realm.beginTransaction();
})
.map(mToRealmMapper::from)
.doOnNext(newMessage -> {
realmSpecification.setMessageId(newMessage.getId());
MessageRealm messageToUpdate =
realmSpecification.toRealmQuery(realmWrapper.realm).findFirst();
if (messageToUpdate != null) {
newMessage.setUniqueId(messageToUpdate.getUniqueId());
newMessage.setFileDownloaded(messageToUpdate.isFileDownloaded());
}
realmWrapper.realm.copyToRealmOrUpdate(newMessage);
})
.doOnError(throwable -> {
realmWrapper.realm.cancelTransaction();
realmWrapper.realm.close();
})
.doOnCompleted(() -> {
realmWrapper.realm.commitTransaction();
realmWrapper.realm.close();
})
.toCompletable();
}
public Observable<List<Message>> updateMessages(Iterable<Message> items, Specification specification) {
final RealmWrapper realmWrapper = new RealmWrapper();
MessageByIdSpecification realmSpecification = (MessageByIdSpecification) specification;
return Observable.from(items)
.flatMapIterable(new Func1<Message, Iterable<Message>>() {
@Override
public Iterable<Message> call(Message message) {
return items;
}
})
.map(mToRealmMapper::from)
.doOnNext(newMessage -> {
realmSpecification.setMessageId(newMessage.getId());
MessageRealm messageToUpdate =
realmSpecification.toRealmQuery(realmWrapper.realm).findFirst();
if (messageToUpdate != null) {
newMessage.setUniqueId(messageToUpdate.getUniqueId());
newMessage.setFileDownloaded(messageToUpdate.isFileDownloaded());
}
realmWrapper.realm.copyToRealmOrUpdate(newMessage);
})
.flatMap(new Func1<MessageRealm, Observable<Message>>() {
@Override
public Observable<Message> call(MessageRealm messageRealm) {
return Observable.just(mToMessageMapper.from(messageRealm));
}
})
.toList()
.doOnSubscribe(() -> {
realmWrapper.realm = mRealmProvider.provide();
realmWrapper.realm.beginTransaction();
})
.doOnError(throwable -> {
realmWrapper.realm.cancelTransaction();
realmWrapper.realm.close();
})
.doOnCompleted(() -> {
realmWrapper.realm.commitTransaction();
realmWrapper.realm.close();
});
}
@Override
public Completable remove(Specification specification) {
RealmWrapper realmWrapper = new RealmWrapper();
MessageByIdSpecification realmSpecification = (MessageByIdSpecification) specification;
return Observable.fromCallable(() -> realmSpecification.toRealmQuery(realmWrapper.realm).findFirst())
.doOnSubscribe(() -> {
realmWrapper.realm = mRealmProvider.provide();
realmWrapper.realm.beginTransaction();
})
.doOnNext(messageRealm -> messageRealm.deleteFromRealm())
.doOnError(throwable -> {
realmWrapper.realm.cancelTransaction();
realmWrapper.realm.close();
})
.doOnCompleted(() -> {
realmWrapper.realm.commitTransaction();
realmWrapper.realm.close();
})
.toCompletable();
}
@Override
public Completable removeAll() {
RealmWrapper realmWrapper = new RealmWrapper();
return Observable.fromCallable(new Callable<Void>() {
@Override
public Void call() throws Exception {
realmWrapper.realm.delete(MessageRealm.class);
return null;
}
})
.doOnSubscribe(() -> {
realmWrapper.realm = mRealmProvider.provide();
realmWrapper.realm.beginTransaction();
})
.doOnError(throwable -> {
realmWrapper.realm.cancelTransaction();
realmWrapper.realm.close();
})
.doOnCompleted(() -> {
realmWrapper.realm.commitTransaction();
realmWrapper.realm.close();
})
.toCompletable();
}
@Override
public Observable<Message> queryForOneById(String id, Specification specification) {
RealmWrapper realmWrapper = new RealmWrapper();
MessageByIdSpecification realmSpecification = (MessageByIdSpecification) specification;
((MessageByIdSpecification) specification).setMessageId(id);
return Observable.fromCallable(() -> realmSpecification.toRealmQuery(realmWrapper.realm).findFirst())
.map(mToMessageMapper::from)
.doOnSubscribe(() -> {
realmWrapper.realm = mRealmProvider.provide();
realmWrapper.realm.beginTransaction();
})
.doOnError(throwable -> {
realmWrapper.realm.cancelTransaction();
realmWrapper.realm.close();
})
.doOnCompleted(() -> {
realmWrapper.realm.commitTransaction();
realmWrapper.realm.close();
});
}
@Override
public Observable<List<Message>> queryForAll(Specification specification) {
RealmWrapper realmWrapper = new RealmWrapper();
RealmSpecification<MessageRealm> realmSpecification = (RealmSpecification) specification;
return Observable.fromCallable(() -> realmSpecification.toRealmQuery(realmWrapper.realm)
.findAllSorted(MessageRealm.CREATED_AT, Sort.DESCENDING))
.flatMapIterable(talksRealms -> talksRealms)
.flatMap(new Func1<MessageRealm, Observable<Message>>() {
@Override
public Observable<Message> call(MessageRealm userRealm) {
return Observable.just(mToMessageMapper.from(userRealm));
}
})
.toList()
.doOnSubscribe(() -> {
realmWrapper.realm = mRealmProvider.provide();
realmWrapper.realm.beginTransaction();
})
.doOnError(throwable -> {
realmWrapper.realm.cancelTransaction();
realmWrapper.realm.close();
})
.doOnCompleted(() -> {
realmWrapper.realm.commitTransaction();
realmWrapper.realm.close();
});
}
}

View File

@@ -0,0 +1,21 @@
package com.p3.bewoplaner.data.repository;
import io.realm.Realm;
import io.realm.RealmConfiguration;
/**
* Created by Rares Teodorescu on 11/01/2017.
*/
public class RealmProvider {
private final RealmConfiguration realmConfiguration;
RealmProvider(RealmConfiguration realmConfiguration) {
this.realmConfiguration = realmConfiguration;
}
public Realm provide() {
return Realm.getInstance(realmConfiguration);
}
}

View File

@@ -0,0 +1,14 @@
package com.p3.bewoplaner.data.repository;
import io.realm.Realm;
/**
* Created by Rares Teodorescu on 13/01/2017.
*/
final class RealmWrapper {
Realm realm;
RealmWrapper() {
}
}

View File

@@ -0,0 +1,28 @@
package com.p3.bewoplaner.data.repository;
import com.p3.bewoplaner.data.repository.specifications.Specification;
import java.util.List;
import rx.Completable;
import rx.Observable;
/**
* Created by Rares Teodorescu on 11/01/2017.
*/
interface Repository<T> {
Completable add(Iterable<T> items);
Completable update(T item, Specification specification);
Completable update(Iterable<T> items, Specification specification);
Completable remove(Specification specification);
Observable<T> queryForOneById(String id, Specification specification);
Observable<List<T>> queryForAll(Specification specification);
Completable removeAll();
}

View File

@@ -0,0 +1,96 @@
package com.p3.bewoplaner.data.repository;
import com.p3.bewoplaner.data.repository.mapper.Mapper;
import com.p3.bewoplaner.data.repository.models.GroupRealm;
import com.p3.bewoplaner.data.repository.models.MessageRealm;
import com.p3.bewoplaner.data.repository.models.UserRealm;
import com.p3.bewoplaner.di.scopes.ApplicationScope;
import com.p3.bewoplaner.model.Group;
import com.p3.bewoplaner.model.Message;
import com.p3.bewoplaner.model.User;
import dagger.Module;
import dagger.Provides;
import io.realm.RealmConfiguration;
/**
* Created by Rares Teodorescu on 11/01/2017.
*/
@Module
public class RepositoryModule {
@Provides
@ApplicationScope
RealmConfiguration provideRealmConfiguration() {
return new RealmConfiguration.Builder()
.deleteRealmIfMigrationNeeded()
.build();
}
@Provides
@ApplicationScope
RealmProvider provideRealm(RealmConfiguration realmConfiguration) {
return new RealmProvider(realmConfiguration);
}
@Provides
@ApplicationScope
Mapper<Group, GroupRealm> provideToGroupRealmMapper() {
return GroupRealm::createFromGroup;
}
@Provides
@ApplicationScope
Mapper<GroupRealm, Group> provideToGroupMapper() {
return Group::createFromGroupRealm;
}
@Provides
@ApplicationScope
GroupRealmRepository provideGroupRealmRepository(RealmProvider realmProvider,
Mapper<Group, GroupRealm> toRealmMapper,
Mapper<GroupRealm, Group> toGroupMapper) {
return new GroupRealmRepository(realmProvider, toRealmMapper, toGroupMapper);
}
@Provides
@ApplicationScope
Mapper<User, UserRealm> provideToUserRealmMapper() {
return UserRealm::createFromUser;
}
@Provides
@ApplicationScope
Mapper<UserRealm, User> provideToUserMapper() {
return User::createFromUserRealm;
}
@Provides
@ApplicationScope
UserRealmRepository provideUserRealmRepository(RealmProvider realmProvider,
Mapper<User, UserRealm> toRealmMapper,
Mapper<UserRealm, User> toUserMapper) {
return new UserRealmRepository(realmProvider, toRealmMapper, toUserMapper);
}
@Provides
@ApplicationScope
Mapper<Message, MessageRealm> provideToMessageRealmMapper() {
return MessageRealm::createFromMessage;
}
@Provides
@ApplicationScope
Mapper<MessageRealm, Message> provideToMessageMapper() {
return Message::createFromMessageRealm;
}
@Provides
@ApplicationScope
MessageRealmRepository provideMessageRealmRepository(RealmProvider realmProvider,
Mapper<Message, MessageRealm> toRealmMapper,
Mapper<MessageRealm, Message> toUserMapper) {
return new MessageRealmRepository(realmProvider, toRealmMapper, toUserMapper);
}
}

View File

@@ -0,0 +1,196 @@
package com.p3.bewoplaner.data.repository;
import com.p3.bewoplaner.data.repository.mapper.Mapper;
import com.p3.bewoplaner.data.repository.models.UserRealm;
import com.p3.bewoplaner.data.repository.specifications.RealmSpecification;
import com.p3.bewoplaner.data.repository.specifications.Specification;
import com.p3.bewoplaner.data.repository.specifications.UserByIdSpecification;
import com.p3.bewoplaner.model.User;
import android.util.Log;
import java.util.List;
import java.util.concurrent.Callable;
import io.realm.Sort;
import rx.Completable;
import rx.Observable;
import rx.functions.Func1;
/**
* Created by imarneanu on 3/6/17.
*/
public class UserRealmRepository implements Repository<User> {
private final RealmProvider mRealmProvider;
private final Mapper<User, UserRealm> mToRealmMapper;
private final Mapper<UserRealm, User> mToUserMapper;
public UserRealmRepository(RealmProvider realmProvider,
Mapper<User, UserRealm> toRealmMapper,
Mapper<UserRealm, User> toUserMapper) {
mRealmProvider = realmProvider;
mToRealmMapper = toRealmMapper;
mToUserMapper = toUserMapper;
}
@Override
public Completable add(Iterable<User> items) {
final RealmWrapper realmWrapper = new RealmWrapper();
return Observable.from(items)
.doOnSubscribe(() -> {
realmWrapper.realm = mRealmProvider.provide();
realmWrapper.realm.beginTransaction();
})
.map(mToRealmMapper::from)
.doOnNext(userRealm -> realmWrapper.realm.insert(userRealm))
.doOnError(throwable -> {
realmWrapper.realm.cancelTransaction();
realmWrapper.realm.close();
})
.doOnCompleted(() -> {
realmWrapper.realm.commitTransaction();
realmWrapper.realm.close();
})
.toCompletable();
}
@Override
public Completable update(User item, Specification specification) {
RealmWrapper realmWrapper = new RealmWrapper();
UserByIdSpecification realmSpecification = (UserByIdSpecification) specification;
return Observable.just(item)
.doOnSubscribe(() -> {
realmWrapper.realm = mRealmProvider.provide();
realmWrapper.realm.beginTransaction();
})
.map(mToRealmMapper::from)
.doOnNext(newUser -> {
realmSpecification.setUserId(newUser.getId());
UserRealm userToUpdate =
realmSpecification.toRealmQuery(realmWrapper.realm).findFirst();
if (userToUpdate != null) {
newUser.setId(userToUpdate.getId());
}
realmWrapper.realm.copyToRealmOrUpdate(newUser);
})
.doOnError(throwable -> {
realmWrapper.realm.cancelTransaction();
realmWrapper.realm.close();
})
.doOnCompleted(() -> {
realmWrapper.realm.commitTransaction();
realmWrapper.realm.close();
})
.toCompletable();
}
@Override
public Completable update(Iterable<User> items, Specification specification) {
final RealmWrapper realmWrapper = new RealmWrapper();
UserByIdSpecification realmSpecification = (UserByIdSpecification) specification;
return Observable.from(items)
.doOnSubscribe(() -> {
realmWrapper.realm = mRealmProvider.provide();
realmWrapper.realm.beginTransaction();
})
.map(mToRealmMapper::from)
.doOnNext(newUser -> {
realmSpecification.setUserId(newUser.getId());
UserRealm userToUpdate =
realmSpecification.toRealmQuery(realmWrapper.realm).findFirst();
if (userToUpdate != null) {
newUser.setId(userToUpdate.getId());
}
realmWrapper.realm.copyToRealmOrUpdate(newUser);
})
.doOnError(throwable -> {
realmWrapper.realm.cancelTransaction();
realmWrapper.realm.close();
})
.doOnCompleted(() -> {
realmWrapper.realm.commitTransaction();
realmWrapper.realm.close();
})
.toCompletable();
}
@Override
public Completable remove(Specification specification) {
return null;
}
@Override
public Observable<User> queryForOneById(String id, Specification specification) {
RealmWrapper realmWrapper = new RealmWrapper();
UserByIdSpecification realmSpecification = (UserByIdSpecification) specification;
Log.e("USER", "long value: " + Long.parseLong(id));
((UserByIdSpecification) specification).setUserId(Long.parseLong(id));
return Observable.fromCallable(() -> realmSpecification.toRealmQuery(realmWrapper.realm).findFirst())
.map(mToUserMapper::from)
.doOnSubscribe(() -> {
realmWrapper.realm = mRealmProvider.provide();
realmWrapper.realm.beginTransaction();
})
.doOnError(throwable -> {
realmWrapper.realm.cancelTransaction();
realmWrapper.realm.close();
})
.doOnCompleted(() -> {
realmWrapper.realm.commitTransaction();
realmWrapper.realm.close();
});
}
@Override
public Observable<List<User>> queryForAll(Specification specification) {
RealmWrapper realmWrapper = new RealmWrapper();
RealmSpecification<UserRealm> realmSpecification = (RealmSpecification) specification;
return Observable.fromCallable(() -> realmSpecification.toRealmQuery(realmWrapper.realm)
.findAllSorted(UserRealm.FIRST_NAME, Sort.ASCENDING))
.flatMapIterable(userRealms -> userRealms)
.flatMap(new Func1<UserRealm, Observable<User>>() {
@Override
public Observable<User> call(UserRealm userRealm) {
return Observable.just(mToUserMapper.from(userRealm));
}
})
.toList()
.doOnSubscribe(() -> {
realmWrapper.realm = mRealmProvider.provide();
realmWrapper.realm.beginTransaction();
})
.doOnError(throwable -> {
realmWrapper.realm.cancelTransaction();
realmWrapper.realm.close();
})
.doOnCompleted(() -> {
realmWrapper.realm.commitTransaction();
realmWrapper.realm.close();
});
}
@Override
public Completable removeAll() {
RealmWrapper realmWrapper = new RealmWrapper();
return Observable.fromCallable(new Callable<Void>() {
@Override
public Void call() throws Exception {
realmWrapper.realm.delete(UserRealm.class);
return null;
}
})
.doOnSubscribe(() -> {
realmWrapper.realm = mRealmProvider.provide();
realmWrapper.realm.beginTransaction();
})
.doOnError(throwable -> {
realmWrapper.realm.cancelTransaction();
realmWrapper.realm.close();
})
.doOnCompleted(() -> {
realmWrapper.realm.commitTransaction();
realmWrapper.realm.close();
})
.toCompletable();
}
}

View File

@@ -0,0 +1,10 @@
package com.p3.bewoplaner.data.repository.mapper;
/**
* Class responsible for mapping one object to another.
* <p>
* Created by Rares Teodorescu on 11/01/2017.
*/
public interface Mapper<F, T> {
T from(F from);
}

View File

@@ -0,0 +1,145 @@
package com.p3.bewoplaner.data.repository.models;
import com.p3.bewoplaner.model.Group;
import com.p3.bewoplaner.model.Message;
import com.p3.bewoplaner.model.User;
import java.util.ArrayList;
import io.realm.RealmList;
import io.realm.RealmObject;
import io.realm.annotations.PrimaryKey;
/**
* Created by imarneanu on 3/6/17.
*/
public class GroupRealm extends RealmObject {
public static final String ID = "id";
public static final String NAME = "name";
public static final String CREATED_AT = "createdAt";
public static final String USER_ID = "userId";
@PrimaryKey
private String uniqueId;
private long id;
private String name;
private String avatar;
private MessageRealm message;
private String mobile;
private boolean onlyEmployees;
private RealmList<UserRealm> userIdList;
private long createdAt;
private long userId;
public GroupRealm() {
}
public GroupRealm(long id, String name, String avatar, Message message, String mobile, boolean onlyEmployees, ArrayList<User> userList, long userId) {
this.userIdList = new RealmList<>();
this.id = id;
this.name = name;
this.avatar = avatar;
this.message = MessageRealm.createFromMessage(message);
this.createdAt = this.message != null ? this.message.getCreatedAt() : 0;
this.onlyEmployees = onlyEmployees;
this.mobile = mobile;
if (userList != null) {
for (User l : userList) {
this.userIdList.add(UserRealm.createFromUser(l));
}
}
this.userId = userId;
this.uniqueId = id + "_" + userId;
}
public long getUserId() {
return userId;
}
public static GroupRealm createFromGroup(Group group) {
if (group == null) {
return null;
}
return new GroupRealm(group.id, group.name, group.avatar, group.lastMessage, group.mobile, group.onlyEmployees, group.users, group.userId);
}
public void setUniqueId(String uniqueId) {
this.uniqueId = uniqueId;
}
public String getUniqueId() {
return uniqueId;
}
public void setId(long id) {
this.id = id;
}
public long getId() {
return id;
}
public String getName() {
return name;
}
public String getAvatar() {
return avatar;
}
public boolean isOnlyEmployees() {
return onlyEmployees;
}
public MessageRealm getMessage() {
return message;
}
public void setMessage(MessageRealm message) {
this.message = message;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
public void setOnlyEmployees(boolean onlyEmployees) {
this.onlyEmployees = onlyEmployees;
}
public String getMobile() {
return mobile;
}
public void setName(String name) {
this.name = name;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public RealmList<UserRealm> getUserIdList() {
return userIdList;
}
public void setUserIdList(RealmList<UserRealm> userIdList) {
this.userIdList = userIdList;
}
@Override
public String toString() {
return "GroupRealm{" +
"id=" + id +
", name='" + name + '\'' +
", uniqueId" + uniqueId +
", avatar='" + avatar + '\'' +
", message=" + message +
", mobile='" + mobile + '\'' +
", onlyEmployees=" + onlyEmployees +
", userIdList=" + userIdList +
", createdAt=" + createdAt +
'}';
}
}

View File

@@ -0,0 +1,227 @@
package com.p3.bewoplaner.data.repository.models;
import com.p3.bewoplaner.model.Message;
import io.realm.RealmObject;
import io.realm.annotations.PrimaryKey;
/**
* Created by Rares Teodorescu on 22/02/2017.
*/
public class MessageRealm extends RealmObject {
public static final String ID = "id";
public static final String GROUP_ID = "groupId";
public static final String CREATED_AT = "createdAt";
public static final String USER_ID = "userId";
@PrimaryKey
private String uniqueId;
private String id;
private long groupId;
private String message;
private String filePath;
private String smallerImage;
private String filename;
private String uri;
private long createdAt;
private String senderAvatar;
private String senderName;
private long senderId;
private int status;
private boolean isRead;
private long userId;
private boolean fileDownloaded;
public MessageRealm() {
}
public MessageRealm(String id,
int groupId,
String message,
String filePath,
String smallerImage,
String filename,
String uri,
long createdAt,
int senderId,
String senderName,
String senderAvatar,
Message.Status status,
boolean isRead,
long userId,
boolean fileDownloaded) {
this.id = id;
this.groupId = groupId;
this.message = message;
this.filePath = filePath;
this.smallerImage = smallerImage;
this.filename = filename;
this.uri = uri;
this.createdAt = createdAt;
this.senderId = senderId;
this.senderName = senderName;
this.senderAvatar = senderAvatar;
this.status = Message.Status.getValue(status);
this.isRead = isRead;
this.userId = userId;
this.fileDownloaded = fileDownloaded;
}
public long getCreatedAt() {
return createdAt;
}
public void setCreatedAt(long createdAt) {
this.createdAt = createdAt;
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
public String getId() {
return id;
}
public String getUniqueId() {
return uniqueId;
}
public void setUniqueId(String uniqueId) {
this.uniqueId = uniqueId;
}
public void setId(String id) {
this.id = id;
}
public static String getID() {
return ID;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getSenderAvatar() {
return senderAvatar;
}
public void setSenderAvatar(String senderAvatar) {
this.senderAvatar = senderAvatar;
}
public long getSenderId() {
return senderId;
}
public void setSenderId(long senderId) {
this.senderId = senderId;
}
public String getSenderName() {
return senderName;
}
public void setSenderName(String senderName) {
this.senderName = senderName;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public long getGroupId() {
return groupId;
}
public void setGroupId(long groupId) {
this.groupId = groupId;
}
public void setUri(String uri) {
this.uri = uri;
}
public String getUri() {
return uri;
}
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
public boolean isRead() {
return isRead;
}
public void setRead(boolean read) {
isRead = read;
}
public boolean isFileDownloaded() {
return fileDownloaded;
}
public void setFileDownloaded(boolean fileDownloaded) {
this.fileDownloaded = fileDownloaded;
}
public String getSmallerImage() {
return smallerImage;
}
public void setSmallerImage(String smallerImage) {
this.smallerImage = smallerImage;
}
public static MessageRealm createFromMessage(Message message) {
if (message == null) {
return null;
}
MessageRealm messageRealm = new MessageRealm();
messageRealm.setId(message.id);
messageRealm.setGroupId(message.groupId);
messageRealm.setMessage(message.message);
messageRealm.setFilePath(message.filePath);
messageRealm.setSmallerImage(message.smallerImage);
messageRealm.setFilename(message.originalFilename);
messageRealm.setUri(message.uri);
messageRealm.setCreatedAt(message.createdAt * 1000);
messageRealm.setSenderId(message.senderId);
messageRealm.setSenderName(message.senderName);
messageRealm.setSenderAvatar(message.senderAvatar);
messageRealm.setStatus(Message.Status.getValue(message.status));
messageRealm.setRead(message.isRead);
messageRealm.userId = message.userId;
messageRealm.uniqueId = getUniqueId(message.id, message.userId);
messageRealm.fileDownloaded = message.fileDownloaded;
return messageRealm;
}
public static String getUniqueId(String messageId, long userId) {
return messageId + "_" + userId;
}
public long getUserId() {
return userId;
}
}

View File

@@ -0,0 +1,74 @@
package com.p3.bewoplaner.data.repository.models;
import com.p3.bewoplaner.model.User;
import io.realm.RealmObject;
import io.realm.annotations.PrimaryKey;
/**
* Created by imarneanu on 3/6/17.
*/
public class UserRealm extends RealmObject {
public static final String ID = "id";
public static final String FIRST_NAME = "firstName";
@PrimaryKey
private long id;
private String firstName;
private String lastName;
private String avatar;
private String mobile;
private boolean isEmployee;
public UserRealm() {
}
public UserRealm(long id,
String firstName,
String lastName,
String avatar,
String mobile,
boolean isEmployee) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.avatar = avatar;
this.mobile = mobile;
this.isEmployee = isEmployee;
}
public void setId(long id) {
this.id = id;
}
public long getId() {
return id;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getAvatar() {
return avatar;
}
public String getMobile() {
return mobile;
}
public boolean isEmployee() {
return isEmployee;
}
public static UserRealm createFromUser(User user) {
if (user == null) {
return null;
}
return new UserRealm(user.id, user.firstName, user.lastName, user.avatar, user.mobile, user.isEmployee);
}
}

View File

@@ -0,0 +1,30 @@
package com.p3.bewoplaner.data.repository.specifications;
import com.p3.bewoplaner.data.repository.models.GroupRealm;
import io.realm.Realm;
import io.realm.RealmQuery;
/**
* Created by imarneanu on 3/6/17.
*/
public class GroupByIdSpecification implements RealmSpecification<GroupRealm> {
private long mGroupId;
private final long mUserId;
public GroupByIdSpecification(long userId) {
mUserId = userId;
}
public void setGroupId(long groupId) {
mGroupId = groupId;
}
@Override
public RealmQuery<GroupRealm> toRealmQuery(Realm realm) {
return realm.where(GroupRealm.class)
.equalTo(GroupRealm.ID, mGroupId)
.equalTo(GroupRealm.USER_ID, mUserId);
}
}

View File

@@ -0,0 +1,28 @@
package com.p3.bewoplaner.data.repository.specifications;
import com.p3.bewoplaner.data.repository.models.GroupRealm;
import io.realm.Case;
import io.realm.Realm;
import io.realm.RealmQuery;
/**
* Created by Rares Teodorescu on 10/03/2017.
*/
public class GroupByNameSpecification implements RealmSpecification<GroupRealm> {
private final String mName;
private final long mUserId;
public GroupByNameSpecification(String name, long userId) {
mName = name;
mUserId = userId;
}
@Override
public RealmQuery<GroupRealm> toRealmQuery(Realm realm) {
return realm.where(GroupRealm.class)
.equalTo(GroupRealm.USER_ID, mUserId)
.contains(GroupRealm.NAME, mName, Case.INSENSITIVE);
}
}

View File

@@ -0,0 +1,23 @@
package com.p3.bewoplaner.data.repository.specifications;
import com.p3.bewoplaner.data.repository.models.GroupRealm;
import io.realm.Realm;
import io.realm.RealmQuery;
/**
* Created by imarneanu on 3/6/17.
*/
public class GroupSpecification implements RealmSpecification<GroupRealm> {
private final long mUserId;
public GroupSpecification(long userId) {
mUserId = userId;
}
@Override
public RealmQuery<GroupRealm> toRealmQuery(Realm realm) {
return realm.where(GroupRealm.class).equalTo(GroupRealm.USER_ID, mUserId);
}
}

View File

@@ -0,0 +1,30 @@
package com.p3.bewoplaner.data.repository.specifications;
import com.p3.bewoplaner.data.repository.models.MessageRealm;
import io.realm.Realm;
import io.realm.RealmQuery;
/**
* Created by Rares Teodorescu on 22/02/2017.
*/
public class MessageByGroupIdSpecification implements RealmSpecification<MessageRealm> {
private long mGroupId;
private final long mUserId;
public MessageByGroupIdSpecification(long userId) {
mUserId = userId;
}
public void setGroupId(long groupId) {
mGroupId = groupId;
}
@Override
public RealmQuery<MessageRealm> toRealmQuery(Realm realm) {
return realm.where(MessageRealm.class)
.equalTo(MessageRealm.GROUP_ID, mGroupId)
.equalTo(MessageRealm.USER_ID, mUserId);
}
}

View File

@@ -0,0 +1,34 @@
package com.p3.bewoplaner.data.repository.specifications;
import com.p3.bewoplaner.data.repository.models.MessageRealm;
import io.realm.Realm;
import io.realm.RealmQuery;
/**
* Created by Rares Teodorescu on 22/02/2017.
*/
public class MessageByIdSpecification implements RealmSpecification<MessageRealm> {
private String mMessageId;
private long mUserId;
public MessageByIdSpecification(long userId) {
mUserId = userId;
}
public long getUserId() {
return mUserId;
}
public void setMessageId(String messageId) {
mMessageId = messageId;
}
@Override
public RealmQuery<MessageRealm> toRealmQuery(Realm realm) {
return realm.where(MessageRealm.class)
.equalTo(MessageRealm.ID, mMessageId)
.equalTo(MessageRealm.USER_ID, mUserId);
}
}

View File

@@ -0,0 +1,12 @@
package com.p3.bewoplaner.data.repository.specifications;
import io.realm.Realm;
import io.realm.RealmObject;
import io.realm.RealmQuery;
/**
* Created by Rares Teodorescu on 11/01/2017.
*/
public interface RealmSpecification<T extends RealmObject> extends Specification {
RealmQuery<T> toRealmQuery(Realm realm);
}

View File

@@ -0,0 +1,7 @@
package com.p3.bewoplaner.data.repository.specifications;
/**
* Created by Rares Teodorescu on 11/01/2017.
*/
public interface Specification {
}

View File

@@ -0,0 +1,24 @@
package com.p3.bewoplaner.data.repository.specifications;
import com.p3.bewoplaner.data.repository.models.UserRealm;
import io.realm.Realm;
import io.realm.RealmQuery;
/**
* Created by imarneanu on 3/6/17.
*/
public class UserByIdSpecification implements RealmSpecification<UserRealm> {
private long mUserId;
public void setUserId(long userId) {
mUserId = userId;
}
@Override
public RealmQuery<UserRealm> toRealmQuery(Realm realm) {
return realm.where(UserRealm.class)
.equalTo(UserRealm.ID, mUserId);
}
}

View File

@@ -0,0 +1,98 @@
package com.p3.bewoplaner.di;
import com.bumptech.glide.Glide;
import com.p3.bewoplaner.BewoplanerApplication;
import com.p3.bewoplaner.data.AppPrefsConstants;
import com.p3.bewoplaner.data.api.ApiModule;
import com.p3.bewoplaner.data.prefs.AppPrefs;
import com.p3.bewoplaner.data.prefs.AppPrefsModule;
import com.p3.bewoplaner.data.prefs.BooleanPreference;
import com.p3.bewoplaner.data.prefs.LongPreference;
import com.p3.bewoplaner.data.prefs.StringPreference;
import com.p3.bewoplaner.data.repository.GroupRealmRepository;
import com.p3.bewoplaner.data.repository.MessageRealmRepository;
import com.p3.bewoplaner.data.repository.RealmProvider;
import com.p3.bewoplaner.data.repository.RepositoryModule;
import com.p3.bewoplaner.data.repository.UserRealmRepository;
import com.p3.bewoplaner.di.scopes.ApplicationScope;
import com.p3.bewoplaner.utils.SystemUtils;
import com.securepreferences.SecurePreferences;
import android.app.NotificationManager;
import android.content.Context;
import android.content.SharedPreferences;
import javax.inject.Named;
import dagger.Component;
import retrofit2.Retrofit;
/**
* Created by imarneanu on 2/9/17.
*/
@ApplicationScope
@Component(modules = {ApplicationModule.class, AppPrefsModule.class, ApiModule.class, RepositoryModule.class})
public interface ApplicationComponent {
void inject(BewoplanerApplication conferenceApplication);
@P3DSEndpoint
Retrofit provideP3DSRetrofit();
@VoucherEndpoint
Retrofit provideVoucherRetrofit();
SystemUtils provideSystemUtils();
@AppPrefs
SecurePreferences provideSecuredPrefs();
@Named(AppPrefsConstants.API_ENDPOINT)
StringPreference provideApiEndpoint();
@Named(AppPrefsConstants.LOGIN_TOKEN)
StringPreference provideLoginToken();
@Named(AppPrefsConstants.SAVE_PASSWORD)
BooleanPreference provideSavePassword();
@Named(AppPrefsConstants.CHAT_CODE)
StringPreference provideChatCode();
@Named(AppPrefsConstants.ACCOUNT_NO)
StringPreference provideAccountNumber();
@Named(AppPrefsConstants.USER_ID)
LongPreference provideUserId();
@Named(AppPrefsConstants.AVATAR)
StringPreference provideAvatar();
@Named(AppPrefsConstants.NAME)
StringPreference provideProfileName();
@Named(AppPrefsConstants.PHONE_NUMBER)
StringPreference providePhoneNumber();
@Named(AppPrefsConstants.CHAT_OPENED)
BooleanPreference provideChatOpened();
@Named(AppPrefsConstants.APP_KILLED)
BooleanPreference provideAppKilled();
NotificationManager provideNotificationManager();
GroupRealmRepository provideGroupRealmRepository();
UserRealmRepository provideUserRealmRepository();
MessageRealmRepository provideMessageRealmRepository();
RealmProvider provideRealmProvider();
@AppPrefs
SharedPreferences provideSharedPreferences();
Glide provideGlide();
Context provideContext();
}

View File

@@ -0,0 +1,61 @@
package com.p3.bewoplaner.di;
import com.bumptech.glide.Glide;
import com.p3.bewoplaner.BewoplanerApplication;
import com.p3.bewoplaner.di.scopes.ApplicationScope;
import com.p3.bewoplaner.utils.SystemUtils;
import android.app.NotificationManager;
import android.content.Context;
import android.net.ConnectivityManager;
import dagger.Module;
import dagger.Provides;
/**
* Created by imarneanu on 2/9/17.
*/
@Module
public class ApplicationModule {
private final Context mAppContext;
public ApplicationModule(Context appContext) {
mAppContext = appContext.getApplicationContext();
}
@Provides
@ApplicationScope
Context provideContext() {
return mAppContext;
}
@Provides
@ApplicationScope
BewoplanerApplication providesBewoplanerApplication() {
return (BewoplanerApplication) mAppContext;
}
@Provides
@ApplicationScope
SystemUtils provideSystemUtils(ConnectivityManager connectivityManager) {
return new SystemUtils(connectivityManager);
}
@Provides
@ApplicationScope
ConnectivityManager provideConnectivityManager() {
return (ConnectivityManager) mAppContext.getSystemService(Context.CONNECTIVITY_SERVICE);
}
@Provides
@ApplicationScope
NotificationManager provideNotificationManager() {
return (NotificationManager) mAppContext.getSystemService(Context.NOTIFICATION_SERVICE);
}
@Provides
@ApplicationScope
Glide provideGlide() {
return Glide.get(mAppContext);
}
}

View File

@@ -0,0 +1,120 @@
package com.p3.bewoplaner.di;
import com.p3.bewoplaner.BewoplanerApplication;
import com.p3.bewoplaner.data.firebase.DaggerMessagingComponent;
import com.p3.bewoplaner.data.firebase.MessagingComponent;
import com.p3.bewoplaner.page.chat.ChatComponent;
import com.p3.bewoplaner.page.chat.DaggerChatComponent;
import com.p3.bewoplaner.page.contacts.ContactsComponent;
import com.p3.bewoplaner.page.contacts.DaggerContactsComponent;
import com.p3.bewoplaner.page.login.DaggerLoginComponent;
import com.p3.bewoplaner.page.login.LoginComponent;
import com.p3.bewoplaner.page.main.DaggerMainComponent;
import com.p3.bewoplaner.page.main.MainComponent;
import com.p3.bewoplaner.page.profile.DaggerProfileComponent;
import com.p3.bewoplaner.page.profile.ProfileComponent;
import com.p3.bewoplaner.page.splash.DaggerSplashComponent;
import com.p3.bewoplaner.page.splash.SplashComponent;
import android.content.Context;
/**
* Created by imarneanu on 2/9/17.
*/
public class InjectionHelper {
private static SplashComponent sSplashComponent;
private static ContactsComponent sContactsComponent;
private static LoginComponent sLoginComponent;
private static MainComponent sMainComponent;
private static MessagingComponent sMessagingComponent;
private static ChatComponent sChatComponent;
private static ProfileComponent sProfileComponent;
public static ApplicationComponent getApplicationComponent(Context context) {
return ((BewoplanerApplication) context.getApplicationContext()).getApplicationComponent();
}
public static SplashComponent getSplashComponent(Context context) {
if (sSplashComponent == null) {
sSplashComponent = DaggerSplashComponent.builder()
.applicationComponent(getApplicationComponent(context))
.build();
}
return sSplashComponent;
}
public static LoginComponent getLoginComponent(Context context) {
if (sLoginComponent == null) {
sLoginComponent = DaggerLoginComponent.builder()
.applicationComponent(getApplicationComponent(context))
.build();
}
return sLoginComponent;
}
public static MainComponent getMainComponent(Context context) {
if (sMainComponent == null) {
sMainComponent = DaggerMainComponent.builder()
.applicationComponent(getApplicationComponent(context))
.build();
}
return sMainComponent;
}
public static ContactsComponent getContactsComponent(Context context) {
if (sContactsComponent == null) {
sContactsComponent = DaggerContactsComponent.builder()
.applicationComponent(getApplicationComponent(context))
.build();
}
return sContactsComponent;
}
public static MessagingComponent getMessagingComponent(Context context) {
if (sMessagingComponent == null) {
sMessagingComponent = DaggerMessagingComponent.builder()
.applicationComponent(getApplicationComponent(context))
.build();
}
return sMessagingComponent;
}
public static ProfileComponent getProfileComponent(Context context) {
if (sProfileComponent == null) {
sProfileComponent = DaggerProfileComponent.builder()
.applicationComponent(getApplicationComponent(context))
.build();
}
return sProfileComponent;
}
public static ChatComponent getChatComponent(Context context) {
if (sChatComponent == null) {
sChatComponent = DaggerChatComponent.builder()
.applicationComponent(getApplicationComponent(context))
.build();
}
return sChatComponent;
}
public static void destroySplashComponent() {
sSplashComponent = null;
}
public static void destroyMainComponents() {
sContactsComponent = null;
sMainComponent = null;
sContactsComponent = null;
sProfileComponent = null;
sMessagingComponent = null;
}
public static void destroyChatComponent() {
sChatComponent = null;
}
public static void destroyLoginComponent() {
sLoginComponent = null;
}
}

View File

@@ -0,0 +1,14 @@
package com.p3.bewoplaner.di;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Qualifier;
/**
* Created by Rares Teodorescu on 24/03/2017.
*/
@Qualifier
@Retention(RetentionPolicy.SOURCE)
public @interface P3DSEndpoint {
}

View File

@@ -0,0 +1,14 @@
package com.p3.bewoplaner.di;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Qualifier;
/**
* Created by Rares Teodorescu on 24/03/2017.
*/
@Qualifier
@Retention(RetentionPolicy.SOURCE)
public @interface VoucherEndpoint {
}

View File

@@ -0,0 +1,17 @@
package com.p3.bewoplaner.di.scopes;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Scope;
/**
* Created by imarneanu on 2/9/17.
*/
@Scope
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}

View File

@@ -0,0 +1,16 @@
package com.p3.bewoplaner.di.scopes;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Scope;
/**
* Created by imarneanu on 2/9/17.
*/
@Scope
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationScope {
}

View File

@@ -0,0 +1,148 @@
package com.p3.bewoplaner.model;
import com.google.gson.annotations.SerializedName;
import com.p3.bewoplaner.data.repository.models.GroupRealm;
import com.p3.bewoplaner.data.repository.models.MessageRealm;
import com.p3.bewoplaner.data.repository.models.UserRealm;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.ArrayList;
import io.realm.RealmList;
/**
* Created by imarneanu on 2/21/17.
*/
public class Group implements Parcelable {
@SerializedName("oid")
public long id;
@SerializedName("name")
public String name;
@SerializedName("avatar")
public String avatar;
@SerializedName("mobile")
public String mobile;
@SerializedName("lastmessage")
public Message lastMessage;
@SerializedName("users")
public ArrayList<User> users;
@SerializedName("onlyemployees")
public boolean onlyEmployees;
private boolean isRead;
public long userId;
public boolean isHelpGroup() {
return !onlyEmployees;
}
public Group() {
}
public Group(long id, String name, String avatar, String mobile, MessageRealm lastMessage, RealmList<UserRealm> users, boolean onlyEmployees) {
this.users = new ArrayList<>();
this.id = id;
this.name = name;
this.avatar = avatar;
this.mobile = mobile;
this.lastMessage = Message.createFromMessageRealm(lastMessage);
this.isRead = this.lastMessage != null && this.lastMessage.isRead;
for (UserRealm user : users) {
this.users.add(new User(user.getId(), user.getFirstName(), user.getLastName(), user.getAvatar(), user.getMobile(), user.isEmployee()));
}
this.onlyEmployees = onlyEmployees;
}
public String getContactsNames() {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < users.size(); i++) {
stringBuilder.append(users.get(i).getName()).append(", ");
}
int length = stringBuilder.length();
if (length > 1) {
stringBuilder.delete(length - 2, length - 1);
}
return stringBuilder.toString();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(this.id);
dest.writeString(this.name);
dest.writeByte(this.onlyEmployees ? (byte) 1 : (byte) 0);
dest.writeList(this.users);
dest.writeString(this.avatar);
dest.writeString(this.mobile);
}
public boolean isRead() {
return lastMessage != null && lastMessage.isRead;
}
public static Group createFromGroupRealm(GroupRealm groupRealm) {
if (groupRealm == null) {
return null;
}
return new Group(groupRealm.getId(),
groupRealm.getName(),
groupRealm.getAvatar(),
groupRealm.getMobile(),
groupRealm.getMessage(),
groupRealm.getUserIdList(),
groupRealm.isOnlyEmployees());
}
protected Group(Parcel in) {
this.id = in.readLong();
this.name = in.readString();
this.onlyEmployees = in.readByte() != 0;
this.users = new ArrayList<>();
in.readList(this.users, User.class.getClassLoader());
this.avatar = in.readString();
this.mobile = in.readString();
}
public static final Parcelable.Creator<Group> CREATOR = new Parcelable.Creator<Group>() {
@Override
public Group createFromParcel(Parcel source) {
return new Group(source);
}
@Override
public Group[] newArray(int size) {
return new Group[size];
}
};
@Override
public String toString() {
return "Group{" +
"id=" + id +
", name='" + name + '\'' +
", avatar='" + avatar + '\'' +
", mobile='" + mobile + '\'' +
", lastMessage=" + lastMessage +
", users=" + users +
", onlyEmployees=" + onlyEmployees +
", isRead=" + isRead +
'}';
}
public boolean isEmployee(ArrayList<User> users) {
for (int i = 0; i < users.size(); i++) {
if (name.equals(users.get(i).getName())) {
return users.get(i).isEmployee;
}
}
return false;
}
}

View File

@@ -0,0 +1,238 @@
package com.p3.bewoplaner.model;
import com.google.gson.annotations.SerializedName;
import com.p3.bewoplaner.R;
import com.p3.bewoplaner.data.repository.models.MessageRealm;
import java.util.ArrayList;
/**
* Created by imarneanu on 2/21/17.
*/
public class Message {
private static final int STATUS_DELIVERED = 0;
private static final int STATUS_FAILED = 1;
private static final int STATUS_PENDING = 2;
@SerializedName("id")
public String id;
@SerializedName("userid")
public long senderId;
@SerializedName("user_name")
public String senderName;
@SerializedName("user_picture")
public String senderAvatar;
@SerializedName("groupid")
public long groupId;
@SerializedName("text")
public String message;
@SerializedName("original_filename")
public String originalFilename;
@SerializedName("file")
public String filePath;
@SerializedName("smaller_image")
public String smallerImage;
@SerializedName("created_at")
public long createdAt;
public Status status;
public String uri;
public boolean isRead;
public long userId;
public boolean fileDownloaded;
public Message() {
}
public Message(String id,
long groupId,
long createdAt,
String message,
String filePath,
String smallerImage,
String filename,
String uri,
long senderId,
String senderName,
String senderAvatar,
Status status,
boolean isRead,
long userId,
boolean fileDownloaded) {
this.id = id;
this.groupId = groupId;
this.createdAt = createdAt;
this.message = message;
this.filePath = filePath;
this.originalFilename = filename;
this.smallerImage = smallerImage;
this.uri = uri;
this.senderId = senderId;
this.senderName = senderName;
this.senderAvatar = senderAvatar;
this.status = status;
this.isRead = isRead;
this.userId = userId;
this.fileDownloaded = fileDownloaded;
}
public String getUniqueId() {
return MessageRealm.getUniqueId(id, userId);
}
public enum Status {
DELIVERED(R.drawable.message_delivered), FAILED(R.drawable.message_failed), PENDING(-1);
private int resourceId;
Status(int value) {
resourceId = value;
}
public static Status getStatus(int value) {
switch (value) {
case STATUS_DELIVERED:
return DELIVERED;
case STATUS_FAILED:
return FAILED;
case STATUS_PENDING:
return PENDING;
default:
return FAILED;
}
}
public static int getValue(Status status) {
if (status == null) {
return STATUS_DELIVERED;
}
switch (status) {
case DELIVERED:
return STATUS_DELIVERED;
case FAILED:
return STATUS_FAILED;
case PENDING:
return STATUS_PENDING;
}
return 1;
}
}
public int getStatusResourceId() {
return status != null ? status.resourceId : 0;
}
public static Message createFromMessageRealm(MessageRealm messageRealm) {
if (messageRealm == null) {
return null;
}
return new Message(messageRealm.getId(),
messageRealm.getGroupId(),
messageRealm.getCreatedAt(),
messageRealm.getMessage(),
messageRealm.getFilePath(),
messageRealm.getSmallerImage(),
messageRealm.getFilename(),
messageRealm.getUri(),
messageRealm.getSenderId(),
messageRealm.getSenderName(),
messageRealm.getSenderAvatar(),
Status.getStatus(messageRealm.getStatus()),
messageRealm.isRead(),
messageRealm.getUserId(),
messageRealm.isFileDownloaded());
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Message message1 = (Message) o;
if (senderId != message1.senderId) {
return false;
}
if (groupId != message1.groupId) {
return false;
}
if (createdAt != message1.createdAt && createdAt != message1.createdAt * 1000 && createdAt * 1000 != message1.createdAt) {
return false;
}
if (!id.equals(message1.id)) {
return false;
}
if (uri != null ? !uri.equals(message1.uri) : message1.uri != null) {
return false;
}
if (senderName != null ? !senderName.equals(message1.senderName) : message1.senderName != null) {
return false;
}
if (senderAvatar != null ? !senderAvatar.equals(message1.senderAvatar) : message1.senderAvatar != null) {
return false;
}
if (message != null ? !message.equals(message1.message) : message1.message != null) {
return false;
}
if (filePath != null ? !filePath.equals(message1.filePath) : message1.filePath != null) {
return false;
}
if (smallerImage != null ? !smallerImage.equals(message1.smallerImage) : message1.smallerImage != null) {
return false;
}
if (isRead != ((Message) o).isRead) {
return false;
}
return status == message1.status;
}
@Override
public int hashCode() {
int result = id.hashCode();
result = 31 * result + (int) (senderId ^ (senderId >>> 32));
result = 31 * result + (senderName != null ? senderName.hashCode() : 0);
result = 31 * result + (senderAvatar != null ? senderAvatar.hashCode() : 0);
result = 31 * result + (uri != null ? uri.hashCode() : 0);
result = 31 * result + (int) (groupId ^ (groupId >>> 32));
result = 31 * result + (message != null ? message.hashCode() : 0);
result = 31 * result + (filePath != null ? filePath.hashCode() : 0);
result = 31 * result + (smallerImage != null ? smallerImage.hashCode() : 0);
result = 31 * result + (int) (createdAt ^ (createdAt >>> 32));
result = 31 * result + (status != null ? status.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "Message{" +
"id='" + id + '\'' +
", senderId=" + senderId +
", senderName='" + senderName + '\'' +
", senderAvatar='" + senderAvatar + '\'' +
", groupId=" + groupId +
", message='" + message + '\'' +
", filePath='" + filePath + '\'' +
", smallerImage='" + smallerImage + '\'' +
", uri=" + uri +
", createdAt=" + createdAt +
", status=" + status +
", isRead=" + isRead +
'}';
}
public boolean removeFromList(ArrayList<Message> messages) {
for (int index = 0, size = messages.size(); index < size; index++) {
if (id.equals(messages.get(index).id)) {
messages.remove(index);
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,90 @@
package com.p3.bewoplaner.model;
import com.google.gson.annotations.SerializedName;
import com.p3.bewoplaner.data.repository.models.UserRealm;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by laszloffiferenc on 2/21/17.
*/
public class User implements Parcelable {
@SerializedName("oid")
public long id;
@SerializedName("firstname")
public String firstName;
@SerializedName("lastname")
public String lastName;
@SerializedName("picture")
public String avatar;
@SerializedName("mobile")
public String mobile;
@SerializedName("token")
public String token;
@SerializedName("is_employee")
public boolean isEmployee;
public User(long id, String firstName, String lastName, String avatar, String mobile, boolean isEmployee) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.avatar = avatar;
this.mobile = mobile;
this.isEmployee = isEmployee;
}
public String getName() {
return firstName.concat(" ").concat(lastName);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.firstName);
dest.writeString(this.lastName);
dest.writeString(this.avatar);
dest.writeString(this.mobile);
dest.writeByte(this.isEmployee ? (byte) 1 : (byte) 0);
}
private User(Parcel in) {
this.firstName = in.readString();
this.lastName = in.readString();
this.avatar = in.readString();
this.mobile = in.readString();
this.isEmployee = in.readByte() != 0;
}
public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() {
@Override
public User createFromParcel(Parcel source) {
return new User(source);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
public static User createFromUserRealm(UserRealm userRealm) {
if (userRealm == null) {
return null;
}
return new User(userRealm.getId(), userRealm.getFirstName(), userRealm.getLastName(),
userRealm.getAvatar(), userRealm.getMobile(), userRealm.isEmployee());
}
}

View File

@@ -0,0 +1,39 @@
package com.p3.bewoplaner.model.common;
import com.google.gson.annotations.SerializedName;
import java.util.List;
/**
* Created by laszloffiferenc on 2/21/17.
*/
public class BaseResponse {
@SerializedName("success")
public boolean success;
@SerializedName("errors")
public Error errors;
@Override
public String toString() {
return "BaseResponseModel{" +
", success=" + success +
", errors=" + errors +
'}';
}
public static class Error {
@SerializedName("login_failed")
public List<String> loginFailed;
@SerializedName("device_id")
public List<String> deviceId;
@SerializedName("fcm_token")
public List<String> fcmToken;
@SerializedName("token")
public String token;
}
}

View File

@@ -0,0 +1,496 @@
package com.p3.bewoplaner.page.chat;
import com.p3.bewoplaner.R;
import com.p3.bewoplaner.data.AppPrefsConstants;
import com.p3.bewoplaner.data.events.InvalidTokenEvent;
import com.p3.bewoplaner.data.events.LogoutDirectlyEvent;
import com.p3.bewoplaner.data.prefs.BooleanPreference;
import com.p3.bewoplaner.data.prefs.LongPreference;
import com.p3.bewoplaner.di.InjectionHelper;
import com.p3.bewoplaner.model.Group;
import com.p3.bewoplaner.model.Message;
import com.p3.bewoplaner.page.common.RuntimePermissionActivity;
import com.p3.bewoplaner.page.login.LoginPresenter;
import com.p3.bewoplaner.page.main.LogoutNavigation;
import com.p3.bewoplaner.utils.EndlessScrollListener;
import com.p3.bewoplaner.utils.FileUtils;
import com.p3.bewoplaner.utils.SystemUtils;
import com.p3.bewoplaner.utils.ui.EmptyLayout;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SimpleItemAnimator;
import android.text.Editable;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
import java.util.ArrayList;
import javax.inject.Inject;
import javax.inject.Named;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.OnTextChanged;
import hani.momanii.supernova_emoji_library.Actions.EmojIconActions;
import hani.momanii.supernova_emoji_library.Helper.EmojiconEditText;
import okhttp3.MediaType;
import static com.p3.bewoplaner.data.AppPrefsConstants.USER_ID;
/**
* Created by Rares Teodorescu on 21/02/2017.
*/
abstract class BaseChatActivity extends RuntimePermissionActivity implements ChatView, BaseChatAdapter.MessageClickListener, LogoutNavigation {
private final static int STORAGE_REQUEST_CODE = 9999;
private final static int SELECT_FILE = 5055;
private final static int VISIBLE_THRESHOLD = 25;
protected final static String MESSAGE_ARG = "message_arg";
protected final static String GROUP_ARG = "group_arg";
@BindView(R.id.chat_toolbar_image_view)
ImageView mChatImageView;
@BindView(R.id.chat_toolbar_single_title)
TextView mSingleChatTitleTextView;
@BindView(R.id.chat_toolbar_group_title)
TextView mGroupTitleTextView;
@BindView(R.id.chat_toolbar_group_members)
TextView mGroupMembersTextView;
@BindView(R.id.chat_toolbar_extra_image)
ImageView mExtraImageView;
@BindView(R.id.chat_message)
EmojiconEditText mChatMessageEmojiEditText;
@BindView(R.id.chat_recycler_view)
RecyclerView mRecyclerView;
@BindView(R.id.chat_empty_layout)
EmptyLayout mEmptyLayout;
@BindView(R.id.emoji_button)
ImageView mEmojiButton;
@BindView(R.id.chat_send_message)
ImageView mSendMessageImageView;
@BindView(R.id.chat_coordinator_layout)
CoordinatorLayout mCoordinatorLayout;
@Inject
@Named(USER_ID)
LongPreference mUserIdPreference;
@Inject
ChatPresenter mChatPresenter;
@Inject
SystemUtils mSystemUtils;
@Inject
LoginPresenter mLoginPresenter;
protected Group mGroup;
protected ChatView.Callback mCallback;
protected EndlessScrollListener mEndlessScrollListener;
protected BaseChatAdapter mBaseChatAdapter;
private Snackbar mSnackbar;
@Inject
@Named(AppPrefsConstants.CHAT_OPENED)
BooleanPreference mChatOpenedPref;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
if (!getIntent().hasExtra(GROUP_ARG) && !getIntent().hasExtra(MESSAGE_ARG)) {
throw new RuntimeException("Arguments are missing. Please use createIntentMethodInstead;");
}
super.onCreate(savedInstanceState);
EventBus.getDefault().register(this);
setContentView(R.layout.activity_chat);
ButterKnife.bind(this);
mSendMessageImageView.setEnabled(false);
InjectionHelper.getChatComponent(this).inject(this);
mBaseChatAdapter = getAdapter();
mEmptyLayout.setOnTryAgainClickListener(v -> mCallback.fetchMoreMessagesByGroupId());
setupRecyclerView();
EmojIconActions emojIcon = new EmojIconActions(this, mCoordinatorLayout, mChatMessageEmojiEditText, mEmojiButton);
emojIcon.ShowEmojIcon();
showLoading();
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(mKeyboardListener);
}
private void setupRecyclerView() {
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setStackFromEnd(true);
linearLayoutManager.setReverseLayout(true);
mRecyclerView.setLayoutManager(linearLayoutManager);
mBaseChatAdapter.setMessageClickListener(this);
mRecyclerView.setAdapter(mBaseChatAdapter);
((SimpleItemAnimator) mRecyclerView.getItemAnimator()).setSupportsChangeAnimations(false);
mEndlessScrollListener = new EndlessScrollListener(VISIBLE_THRESHOLD, linearLayoutManager) {
@Override
public boolean onLoad() {
if (mCallback == null || mCallback.isLoading()) {
return false;
}
mCallback.fetchMoreMessagesByGroupId();
return true;
}
};
mRecyclerView.addOnScrollListener(mEndlessScrollListener);
}
@OnClick(R.id.chat_toolbar_extra_image)
void extraImageViewClicked() {
extraImageClicked();
}
protected abstract void extraImageClicked();
protected abstract void setToolbar();
protected abstract BaseChatAdapter getAdapter();
protected abstract void loadMessages();
@Override
public void setCallback(Callback callback) {
mCallback = callback;
}
@Override
public void messagesLoaded(ArrayList<Message> messages, boolean isFirstPage) {
if (isFirstPage) {
mEndlessScrollListener.reset();
mBaseChatAdapter.setMessages(messages);
mRecyclerView.scrollToPosition(0);
return;
}
mBaseChatAdapter.addMessages(messages);
}
@Override
public void showError(@StringRes int errorRes) {
if (mSnackbar == null) {
mSnackbar = Snackbar.make(mCoordinatorLayout, errorRes, Snackbar.LENGTH_LONG);
mSnackbar.setActionTextColor(ContextCompat.getColor(this, R.color.colorDefault));
}
mSnackbar.setText(errorRes);
mSnackbar.show();
}
@Override
public void showLoading() {
mEmptyLayout.showLoading();
}
@Override
public void hideLoading() {
mEmptyLayout.setVisibility(View.GONE);
}
@Override
public void showMessagesLoading() {
mBaseChatAdapter.setLoading(true);
}
@Override
public void hideMessagesLoading() {
mBaseChatAdapter.setLoading(false);
}
@Override
public void showEmptyMessage() {
mEmptyLayout.setEmptyText(getString(R.string.chat_no_messages));
mEmptyLayout.showEmpty();
}
@OnClick(R.id.chat_attachment)
void attachmentClicked() {
requestAppPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
R.string.permission_storage_explained,
STORAGE_REQUEST_CODE,
this::selectFile);
}
@OnClick(R.id.chat_toolbar_back_arrow)
void backPressed() {
onBackPressed();
}
@OnClick(R.id.chat_send_message)
void messageSent() {
mLoginPresenter.checkIfVoucherStillAvailable(this);
mCallback.sendMessage(mChatMessageEmojiEditText.getText().toString());
mChatMessageEmojiEditText.setText("");
}
@OnTextChanged(value = R.id.chat_message, callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
void messageTextChanged(Editable editable) {
mSendMessageImageView.setEnabled(!TextUtils.isEmpty(editable.toString()) && editable.toString().trim().length() > 0);
}
private void selectFile(int requestCode) {
Intent intent;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
} else {
intent = new Intent(Intent.ACTION_GET_CONTENT);
}
intent.setType("*/*");
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(Intent.createChooser(intent, getResources().getString(R.string.chat_select_file)), SELECT_FILE);
}
@Override
protected void onResume() {
super.onResume();
mLoginPresenter.checkIfVoucherStillAvailable(this);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode != Activity.RESULT_OK) {
return;
}
if (null == data) {
return;
}
if (requestCode == SELECT_FILE) {
if (null == data.getData()) {
return;
}
final Uri uri = data.getData();
if (Build.VERSION.SDK_INT >= 19) {
final int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// Check for the freshest data.
try {
getContentResolver().takePersistableUriPermission(uri, takeFlags);
} catch (Exception e) {
e.printStackTrace();
}
}
if (!mChatPresenter.isAttached()) {
mChatPresenter.attachView(this);
}
// Show preview for images/gifs and maybe videos
mCallback.sendFile(uri);
}
}
@Override
public void logoutDirectly() {
EventBus.getDefault().post(new LogoutDirectlyEvent());
finish();
}
@Override
public void showToastMessage(@StringRes int message) {
Toast.makeText(BaseChatActivity.this, message, Toast.LENGTH_SHORT).show();
}
@Override
public void fileClicked(Message message, int itemPosition) {
if (!mChatPresenter.isAttached()) {
mChatPresenter.attachView(this);
}
String[] split = message.filePath.split("/");
String fileName = split[split.length - 1];
File file = getExternalFilesDir(null);
if (file != null) {
mCallback.downloadFile(message, file + "/" + fileName, itemPosition);
return;
}
file = new File(Environment.getExternalStorageDirectory(), getString(R.string.app_name) + "/" + fileName);
//noinspection ResultOfMethodCallIgnored
file.mkdirs();
mCallback.downloadFile(message, file.getAbsolutePath(), itemPosition);
}
@Override
public void localFileClicked(Uri uri) {
File file = getFile(uri);
openFile(uri, FileUtils.getFileTypeFromFilePath(file.getAbsolutePath()));
}
@Override
public void resendMessage(Message message) {
mLoginPresenter.checkIfVoucherStillAvailable(this);
if (!mChatPresenter.isAttached()) {
mChatPresenter.attachView(this);
}
mCallback.resendMessage(message);
}
@Override
public MediaType getMediaType(Uri fileUri) {
return MediaType.parse(getContentResolver().getType(fileUri));
}
@Override
public File getFile(Uri fileUri) {
return FileUtils.getFile(this, fileUri);
}
@Override
public void enableMessageRetry(String messageId) {
mBaseChatAdapter.enableMessageRetry(messageId);
}
@Override
public void showItemProgress(int itemPosition, int progress) {
runOnUiThread(() -> mBaseChatAdapter.setItemProgress(itemPosition, progress));
}
@Override
public void stopItemProgress(int itemPosition) {
mBaseChatAdapter.stopItemProgress(itemPosition);
}
@Override
public void fileDownloadedSuccessfully(String filePath, String fileType) {
openFile(FileProvider.getUriForFile(BaseChatActivity.this,
getApplicationContext().getPackageName() + ".provider",
new File(filePath)),
fileType);
}
private void openFile(Uri uri, String fileType) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(uri, fileType);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// Verify the original intent will resolve to at least one activity
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
} else {
showToastMessage(R.string.error_could_not_open_file);
}
}
@Override
public void lastMessageUpdated(Message newMessage, String oldMessageId) {
mBaseChatAdapter.updateMessage(newMessage, oldMessageId);
}
@Override
protected void onPostCreate(@Nullable Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
mChatOpenedPref.set(false);
}
@Override
protected void onDestroy() {
mChatOpenedPref.set(false);
if (mEndlessScrollListener != null) {
mRecyclerView.removeOnScrollListener(mEndlessScrollListener);
}
getWindow().getDecorView().getViewTreeObserver().removeOnGlobalLayoutListener(mKeyboardListener);
EventBus.getDefault().unregister(this);
InjectionHelper.destroyChatComponent();
mRecyclerView.setAdapter(null);
mEmptyLayout.setOnTryAgainClickListener(null);
super.onDestroy();
}
@Override
public void addOneMessage(Message message) {
mBaseChatAdapter.addMessage(message);
if (mEndlessScrollListener.getVisibleItem() == 0) {
mBaseChatAdapter.notifyDataSetChanged();
}
mRecyclerView.scrollToPosition(0);
}
protected void setGroupFromArguments() {
mGroup = getIntent().getParcelableExtra(GROUP_ARG);
cancelNotification();
loadMessages();
setToolbar();
}
@SuppressWarnings("unused")
@Subscribe(threadMode = ThreadMode.MAIN)
public void onInvalidToken(InvalidTokenEvent invalidTokenEvent) {
finish();
}
protected void cancelNotification() {
if (mGroup == null) {
return;
}
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
notificationManager.cancel((int) mGroup.id);
}
private ViewTreeObserver.OnGlobalLayoutListener mKeyboardListener = new ViewTreeObserver.OnGlobalLayoutListener() {
private boolean mKeyboardShown = false;
@Override
public void onGlobalLayout() {
Rect r = new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
int screenHeight = getWindow().getDecorView().getHeight();
// r.bottom is the position above soft keypad or device button.
// if keypad is shown, the r.bottom is smaller than that before.
int keypadHeight = screenHeight - r.bottom;
if (keypadHeight > screenHeight * 0.15 && !mKeyboardShown) { // 0.15 ratio is perhaps enough to determine keypad height.
// keyboard is opened
mRecyclerView.scrollToPosition(0);
mKeyboardShown = true;
} else {
mKeyboardShown = false;
}
}
};
}

View File

@@ -0,0 +1,439 @@
package com.p3.bewoplaner.page.chat;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.github.lzyzsd.circleprogress.DonutProgress;
import com.p3.bewoplaner.R;
import com.p3.bewoplaner.model.Message;
import com.p3.bewoplaner.utils.DateUtils;
import com.p3.bewoplaner.utils.ui.ViewUtils;
import android.net.Uri;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.Optional;
import hani.momanii.supernova_emoji_library.Helper.EmojiconTextView;
/**
* Created by Rares Teodorescu on 21/02/2017.
*/
abstract class BaseChatAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
final static int DATE_TYPE = 0;
final static int LEFT_CHAT_TYPE = 1;
final static int RIGHT_CHAT_TYPE = 2;
final static int LOADING_TYPE = 3;
private long mUserId;
final ArrayList<Message> mMessages = new ArrayList<>();
private final List<Integer> mDatePositions = new ArrayList<>();
final List<Date> mDateList = new ArrayList<>();
final List<Integer> mPositionOffset = new ArrayList<>();
private boolean mIsLoading;
private int mLastOffset = 0;
private Calendar mCurrentDay;
private Calendar mLastDay;
private MessageClickListener mMessageClickListener;
private boolean mIsRetrying;
protected HashMap<Integer, Integer> mDownloadProgressMap = new HashMap<>();
BaseChatAdapter(long userId) {
mUserId = userId;
mCurrentDay = Calendar.getInstance();
mLastDay = Calendar.getInstance();
}
void setMessages(ArrayList<Message> messages) {
mMessages.clear();
mDatePositions.clear();
mDateList.clear();
mPositionOffset.clear();
mIsLoading = false;
mLastOffset = 0;
mCurrentDay = Calendar.getInstance();
mLastDay = Calendar.getInstance();
mIsRetrying = false;
if (!messages.isEmpty()) {
mPositionOffset.add(0);
mDateList.add(new Date(messages.get(0).createdAt));
}
int messageSize = messages.size();
for (int i = 1; i < messageSize; i++) {
mCurrentDay.setTimeInMillis(messages.get(i).createdAt);
mLastDay.setTimeInMillis(messages.get(i - 1).createdAt);
if (mCurrentDay.get(Calendar.DAY_OF_YEAR) != mLastDay.get(Calendar.DAY_OF_YEAR)) {
mDatePositions.add(i + mLastOffset);
mPositionOffset.add(0);
mDateList.add(mLastDay.getTime());
mLastOffset++;
}
mPositionOffset.add(mLastOffset);
mDateList.add(new Date(messages.get(i).createdAt));
}
mMessages.addAll(messages);
notifyDataSetChanged();
}
void addMessages(ArrayList<Message> messages) {
if (messages == null || messages.isEmpty() || mMessages.equals(messages)) {
return;
}
int currentMessagesCount = mMessages.size();
int lastOffsetBeforeParsing = mLastOffset;
int datesInserted = 0;
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(messages.get(0).createdAt);
if (mCurrentDay.get(Calendar.DAY_OF_YEAR) != calendar.get(Calendar.DAY_OF_YEAR)) {
mDatePositions.add(currentMessagesCount + mLastOffset);
datesInserted++;
mDateList.add(new Date(messages.get(0).createdAt));
mPositionOffset.add(0);
mLastOffset++;
}
mPositionOffset.add(mLastOffset);
mDateList.add(new Date(messages.get(0).createdAt));
int messageSize = messages.size();
for (int i = 1; i < messageSize; i++) {
mCurrentDay.setTimeInMillis(messages.get(i).createdAt);
mLastDay.setTimeInMillis(messages.get(i - 1).createdAt);
if (mCurrentDay.get(Calendar.DAY_OF_YEAR) != mLastDay.get(Calendar.DAY_OF_YEAR)) {
mDatePositions.add(currentMessagesCount + mLastOffset + i);
datesInserted++;
mPositionOffset.add(0);
mDateList.add(mLastDay.getTime());
mLastOffset++;
}
mPositionOffset.add(mLastOffset);
mDateList.add(new Date(messages.get(i).createdAt));
}
mMessages.addAll(messages);
notifyItemRangeInserted(currentMessagesCount + lastOffsetBeforeParsing, messages.size() + datesInserted);
}
void setItemProgress(int itemPosition, int progress) {
mDownloadProgressMap.put(itemPosition, progress);
notifyItemChanged(itemPosition);
}
void stopItemProgress(int itemPosition) {
mDownloadProgressMap.remove(itemPosition);
notifyItemChanged(itemPosition);
}
void resetItemsProgress() {
mDownloadProgressMap.clear();
notifyDataSetChanged();
}
void addMessage(Message message) {
if (mMessages.contains(message)) {
return;
}
ArrayList<Message> currentMessages = new ArrayList<>();
currentMessages.add(message);
currentMessages.addAll(mMessages);
mMessages.clear();
mMessages.addAll(currentMessages);
Calendar lastDay = Calendar.getInstance();
if (mMessages.size() > 1) {
lastDay.setTimeInMillis(mMessages.get(1).createdAt);
}
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(message.createdAt);
if (calendar.get(Calendar.DAY_OF_YEAR) != lastDay.get(Calendar.DAY_OF_YEAR)) {
// New date inserted
setMessages(new ArrayList<>(mMessages));
return;
}
List<Integer> positionOffset = new ArrayList<>();
positionOffset.add(0);
positionOffset.addAll(mPositionOffset);
mPositionOffset.clear();
mPositionOffset.addAll(positionOffset);
List<Date> currentDateList = new ArrayList<>();
currentDateList.add(new Date(message.createdAt));
currentDateList.addAll(mDateList);
mDateList.clear();
mDateList.addAll(currentDateList);
List<Integer> currentDatePositions = new ArrayList<>();
boolean newDateAdded = false;
if (calendar.get(Calendar.DAY_OF_YEAR) != lastDay.get(Calendar.DAY_OF_YEAR)) {
currentDatePositions.add(1);
newDateAdded = true;
}
for (int i = 0, size = mDatePositions.size(); i < size; i++) {
currentDatePositions.add(mDatePositions.get(i) + (newDateAdded ? 2 : 1));
}
mDatePositions.clear();
mDatePositions.addAll(currentDatePositions);
notifyItemInserted(0);
}
void setLoading(boolean loading) {
mIsLoading = loading;
if (loading) {
notifyItemRangeInserted(getItemCount(), 1);
} else {
notifyItemRemoved(getItemCount() - 1);
}
}
@Override
public int getItemCount() {
return mMessages.size() + mLastOffset + (mIsLoading ? 1 : 0);
}
void updateMessage(Message newMessage, String oldMessageId) {
int index = -1;
for (int i = 0; i < mMessages.size(); i++) {
if (oldMessageId.equals(mMessages.get(i).id)) {
index = i;
break;
}
}
if (index == -1) {
Log.e("BaseChatAdapter", "message not found!! " + newMessage.toString());
return;
}
mMessages.set(index, newMessage);
notifyItemChanged(index);
}
@Override
public int getItemViewType(int position) {
if (mDatePositions.contains(position)) {
return DATE_TYPE;
}
if (position == mMessages.size() + mLastOffset) {
return LOADING_TYPE;
}
return mMessages.get(position - mPositionOffset.get(position)).senderId == mUserId ?
RIGHT_CHAT_TYPE : LEFT_CHAT_TYPE;
}
void setMessageClickListener(MessageClickListener messageClickListener) {
mMessageClickListener = messageClickListener;
}
void enableMessageRetry(String messageId) {
mIsRetrying = false;
Message myMessage = null;
int index = -1;
for (int i = 0; i < mMessages.size(); i++) {
Message message = mMessages.get(i);
if (messageId.equals(message.id)) {
index = i;
myMessage = message;
myMessage.status = Message.Status.FAILED;
break;
}
}
if (myMessage == null) {
Log.e("BaseChatAdapter", "message not found!! " + messageId);
return;
}
mMessages.set(index, myMessage);
notifyItemChanged(index);
}
void setSingleMessage(MessageViewHolder viewHolder, Message message, int position, boolean isLeft) {
setMessage(viewHolder, message, position, false, isLeft);
}
void setGroupMessage(MessageViewHolder viewHolder, Message message, int position, boolean isLeft) {
setMessage(viewHolder, message, position, true, isLeft);
}
private void setMessage(MessageViewHolder viewHolder, Message message, int position, boolean isGroup, boolean isLeft) {
if (isGroup) {
viewHolder.timeTextView.setText(String.format(Locale.getDefault(), "%s | %s", isLeft ? message.senderName : viewHolder.timeTextView.getContext().getString(R.string.chat_you_name), DateUtils.toHourFormatHour(message.createdAt)));
if (TextUtils.isEmpty(message.senderAvatar)) {
//noinspection ConstantConditions
viewHolder.contactImageView.setImageResource(R.drawable.avatar);
} else {
//noinspection ConstantConditions
Glide.with(viewHolder.contactImageView.getContext())
.load(message.senderAvatar)
.placeholder(R.drawable.avatar)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(viewHolder.contactImageView);
}
} else {
viewHolder.timeTextView.setText(DateUtils.toHourFormatHour(message.createdAt));
}
if (!TextUtils.isEmpty(message.filePath)) {
viewHolder.messageTextView.setVisibility(View.GONE);
if (mDownloadProgressMap.containsKey(position)) {
viewHolder.fileImageView.setVisibility(View.GONE);
viewHolder.donutProgress.setVisibility(View.VISIBLE);
viewHolder.donutProgress.setProgress(mDownloadProgressMap.get(position));
viewHolder.donutProgress.setText(String.format(Locale.getDefault(), "%d %%", mDownloadProgressMap.get(position)));
if (!message.filePath.endsWith(".png") && !message.filePath.endsWith(".jpg") && !message.filePath.endsWith(".JPG")) {
viewHolder.filenameTextView.setVisibility(View.VISIBLE);
viewHolder.filenameTextView.setText(message.originalFilename);
} else {
viewHolder.filenameTextView.setVisibility(View.GONE);
}
} else if (message.filePath.endsWith(".png") || message.filePath.endsWith(".jpg") || message.filePath.endsWith(".JPG")) {
viewHolder.donutProgress.setVisibility(View.GONE);
viewHolder.fileImageView.setVisibility(View.VISIBLE);
viewHolder.filenameTextView.setVisibility(View.GONE);
viewHolder.fileImageView.setImageDrawable(null);
Glide.with(viewHolder.fileImageView.getContext())
.load(message.smallerImage)
.placeholder(R.drawable.document)
.dontAnimate()
.override(500, 500)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(viewHolder.fileImageView);
} else {
viewHolder.donutProgress.setVisibility(View.GONE);
viewHolder.fileImageView.setVisibility(View.VISIBLE);
viewHolder.filenameTextView.setVisibility(View.VISIBLE);
viewHolder.fileImageView.setImageDrawable(null);
viewHolder.fileImageView.setImageResource(R.drawable.document);
viewHolder.filenameTextView.setText(message.originalFilename);
}
if (!TextUtils.isEmpty(message.message)) {
viewHolder.messageTextView.setVisibility(View.VISIBLE);
viewHolder.messageTextView.setText(message.message);
}
} else {
viewHolder.donutProgress.setVisibility(View.GONE);
viewHolder.fileImageView.setVisibility(View.GONE);
viewHolder.filenameTextView.setVisibility(View.GONE);
viewHolder.messageTextView.setVisibility(View.VISIBLE);
viewHolder.messageTextView.setText(message.message);
}
}
static class DateViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.chat_item_date)
TextView dateTextView;
DateViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
class MessageViewHolder extends RecyclerView.ViewHolder {
@Nullable
@BindView(R.id.chat_item_message_status_progress_bar)
ProgressBar statusProgressBar;
@Nullable
@BindView(R.id.chat_item_message_status)
ImageView statusImageView;
@Nullable
@BindView(R.id.chat_item_sending_failed)
TextView sendingFailedTextView;
@Nullable
@BindView(R.id.chat_item_contact_image)
ImageView contactImageView;
@BindView(R.id.chat_item_time)
TextView timeTextView;
@BindView(R.id.chat_item_message)
EmojiconTextView messageTextView;
@BindView(R.id.chat_item_file)
ImageView fileImageView;
@BindView(R.id.chat_item_progress)
DonutProgress donutProgress;
@BindView(R.id.chat_item_file_name)
TextView filenameTextView;
MessageViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
@Optional
@OnClick(R.id.chat_item_sending_failed)
void retrySendingClicked() {
if (mIsRetrying) {
return;
}
mIsRetrying = true;
int currentPosition = getAdapterPosition();
Message messageToResend = mMessages.get(currentPosition - mPositionOffset.get(currentPosition));
//noinspection ConstantConditions
statusProgressBar.setVisibility(View.VISIBLE);
ViewUtils.setProgressBarColorToOrange(statusProgressBar);
//noinspection ConstantConditions
statusImageView.setVisibility(View.GONE);
//noinspection ConstantConditions
sendingFailedTextView.setVisibility(View.GONE);
mMessageClickListener.resendMessage(messageToResend);
}
@Optional
@OnClick(R.id.chat_item_file)
void fileClicked() {
int currentPosition = getAdapterPosition();
Message message = mMessages.get(currentPosition - mPositionOffset.get(currentPosition));
if (message.status != Message.Status.DELIVERED) {
mMessageClickListener.localFileClicked(Uri.parse(message.uri));
return;
}
if (mDownloadProgressMap.containsKey(currentPosition)) {
return;
}
mDownloadProgressMap.put(currentPosition, 0);
mMessageClickListener.fileClicked(message, getAdapterPosition());
}
}
static class ProgressViewHolder extends RecyclerView.ViewHolder {
ProgressViewHolder(View itemView) {
super(itemView);
}
}
interface MessageClickListener {
void fileClicked(Message message, int itemPosition);
void localFileClicked(Uri uri);
void resendMessage(Message message);
}
}

View File

@@ -0,0 +1,17 @@
package com.p3.bewoplaner.page.chat;
import com.p3.bewoplaner.di.ApplicationComponent;
import com.p3.bewoplaner.di.scopes.ActivityScope;
import com.p3.bewoplaner.page.login.LoginModule;
import dagger.Component;
/**
* Created by Rares Teodorescu on 21/02/2017.
*/
@ActivityScope
@Component(dependencies = ApplicationComponent.class,
modules = {LoginModule.class, ChatModule.class})
public interface ChatComponent {
void inject(BaseChatActivity baseChatActivity);
}

View File

@@ -0,0 +1,23 @@
package com.p3.bewoplaner.page.chat;
import com.p3.bewoplaner.data.api.services.ChatService;
import com.p3.bewoplaner.di.P3DSEndpoint;
import com.p3.bewoplaner.di.scopes.ActivityScope;
import dagger.Module;
import dagger.Provides;
import retrofit2.Retrofit;
/**
* Created by imarneanu on 3/1/17.
*/
@Module
public class ChatModule {
@Provides
@ActivityScope
ChatService provideChatService(@P3DSEndpoint Retrofit retrofit) {
return retrofit.create(ChatService.class);
}
}

View File

@@ -0,0 +1,986 @@
package com.p3.bewoplaner.page.chat;
import com.p3.bewoplaner.R;
import com.p3.bewoplaner.data.AppPrefsConstants;
import com.p3.bewoplaner.data.api.common.ErrorHandlingResponseConverter;
import com.p3.bewoplaner.data.api.responses.MessageResponse;
import com.p3.bewoplaner.data.api.responses.MessagesResponse;
import com.p3.bewoplaner.data.api.services.ChatService;
import com.p3.bewoplaner.data.events.GroupUpdatedEvent;
import com.p3.bewoplaner.data.prefs.LongPreference;
import com.p3.bewoplaner.data.prefs.StringPreference;
import com.p3.bewoplaner.data.repository.GroupRealmRepository;
import com.p3.bewoplaner.data.repository.MessageRealmRepository;
import com.p3.bewoplaner.data.repository.RealmProvider;
import com.p3.bewoplaner.data.repository.models.MessageRealm;
import com.p3.bewoplaner.data.repository.specifications.GroupByIdSpecification;
import com.p3.bewoplaner.data.repository.specifications.MessageByGroupIdSpecification;
import com.p3.bewoplaner.data.repository.specifications.MessageByIdSpecification;
import com.p3.bewoplaner.di.scopes.ActivityScope;
import com.p3.bewoplaner.model.Group;
import com.p3.bewoplaner.model.Message;
import com.p3.bewoplaner.page.mvp.MVPAbstractPresenter;
import com.p3.bewoplaner.utils.SystemUtils;
import com.p3.bewoplaner.utils.rx.RxUtils;
import org.greenrobot.eventbus.EventBus;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.v4.util.Pair;
import android.text.TextUtils;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.inject.Inject;
import javax.inject.Named;
import io.realm.Realm;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import rx.Completable;
import rx.Observable;
import rx.Subscriber;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Func1;
import rx.schedulers.Schedulers;
import static com.p3.bewoplaner.data.AppPrefsConstants.USER_ID;
import static com.p3.bewoplaner.utils.FileUtils.getFileTypeFromFilePath;
/**
* Created by Rares Teodorescu on 21/02/2017.
*/
@ActivityScope
public class ChatPresenter extends MVPAbstractPresenter<ChatView>
implements ChatView.Callback {
private static final int FIRST_PAGE = 1;
private static final int GROUP_ID_NOT_SET = -999;
private static final int LIMIT = 25;
@Inject
ChatService mChatService;
@Inject
MessageRealmRepository mMessageRealmRepository;
@Inject
GroupRealmRepository mGroupRealmRepository;
@Inject
SystemUtils mSystemUtils;
@Inject
@Named(USER_ID)
LongPreference mUserIdPreference;
@Inject
@Named(AppPrefsConstants.AVATAR)
StringPreference mAvatarPreference;
@Inject
@Named(AppPrefsConstants.NAME)
StringPreference mProfileNamePreference;
@Inject
RealmProvider mRealmProvider;
@Inject
ChatPresenter() {
}
private long mGroupId = GROUP_ID_NOT_SET;
private boolean mLoading = false;
private int mPage = FIRST_PAGE;
private int mOffset;
private boolean mFetchMore;
private ArrayList<Message> mMessages = new ArrayList<>();
private ArrayList<Pair<Message, String>> mMessageToUpdate = new ArrayList<>();
/**
* Get all messages from the server with a specific groupId.
*/
@Override
public void loadMessagesFromServerByGroupId(long groupId) {
if (!mFetchMore) {
getView().hideMessagesLoading();
return;
}
mGroupId = groupId;
Subscription subscription = mChatService.getMessages(groupId, mPage)
.compose(RxUtils.provideDefaultTransformer())
.flatMap(new ErrorHandlingResponseConverter<>())
.flatMap(new Func1<MessagesResponse, Observable<ArrayList<Message>>>() {
@Override
public Observable<ArrayList<Message>> call(MessagesResponse messageResponse) {
ArrayList<Message> messages = messageResponse.response.messages;
for (int i = 0, size = messages.size(); i < size; i++) {
messages.get(i).status = Message.Status.DELIVERED;
messages.get(i).isRead = true;
messages.get(i).userId = mUserIdPreference.get();
}
mFetchMore = messageResponse.response.hasMorePages;
return Observable.just(messageResponse.response.messages);
}
})
.subscribe(this::messagesSuccess, this::messagesError);
addSubscription(subscription);
}
/**
* Get messages from page 0.
*/
@Override
public void fetchNewMessages(long groupId) {
mGroupId = groupId;
Subscription subscription = mChatService.getMessages(mGroupId, 0)
.compose(RxUtils.provideDefaultTransformer())
.flatMap(new ErrorHandlingResponseConverter<>())
.flatMap(new Func1<MessagesResponse, Observable<List<Message>>>() {
@Override
public Observable<List<Message>> call(MessagesResponse messageResponse) {
if (messageResponse.response.messages == null || messageResponse.response.messages.isEmpty()) {
return Observable.just(null);
}
Message lastMessage = messageResponse.response.messages.get(messageResponse.response.messages.size() - 1);
lastMessage.status = Message.Status.DELIVERED;
lastMessage.isRead = true;
if (!mMessages.contains(lastMessage)) {
long groupId = mGroupId;
resetMessages();
loadMessagesFromServerByGroupId(groupId);
return Observable.just(null);
}
for (int i = 0; i < mMessageToUpdate.size(); i++) {
Pair<Message, String> messageToUpdate = mMessageToUpdate.get(i);
if (isAttached()) {
getView().lastMessageUpdated(messageToUpdate.first, messageToUpdate.second);
}
}
mMessageToUpdate.clear();
ArrayList<Message> messages = new ArrayList<>();
for (int i = messageResponse.response.messages.size() - 1; i >= 0; i--) {
Message message = messageResponse.response.messages.get(i);
message.status = Message.Status.DELIVERED;
message.isRead = true;
message.userId = mUserIdPreference.get();
if (mMessages.contains(message)) {
continue;
}
messages.add(message);
}
return mMessageRealmRepository.updateMessages(messages, new MessageByIdSpecification(mUserIdPreference.get()));
}
})
.subscribe(this::fetchNewMessagesSuccess, this::fetchNewMessagesError);
addSubscription(subscription);
}
/**
* Send messages to view.
*/
private void fetchNewMessagesSuccess(List<Message> messages) {
if (messages == null) {
return;
}
for (int i = 0; i < messages.size(); i++) {
if (!isAttached()) {
break;
}
Message message = messages.get(i);
mMessages.add(message);
getView().addOneMessage(message);
}
}
/**
* Send error messages to view.
*/
private void fetchNewMessagesError(Throwable throwable) {
Log.e("fetchNewMessages", "onError", throwable);
if (!isAttached()) {
return;
}
if (mSystemUtils.isNetworkUnavailable()) {
getView().showError(R.string.error_no_internet_connection);
return;
}
getView().showError(R.string.error_something_went_wrong);
}
@Override
public void resetMessages() {
mGroupId = GROUP_ID_NOT_SET;
mLoading = false;
mPage = FIRST_PAGE;
mOffset = 0;
mFetchMore = true;
}
/**
* Save messages to local database.
*/
private void messagesSuccess(ArrayList<Message> messages) {
mMessageRealmRepository.update(messages, new MessageByIdSpecification(mUserIdPreference.get()))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Object>() {
@Override
public void onCompleted() {
loadMessagesFromDBByGroupId(mGroupId, true);
}
@Override
public void onError(Throwable e) {
Log.e(ChatPresenter.class.getSimpleName(), e.getMessage(), e);
}
@Override
public void onNext(Object o) {
// nop here
}
});
}
private void messagesError(Throwable throwable) {
Log.e(ChatPresenter.class.getSimpleName(), throwable.getMessage(), throwable);
getView().hideLoading();
getView().hideMessagesLoading();
if (mSystemUtils.isNetworkUnavailable()) {
getView().showError(R.string.error_no_internet_connection);
return;
}
getView().showError(R.string.error_something_went_wrong);
}
@Override
public void loadMessagesFromDBByGroupId(long groupId, boolean fetchWhenFinished) {
mGroupId = groupId;
loadMessagesFromDBByGroupId();
}
/**
* Get messages from database using a specific groupId.
*/
private void loadMessagesFromDBByGroupId() {
MessageByGroupIdSpecification messageByGroupIdSpecification = new MessageByGroupIdSpecification(mUserIdPreference.get());
messageByGroupIdSpecification.setGroupId(mGroupId);
mMessageRealmRepository.queryForAll(messageByGroupIdSpecification)
.map(ArrayList::new)
.compose(RxUtils.provideDefaultTransformer())
.subscribe(this::loadMessagesFromDBSuccessful, this::loadMessagesError);
}
/**
* Send messages to view.
*/
private void loadMessagesFromDBSuccessful(ArrayList<Message> messages) {
if (!isAttached()) {
return;
}
mMessages.clear();
mMessages.addAll(messages);
mLoading = false;
getView().hideLoading();
getView().hideMessagesLoading();
if (!mMessages.isEmpty()) {
int endOffset = mOffset + LIMIT > mMessages.size() ? mMessages.size() : mOffset + LIMIT;
if (mOffset > endOffset) {
return;
}
boolean isFirstPage = mPage == FIRST_PAGE;
getView().messagesLoaded(new ArrayList<>(mMessages.subList(mOffset, endOffset)), isFirstPage);
if (isFirstPage && mSystemUtils.isNetworkUnavailable()) {
Message firstMessage = mMessages.get(0);
Message message = new Message(firstMessage.id,
firstMessage.groupId,
firstMessage.createdAt / 1000,
firstMessage.message,
firstMessage.filePath,
firstMessage.smallerImage,
firstMessage.originalFilename,
firstMessage.uri,
firstMessage.senderId,
firstMessage.senderName,
firstMessage.senderAvatar,
firstMessage.status,
true,
firstMessage.userId,
firstMessage.fileDownloaded);
storeGroupLocally(mGroupId, message);
}
mPage++;
mOffset += LIMIT;
return;
}
getView().showEmptyMessage();
}
private void loadMessagesError(Throwable throwable) {
if (!isAttached()) {
return;
}
mLoading = false;
getView().hideLoading();
getView().hideMessagesLoading();
}
/**
* If mMessages size is bigger then 25 and network is available, get more messages from the server.
*/
@Override
public void fetchMoreMessagesByGroupId() {
if (mMessages.size() < 25) {
getView().hideMessagesLoading();
return;
}
mLoading = true;
if (mPage == FIRST_PAGE) {
getView().showLoading();
} else {
getView().showMessagesLoading();
}
if (mSystemUtils.isNetworkUnavailable()) {
loadMessagesFromDBByGroupId();
getView().showError(R.string.error_no_internet_connection);
return;
}
loadMessagesFromServerByGroupId(mGroupId);
}
@Override
public boolean isLoading() {
return mLoading;
}
/**
* Create message with a specific file path, save it to the local database.
* Send the file to the server.
*/
@Override
public void sendFile(Uri fileUri) {
File file = getView().getFile(fileUri);
if (file == null || !file.exists()) {
getView().showToastMessage(R.string.error_file_not_found);
return;
}
String[] split = file.getAbsolutePath().split("/");
Message message = new Message(findUniqueRandomId(mRealmProvider, mUserIdPreference.get()),
mGroupId,
System.currentTimeMillis() / 1000,
null,
file.getPath(),
file.getPath(),
split[split.length - 1],
fileUri.toString(),
mUserIdPreference.get(),
mProfileNamePreference.get(),
mAvatarPreference.get(),
Message.Status.PENDING,
true,
mUserIdPreference.get(),
false);
storeMessageLocally(message)
.subscribe(new Subscriber<Object>() {
@Override
public void onCompleted() {
if (mSystemUtils.isNetworkUnavailable()) {
getView().showError(R.string.error_no_internet_connection);
message.status = Message.Status.FAILED;
message.isRead = true;
storeMessageLocally(message, message.id, true);
storeGroupLocally(mGroupId, message);
return;
}
checkForEmptyMessageAndHideIt();
message.createdAt *= 1000;
getView().addOneMessage(message);
// create RequestBody instance from file
RequestBody requestFile = RequestBody.create(getView().getMediaType(fileUri), file);
// MultipartBody.Part is used to send also the actual file name
MultipartBody.Part body = MultipartBody.Part.createFormData("file", file.getName(), requestFile);
// add another part within the multipart request
String groupIdString = String.valueOf(mGroupId);
RequestBody groupIdBody = RequestBody.create(okhttp3.MultipartBody.FORM, groupIdString);
Log.d("SENDING FILE", "Sending file with text message...");
RequestBody textBody = RequestBody.create(okhttp3.MultipartBody.FORM, message.message == null ? "" : message.message);
mChatService.sendFile(groupIdBody, body, textBody)
.compose(RxUtils.provideDefaultTransformer())
.flatMap(new ErrorHandlingResponseConverter<>())
.subscribe(new Subscriber<MessageResponse>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Log.e("sendFile", "onError", e);
@SuppressWarnings("UnnecessaryLocalVariable") Message newMessage = message;
newMessage.createdAt /= 1000;
newMessage.status = Message.Status.FAILED;
newMessage.isRead = true;
storeMessageLocally(newMessage, message.id, false);
storeGroupLocally(mGroupId, message);
if (!isAttached()) {
return;
}
if (mSystemUtils.isNetworkUnavailable()) {
getView().showError(R.string.error_no_internet_connection);
return;
}
getView().showError(R.string.error_something_went_wrong);
}
@Override
public void onNext(MessageResponse baseResponse) {
deleteLocalMessage(message);
baseResponse.response.message.status = Message.Status.DELIVERED;
baseResponse.response.message.isRead = true;
storeMessageLocally(baseResponse.response.message, message.id, false);
storeGroupLocally(mGroupId, baseResponse.response.message);
}
});
}
@Override
public void onError(Throwable e) {
Log.e("sendFile", "onError", e);
}
@Override
public void onNext(Object o) {
}
});
}
/**
* Download file and notify the activity.
*/
@Override
public void downloadFile(Message message, String filePath, int itemPosition) {
if (message.fileDownloaded) {
getView().stopItemProgress(itemPosition);
getView().fileDownloadedSuccessfully(filePath, getFileTypeFromFilePath(filePath));
return;
}
downloadFileFromUrl(message.filePath, filePath, itemPosition)
.compose(RxUtils.provideDefaultTransformer())
.subscribe(new Subscriber<String>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Log.e("ChatPresenter", "downloadFile", e);
if (!isAttached()) {
return;
}
getView().stopItemProgress(itemPosition);
if (mSystemUtils.isNetworkUnavailable()) {
getView().showError(R.string.error_no_internet_connection);
return;
}
getView().showError(R.string.error_something_went_wrong);
}
@Override
public void onNext(String fileType) {
if (!isAttached()) {
return;
}
getView().stopItemProgress(itemPosition);
getView().fileDownloadedSuccessfully(filePath, fileType);
message.fileDownloaded = true;
message.createdAt /= 1000;
storeMessageLocally(message).subscribe();
}
});
}
@Override
public void resendMessage(Message message) {
if (mSystemUtils.isNetworkUnavailable()) {
getView().enableMessageRetry(message.id);
getView().showError(R.string.error_no_internet_connection);
return;
}
if (TextUtils.isEmpty(message.message)) {
Uri fileUri = Uri.parse(message.uri);
File file = getView().getFile(fileUri);
if (file == null || !file.exists()) {
getView().showError(R.string.error_file_does_not_exist_anymore);
return;
}
// create RequestBody instance from file
RequestBody requestFile = RequestBody.create(getView().getMediaType(fileUri), file);
// MultipartBody.Part is used to send also the actual file name
MultipartBody.Part body = MultipartBody.Part.createFormData("file", file.getName(), requestFile);
// add another part within the multipart request
String groupIdString = String.valueOf(mGroupId);
RequestBody groupIdBody = RequestBody.create(okhttp3.MultipartBody.FORM, groupIdString);
RequestBody textBody = RequestBody.create(MultipartBody.FORM, message.message);
mChatService.sendFile(groupIdBody, body, textBody)
.compose(RxUtils.provideDefaultTransformer())
.flatMap(new ErrorHandlingResponseConverter<>())
.subscribe(new Subscriber<MessageResponse>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Log.e("resendMessage", "onError", e);
if (!isAttached()) {
return;
}
getView().enableMessageRetry(message.id);
if (mSystemUtils.isNetworkUnavailable()) {
getView().showError(R.string.error_no_internet_connection);
return;
}
getView().showError(R.string.error_retry_failed);
}
@Override
public void onNext(MessageResponse baseResponse) {
deleteLocalMessage(message);
baseResponse.response.message.status = Message.Status.DELIVERED;
baseResponse.response.message.isRead = true;
storeMessageLocally(baseResponse.response.message, message.id, true);
storeGroupLocally(mGroupId, baseResponse.response.message);
}
});
return;
}
mChatService.sendMessage(message.groupId, message.message)
.compose(RxUtils.provideDefaultTransformer())
.flatMap(new ErrorHandlingResponseConverter<>())
.subscribe(new Subscriber<MessageResponse>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Log.e("resendMessage", "onError", e);
if (!isAttached()) {
return;
}
getView().enableMessageRetry(message.id);
if (mSystemUtils.isNetworkUnavailable()) {
getView().showError(R.string.error_no_internet_connection);
return;
}
getView().showError(R.string.error_retry_failed);
}
@Override
public void onNext(MessageResponse baseResponse) {
deleteLocalMessage(message);
baseResponse.response.message.status = Message.Status.DELIVERED;
storeMessageLocally(baseResponse.response.message, message.id, true);
storeGroupLocally(mGroupId, baseResponse.response.message);
}
});
}
@Override
public void sendMessage(String text) {
Message message = new Message(findUniqueRandomId(mRealmProvider, mUserIdPreference.get()),
mGroupId,
System.currentTimeMillis() / 1000,
text,
null,
null,
null,
null,
mUserIdPreference.get(),
mProfileNamePreference.get(),
mAvatarPreference.get(),
Message.Status.PENDING,
true,
mUserIdPreference.get(),
false);
storeMessageLocally(message)
.subscribe(new Subscriber<Object>() {
@Override
public void onCompleted() {
if (mSystemUtils.isNetworkUnavailable()) {
getView().showError(R.string.error_no_internet_connection);
message.status = Message.Status.FAILED;
message.isRead = true;
storeMessageLocally(message, message.id, true);
storeGroupLocally(mGroupId, message);
return;
}
checkForEmptyMessageAndHideIt();
message.createdAt *= 1000;
getView().addOneMessage(message);
mChatService.sendMessage(mGroupId, text)
.compose(RxUtils.provideDefaultTransformer())
.flatMap(new ErrorHandlingResponseConverter<>())
.subscribe(new Subscriber<MessageResponse>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Log.e("sendMessage", "onError", e);
@SuppressWarnings("UnnecessaryLocalVariable") Message newMessage = message;
newMessage.status = Message.Status.FAILED;
newMessage.createdAt /= 1000;
newMessage.isRead = true;
storeMessageLocally(newMessage, message.id, false);
storeGroupLocally(mGroupId, message);
if (!isAttached()) {
return;
}
if (mSystemUtils.isNetworkUnavailable()) {
getView().showError(R.string.error_no_internet_connection);
return;
}
getView().showError(R.string.error_something_went_wrong);
}
@Override
public void onNext(MessageResponse baseResponse) {
deleteLocalMessage(message);
baseResponse.response.message.status = Message.Status.DELIVERED;
baseResponse.response.message.isRead = true;
storeMessageLocally(baseResponse.response.message, message.id, false);
storeGroupLocally(mGroupId, baseResponse.response.message);
}
});
}
@Override
public void onError(Throwable e) {
Log.e("sendMessage", "onError", e);
}
@Override
public void onNext(Object object) {
}
});
}
private void storeMessageLocally(Message newMessage, String oldMessageId, boolean queryLocalDBWhenFinished) {
newMessage.userId = mUserIdPreference.get();
MessageByIdSpecification messageByIdSpecification = new MessageByIdSpecification(mUserIdPreference.get());
messageByIdSpecification.setMessageId(newMessage.id);
mMessageRealmRepository.update(Collections.singletonList(newMessage), messageByIdSpecification)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Object>() {
@Override
public void onCompleted() {
newMessage.createdAt *= 1000;
if (!queryLocalDBWhenFinished) {
Message messageToDelete = new Message();
messageToDelete.id = oldMessageId;
messageToDelete.removeFromList(mMessages);
mMessages.add(newMessage);
if (!isAttached()) {
mMessageToUpdate.add(new Pair<>(newMessage, oldMessageId));
return;
}
getView().lastMessageUpdated(newMessage, oldMessageId);
return;
}
mPage = FIRST_PAGE;
mOffset = 0;
loadMessagesFromDBByGroupId();
}
@Override
public void onError(Throwable e) {
Log.e("addMessage", "onError", e);
}
@Override
public void onNext(Object o) {
}
});
}
private void deleteLocalMessage(Message message) {
MessageByIdSpecification messageByIdSpecification = new MessageByIdSpecification(mUserIdPreference.get());
messageByIdSpecification.setMessageId(message.id);
mMessageRealmRepository.remove(messageByIdSpecification)
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<Object>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Log.e("deleteLocalMessage", "onError", e);
}
@Override
public void onNext(Object o) {
}
});
}
@Override
public void saveMessage(Message message) {
storeMessageLocally(message)
.subscribe(() -> {
if (!isAttached()) {
return;
}
checkForEmptyMessageAndHideIt();
message.createdAt *= 1000;
mMessages.add(message);
getView().addOneMessage(message);
});
storeGroupLocally(mGroupId, message);
}
private void checkForEmptyMessageAndHideIt() {
if (mMessages.isEmpty()) {
getView().hideLoading();
}
}
private Completable storeMessageLocally(Message message) {
MessageByIdSpecification messageByIdSpecification = new MessageByIdSpecification(mUserIdPreference.get());
messageByIdSpecification.setMessageId(message.id);
return mMessageRealmRepository.update(message, messageByIdSpecification)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
/**
* Save last message to local database.
*/
private void storeGroupLocally(long groupId, Message message) {
Group group = new Group();
group.id = groupId;
group.lastMessage = message;
mGroupRealmRepository.updateGroupMessage(group, new GroupByIdSpecification(mUserIdPreference.get()))
.subscribe(() -> {
if (!isAttached()) {
EventBus.getDefault().post(new GroupUpdatedEvent());
}
});
}
@Override
public void findGroupById() {
Subscription subscription = mGroupRealmRepository.queryForOneById(String.valueOf(mGroupId), new GroupByIdSpecification(mUserIdPreference.get()))
.compose(RxUtils.provideDefaultTransformer())
.subscribe(new Subscriber<Group>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Group group) {
if (group != null) {
getView().setGroup(group);
return;
}
loadGroups();
}
});
addSubscription(subscription);
}
private void loadGroups() {
Subscription subscription = mChatService.getGroups()
.compose(RxUtils.provideDefaultTransformer())
.flatMap(new ErrorHandlingResponseConverter<>())
.flatMap(chatResponse -> getGroupById(chatResponse.response.groups))
.subscribe(this::groupsSuccess, this::groupsError);
addSubscription(subscription);
}
private Observable<Group> getGroupById(ArrayList<Group> groups) {
Group group = null;
for (Group group1 : groups) {
if (group1.id == mGroupId) {
group = group1;
break;
}
}
return Observable.just(group);
}
private void groupsError(Throwable throwable) {
Log.e(ChatPresenter.class.getSimpleName(), throwable.getMessage(), throwable);
if (mSystemUtils.isNetworkUnavailable()) {
getView().showError(R.string.error_no_internet_connection);
return;
}
getView().showError(R.string.error_something_went_wrong);
}
private void groupsSuccess(Group group) {
getView().setGroup(group);
mGroupRealmRepository.update(group, new GroupByIdSpecification(mUserIdPreference.get())).subscribe();
}
@Override
public long getGroupId() {
return mGroupId;
}
@Override
protected void afterAttachView(@NonNull ChatView view) {
super.afterAttachView(view);
view.setCallback(this);
mFetchMore = true;
}
@Override
protected void beforeDetachView(@NonNull ChatView view) {
view.setCallback(null);
super.beforeDetachView(view);
}
private Observable<String> downloadFileFromUrl(@NonNull String fileUrl, @NonNull String filePath, int itemPosition) {
return Observable.create(subscriber -> {
try {
//set the download URL, a url that points to a file on the internet
//this is the file to be downloaded
URL url = new URL(fileUrl);
//create the new connection
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
//set up some things on the connection
urlConnection.setRequestMethod("GET");
urlConnection.setDoOutput(true);
FileOutputStream fileOutput;
InputStream inputStream;
File file = new File(filePath);
if (file.length() > 0) {
urlConnection.setRequestProperty("Range", "bytes=" + file.length() + "-");
//this will be used to append the downloaded data into the file already existing
fileOutput = new FileOutputStream(file, true);
} else {
//this will be used to write the downloaded data into the file we created
fileOutput = new FileOutputStream(file);
}
//and connect!
urlConnection.connect();
//this is the total size of the file
int totalSize = urlConnection.getContentLength();
if (file.length() == totalSize || totalSize == -1) {
subscriber.onNext(getFileTypeFromFilePath(filePath));
subscriber.onCompleted();
return;
}
//this will be used in reading the data from the internet
inputStream = urlConnection.getInputStream();
//variable to store total downloaded bytes
long downloadedSize = file.length();
totalSize += downloadedSize;
//create a buffer...
byte[] buffer = new byte[1024];
int bufferLength = 0;
int lastPercentage = 0;
int percentage = 0;
//now, read through the input buffer and write the contents to the file
while ((bufferLength = inputStream.read(buffer, 0, 1024)) >= 0) {
//add the data in the buffer to the file in the file output stream (the file on the sd card
fileOutput.write(buffer, 0, bufferLength);
//add up the size so we know how much is downloaded
downloadedSize += bufferLength;
percentage = (int) (downloadedSize * 100 / totalSize);
if (percentage - lastPercentage > 1) {
lastPercentage = percentage;
if (isAttached()) {
getView().showItemProgress(itemPosition, percentage);
}
}
}
//close the output stream when done
fileOutput.close();
subscriber.onNext(getFileTypeFromFilePath(filePath));
subscriber.onCompleted();
} catch (IOException e) {
e.printStackTrace();
//noinspection ResultOfMethodCallIgnored
subscriber.onError(e);
subscriber.onCompleted();
}
});
}
public static String findUniqueRandomId(RealmProvider realmProvider, long userId) {
String randomId = generateRandomId();
MessageByIdSpecification messageByIdSpecification = new MessageByIdSpecification(userId);
messageByIdSpecification.setMessageId(randomId);
Realm realm = realmProvider.provide();
realm.beginTransaction();
boolean goOn = true;
while (goOn) {
MessageRealm messageRealm = messageByIdSpecification.toRealmQuery(realm).findFirst();
if (messageRealm == null) {
goOn = false;
} else {
randomId = generateRandomId();
messageByIdSpecification.setMessageId(randomId);
}
}
realm.commitTransaction();
realm.close();
return randomId;
}
private static String generateRandomId() {
return "_" + new Random().nextInt();
}
}

View File

@@ -0,0 +1,85 @@
package com.p3.bewoplaner.page.chat;
import com.p3.bewoplaner.model.Group;
import com.p3.bewoplaner.model.Message;
import com.p3.bewoplaner.page.mvp.MVPView;
import android.net.Uri;
import android.support.annotation.StringRes;
import java.io.File;
import java.util.ArrayList;
import okhttp3.MediaType;
/**
* Created by Rares Teodorescu on 21/02/2017.
*/
interface ChatView extends MVPView {
void setCallback(Callback callback);
void messagesLoaded(ArrayList<Message> messages, boolean isFirstPage);
void showLoading();
void hideLoading();
void showMessagesLoading();
void hideMessagesLoading();
void showEmptyMessage();
void fileDownloadedSuccessfully(String filePath, String fileType);
void lastMessageUpdated(Message newMessage, String oldMessageId);
void showError(@StringRes int errorRes);
void setGroup(Group group);
void addOneMessage(Message message);
MediaType getMediaType(Uri fileUri);
File getFile(Uri fileUri);
void enableMessageRetry(String messageId);
void showItemProgress(int itemPosition, int progress);
void stopItemProgress(int itemPosition);
void showToastMessage(@StringRes int message);
interface Callback {
void loadMessagesFromServerByGroupId(long groupId);
void loadMessagesFromDBByGroupId(long groupId, boolean fetchWhenFinished);
void fetchMoreMessagesByGroupId();
boolean isLoading();
void saveMessage(Message message);
void resetMessages();
void sendFile(Uri fileUri);
void sendMessage(String message);
void resendMessage(Message message);
void downloadFile(Message message, String filePath, int itemPosition);
void findGroupById();
long getGroupId();
void fetchNewMessages(long groupId);
}
}

View File

@@ -0,0 +1,159 @@
package com.p3.bewoplaner.page.chat;
import com.p3.bewoplaner.R;
import com.p3.bewoplaner.model.Group;
import com.p3.bewoplaner.model.Message;
import com.p3.bewoplaner.page.chatmembers.ChatMembersActivity;
import com.p3.bewoplaner.utils.Constants;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.view.View;
/**
* Created by Rares Teodorescu on 21/02/2017.
*/
public class GroupChatActivity extends BaseChatActivity {
public static Intent createIntent(Context context, Group group) {
Intent intent = new Intent(context, GroupChatActivity.class);
intent.putExtra(GROUP_ARG, group);
return intent;
}
public static Intent createIntent(Context context, Bundle groupId) {
Intent intent = new Intent(context, GroupChatActivity.class);
intent.putExtra(MESSAGE_ARG, groupId);
return intent;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mChatPresenter.attachView(this);
mCallback.resetMessages();
if (getIntent().hasExtra(MESSAGE_ARG)) {
Bundle bundle = getIntent().getBundleExtra(MESSAGE_ARG);
long groupId = bundle.getLong(Constants.GROUP_ID);
if (mSystemUtils.isNetworkUnavailable()) {
mCallback.loadMessagesFromDBByGroupId(groupId, true);
showError(R.string.error_no_internet_connection);
} else {
mCallback.loadMessagesFromServerByGroupId(groupId);
}
mCallback.findGroupById();
} else {
setGroupFromArguments();
}
}
@Override
protected void onResume() {
if (!mChatPresenter.isAttached()) {
mChatPresenter.attachView(this);
mBaseChatAdapter.resetItemsProgress();
if (mSystemUtils.isNetworkUnavailable()) {
mCallback.loadMessagesFromDBByGroupId(mGroup.id, true);
showError(R.string.error_no_internet_connection);
} else {
if (mGroup == null) {
if (getIntent().hasExtra(MESSAGE_ARG)) {
long groupId = getIntent().getBundleExtra(MESSAGE_ARG).getLong(Constants.GROUP_ID);
mCallback.fetchNewMessages(groupId);
return;
}
setGroupFromArguments();
mCallback.fetchNewMessages(mGroup.id);
} else {
mCallback.fetchNewMessages(mGroup.id);
}
}
}
cancelNotification();
IntentFilter filter = new IntentFilter(Constants.NOTIFICATION_BROADCAST);
registerReceiver(mReceiver, filter);
super.onResume();
}
@Override
protected void loadMessages() {
if (mSystemUtils.isNetworkUnavailable()) {
mCallback.loadMessagesFromDBByGroupId(mGroup.id, true);
showError(R.string.error_no_internet_connection);
} else {
mCallback.loadMessagesFromServerByGroupId(mGroup.id);
}
}
@Override
protected void setToolbar() {
mSingleChatTitleTextView.setVisibility(View.GONE);
mGroupTitleTextView.setVisibility(View.VISIBLE);
mGroupMembersTextView.setVisibility(View.VISIBLE);
mExtraImageView.setVisibility(View.VISIBLE);
mGroupMembersTextView.setText(mGroup.getContactsNames());
mExtraImageView.setImageResource(R.drawable.vertical_dots);
mChatImageView.setImageResource(mGroup.isHelpGroup() ? R.drawable.avatar_help_group : R.drawable.avatar_team_group);
mGroupTitleTextView.setText(mGroup.name);
mGroupTitleTextView.setTextColor(ContextCompat.getColor(this,
mGroup.isHelpGroup() ? R.color.colorHelpGroup : R.color.colorTeamGroup));
}
@Override
protected void onPause() {
mChatPresenter.detachView();
unregisterReceiver(mReceiver);
super.onPause();
}
@Override
protected void extraImageClicked() {
startActivity(ChatMembersActivity.createIntent(this, mGroup));
}
@Override
protected BaseChatAdapter getAdapter() {
return new GroupChatAdapter(mUserIdPreference.get());
}
@Override
public void setGroup(Group group) {
mGroup = group;
setToolbar();
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getBundleExtra(Constants.MESSAGE);
if (mCallback == null) {
mChatPresenter.attachView(GroupChatActivity.this);
}
if (mCallback.getGroupId() == bundle.getLong(Constants.GROUP_ID)) {
Message message = new Message(bundle.getString(Constants.MESSAGE_ID),
bundle.getLong(Constants.GROUP_ID),
bundle.getLong(Constants.CREATED_AT),
bundle.getString(Constants.MESSAGE_TEXT),
bundle.getString(Constants.FILE),
bundle.getString(Constants.SMALLER_IMAGE),
bundle.getString(Constants.FILENAME),
null,
bundle.getLong(Constants.USER_ID),
bundle.getString(Constants.USERNAME),
bundle.getString(Constants.USER_AVATAR),
Message.Status.DELIVERED,
true,
mUserIdPreference.get(),
false);
mCallback.saveMessage(message);
Bundle results = getResultExtras(true);
results.putBoolean(Constants.CHAT_IS_ALREADY_OPEN, true);
}
}
};
}

View File

@@ -0,0 +1,85 @@
package com.p3.bewoplaner.page.chat;
import com.p3.bewoplaner.R;
import com.p3.bewoplaner.model.Message;
import com.p3.bewoplaner.utils.DateUtils;
import com.p3.bewoplaner.utils.ui.ViewUtils;
import android.support.v7.widget.RecyclerView;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
/**
* Created by Rares Teodorescu on 21/02/2017.
*/
class GroupChatAdapter extends BaseChatAdapter {
GroupChatAdapter(long userId) {
super(userId);
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == LEFT_CHAT_TYPE) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_group_chat_left, parent, false);
return new MessageViewHolder(view);
} else if (viewType == RIGHT_CHAT_TYPE) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_group_chat_right, parent, false);
return new MessageViewHolder(view);
} else if (viewType == DATE_TYPE) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_chat_date, parent, false);
return new DateViewHolder(view);
}
ProgressBar progressBar = new ProgressBar(parent.getContext());
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.CENTER;
params.setMargins(0, (int) parent.getContext().getResources().getDimension(R.dimen.messages_progress_top_margin), 0, 0);
progressBar.setLayoutParams(params);
ViewUtils.setProgressBarColorToOrange(progressBar);
return new ProgressViewHolder(progressBar);
}
@SuppressWarnings("ConstantConditions")
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
int viewType = getItemViewType(position);
switch (viewType) {
case LOADING_TYPE:
break;
case DATE_TYPE:
((DateViewHolder) holder).dateTextView.setText(DateUtils.toDayOfYearString(mDateList.get(position)));
break;
case LEFT_CHAT_TYPE:
final Message leftMessage = mMessages.get(position - mPositionOffset.get(position));
MessageViewHolder leftMessageViewHolder = (MessageViewHolder) holder;
setGroupMessage(leftMessageViewHolder, leftMessage, position, true);
break;
case RIGHT_CHAT_TYPE:
final Message rightMessage = mMessages.get(position - mPositionOffset.get(position));
MessageViewHolder rightMessageViewHolder = (MessageViewHolder) holder;
setGroupMessage(rightMessageViewHolder, rightMessage, position, false);
if (rightMessage.status == Message.Status.DELIVERED) {
rightMessageViewHolder.statusProgressBar.setVisibility(View.GONE);
rightMessageViewHolder.statusImageView.setVisibility(View.VISIBLE);
rightMessageViewHolder.statusImageView.setImageResource(R.drawable.delivered);
rightMessageViewHolder.sendingFailedTextView.setVisibility(View.GONE);
} else if (rightMessage.status == Message.Status.FAILED) {
rightMessageViewHolder.statusProgressBar.setVisibility(View.GONE);
rightMessageViewHolder.statusImageView.setVisibility(View.VISIBLE);
rightMessageViewHolder.statusImageView.setImageResource(R.drawable.failed);
rightMessageViewHolder.sendingFailedTextView.setVisibility(View.VISIBLE);
} else {
ViewUtils.setProgressBarColorToOrange(rightMessageViewHolder.statusProgressBar);
rightMessageViewHolder.statusProgressBar.setVisibility(View.VISIBLE);
rightMessageViewHolder.sendingFailedTextView.setVisibility(View.GONE);
rightMessageViewHolder.statusImageView.setVisibility(View.GONE);
}
break;
}
}
}

View File

@@ -0,0 +1,171 @@
package com.p3.bewoplaner.page.chat;
import com.bumptech.glide.Glide;
import com.p3.bewoplaner.R;
import com.p3.bewoplaner.model.Group;
import com.p3.bewoplaner.model.Message;
import com.p3.bewoplaner.utils.Constants;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.text.TextUtils;
import android.view.View;
/**
* Created by Rares Teodorescu on 21/02/2017.
*/
public class SingleChatActivity extends BaseChatActivity {
public static Intent createIntent(Context context, Group contact) {
Intent intent = new Intent(context, SingleChatActivity.class);
intent.putExtra(GROUP_ARG, contact);
return intent;
}
public static Intent createIntent(Context context, Bundle bundle) {
Intent intent = new Intent(context, SingleChatActivity.class);
intent.putExtra(MESSAGE_ARG, bundle);
return intent;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mChatPresenter.attachView(this);
mCallback.resetMessages();
if (getIntent().hasExtra(MESSAGE_ARG)) {
Bundle bundle = getIntent().getBundleExtra(MESSAGE_ARG);
mGroup = new Group();
mGroup.id = bundle.getLong(Constants.GROUP_ID);
mGroup.name = bundle.getString(Constants.USERNAME);
mGroup.avatar = bundle.getString(Constants.USER_AVATAR);
if (mSystemUtils.isNetworkUnavailable()) {
mCallback.loadMessagesFromDBByGroupId(mGroup.id, true);
showError(R.string.error_no_internet_connection);
} else {
mCallback.loadMessagesFromServerByGroupId(mGroup.id);
}
mCallback.findGroupById();
} else {
setGroupFromArguments();
}
}
@Override
protected void onResume() {
if (!mChatPresenter.isAttached()) {
mChatPresenter.attachView(this);
mBaseChatAdapter.resetItemsProgress();
if (mSystemUtils.isNetworkUnavailable()) {
mCallback.loadMessagesFromDBByGroupId(mGroup.id, true);
showError(R.string.error_no_internet_connection);
} else {
mCallback.fetchNewMessages(mGroup.id);
}
}
cancelNotification();
IntentFilter filter = new IntentFilter(Constants.NOTIFICATION_BROADCAST);
registerReceiver(mReceiver, filter);
super.onResume();
}
@Override
protected void onPause() {
unregisterReceiver(mReceiver);
mChatPresenter.detachView();
super.onPause();
}
@Override
protected void loadMessages() {
if (!TextUtils.isEmpty(mGroup.mobile)) {
mExtraImageView.setVisibility(View.VISIBLE);
mExtraImageView.setImageResource(R.drawable.call);
}
if (mSystemUtils.isNetworkUnavailable()) {
mCallback.loadMessagesFromDBByGroupId(mGroup.id, true);
showError(R.string.error_no_internet_connection);
} else {
mCallback.loadMessagesFromServerByGroupId(mGroup.id);
}
}
@Override
protected void setToolbar() {
mSingleChatTitleTextView.setVisibility(View.VISIBLE);
mSingleChatTitleTextView.setText(mGroup.name);
mSingleChatTitleTextView.setTextColor(ContextCompat.getColor(this, mGroup.isEmployee(mGroup.users) ? R.color.colorHelpGroup : R.color.colorBlack));
if (TextUtils.isEmpty(mGroup.avatar)) {
mChatImageView.setImageResource(R.drawable.avatar);
} else {
Glide.with(this).load(mGroup.avatar)
.placeholder(R.drawable.avatar)
.dontAnimate()
.into(mChatImageView);
}
}
@Override
protected void extraImageClicked() {
Intent intent = new Intent(Intent.ACTION_DIAL, Uri.fromParts("tel",
mGroup.mobile,
null));
startActivity(intent);
}
@Override
protected BaseChatAdapter getAdapter() {
return new SingleChatAdapter(mUserIdPreference.get());
}
@Override
public void setGroup(Group groups) {
mGroup = groups;
loadMessages();
setToolbar();
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getBundleExtra(Constants.MESSAGE);
if (mCallback == null) {
mChatPresenter.attachView(SingleChatActivity.this);
}
if (mCallback.getGroupId() == bundle.getLong(Constants.GROUP_ID)) {
Message message = new Message(bundle.getString(Constants.MESSAGE_ID),
bundle.getLong(Constants.GROUP_ID),
bundle.getLong(Constants.CREATED_AT),
bundle.getString(Constants.MESSAGE_TEXT),
bundle.getString(Constants.FILE),
bundle.getString(Constants.SMALLER_IMAGE),
bundle.getString(Constants.FILENAME),
null,
bundle.getLong(Constants.USER_ID),
bundle.getString(Constants.USERNAME),
bundle.getString(Constants.USER_AVATAR),
Message.Status.DELIVERED,
true,
mUserIdPreference.get(),
false);
mCallback.saveMessage(message);
Bundle results = getResultExtras(true);
results.putBoolean(Constants.CHAT_IS_ALREADY_OPEN, true);
}
}
};
}

View File

@@ -0,0 +1,86 @@
package com.p3.bewoplaner.page.chat;
import com.p3.bewoplaner.R;
import com.p3.bewoplaner.model.Message;
import com.p3.bewoplaner.utils.DateUtils;
import com.p3.bewoplaner.utils.ui.ViewUtils;
import android.support.v7.widget.RecyclerView;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
/**
* Created by Rares Teodorescu on 21/02/2017.
*/
class SingleChatAdapter extends BaseChatAdapter {
SingleChatAdapter(long userId) {
super(userId);
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == LEFT_CHAT_TYPE) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_single_chat_left, parent, false);
return new MessageViewHolder(view);
} else if (viewType == RIGHT_CHAT_TYPE) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_single_chat_right, parent, false);
return new MessageViewHolder(view);
} else if (viewType == DATE_TYPE) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_chat_date, parent, false);
return new DateViewHolder(view);
}
ProgressBar progressBar = new ProgressBar(parent.getContext());
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.CENTER;
params.setMargins(0, (int) parent.getContext().getResources().getDimension(R.dimen.messages_progress_top_margin), 0, 0);
progressBar.setLayoutParams(params);
ViewUtils.setProgressBarColorToOrange(progressBar);
return new ProgressViewHolder(progressBar);
}
@SuppressWarnings("ConstantConditions")
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
int viewType = getItemViewType(position);
switch (viewType) {
case LOADING_TYPE:
break;
case DATE_TYPE:
((DateViewHolder) holder).dateTextView.setText(DateUtils.toDayOfYearString(mDateList.get(position)));
break;
case LEFT_CHAT_TYPE:
MessageViewHolder leftChatHolder = (MessageViewHolder) holder;
final Message leftMessage = mMessages.get(position - mPositionOffset.get(position));
setSingleMessage(leftChatHolder, leftMessage, position, true);
break;
case RIGHT_CHAT_TYPE:
MessageViewHolder rightChatHolder = (MessageViewHolder) holder;
final Message rightMessage = mMessages.get(position - mPositionOffset.get(position));
setSingleMessage(rightChatHolder, rightMessage, position, false);
if (rightMessage.status == Message.Status.DELIVERED) {
rightChatHolder.statusProgressBar.setVisibility(View.GONE);
rightChatHolder.statusImageView.setVisibility(View.VISIBLE);
rightChatHolder.statusImageView.setImageResource(R.drawable.delivered);
rightChatHolder.sendingFailedTextView.setVisibility(View.GONE);
} else if (rightMessage.status == Message.Status.FAILED) {
rightChatHolder.statusProgressBar.setVisibility(View.GONE);
rightChatHolder.statusImageView.setVisibility(View.VISIBLE);
rightChatHolder.statusImageView.setImageResource(R.drawable.failed);
rightChatHolder.sendingFailedTextView.setVisibility(View.VISIBLE);
} else {
ViewUtils.setProgressBarColorToOrange(rightChatHolder.statusProgressBar);
rightChatHolder.statusProgressBar.setVisibility(View.VISIBLE);
rightChatHolder.sendingFailedTextView.setVisibility(View.GONE);
rightChatHolder.statusImageView.setVisibility(View.GONE);
}
break;
}
}
}

View File

@@ -0,0 +1,91 @@
package com.p3.bewoplaner.page.chatmembers;
import com.p3.bewoplaner.R;
import com.p3.bewoplaner.model.Group;
import com.p3.bewoplaner.utils.ui.HorizontalDividerItemDecoration;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
/**
* Created by imarneanu on 2/24/17.
*/
public class ChatMembersActivity extends AppCompatActivity {
protected final static String GROUP_ARG = "group_arg";
@BindView(R.id.chat_toolbar_image_view)
ImageView mChatImageView;
@BindView(R.id.chat_toolbar_group_title)
TextView mGroupTitleTextView;
@BindView(R.id.chat_toolbar_group_members)
TextView mGroupMembersTextView;
@BindView(R.id.chat_toolbar_extra_image)
ImageView mExtraImageView;
@BindView(R.id.chat_members_recycler_view)
RecyclerView mChatMembersRecyclerView;
private RecyclerView.ItemDecoration mHorizontalDividerItemDecoration;
public static Intent createIntent(Context context, Group group) {
Intent intent = new Intent(context, ChatMembersActivity.class);
intent.putExtra(GROUP_ARG, group);
return intent;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat_members);
ButterKnife.bind(this);
Group group = getIntent().getParcelableExtra(GROUP_ARG);
setToolbar(group);
mHorizontalDividerItemDecoration = new HorizontalDividerItemDecoration.Builder(this)
.colorResId(R.color.colorFill).build();
mChatMembersRecyclerView.addItemDecoration(mHorizontalDividerItemDecoration);
mChatMembersRecyclerView.setLayoutManager(new LinearLayoutManager(this));
ChatMembersAdapter contactsAdapter = new ChatMembersAdapter(group.users);
mChatMembersRecyclerView.setAdapter(contactsAdapter);
}
private void setToolbar(Group group) {
mGroupTitleTextView.setVisibility(View.VISIBLE);
mGroupMembersTextView.setVisibility(View.GONE);
mExtraImageView.setVisibility(View.GONE);
mChatImageView.setImageResource(group.isHelpGroup() ? R.drawable.avatar_help_group
: R.drawable.avatar_team_group);
mGroupTitleTextView.setText(group.name);
mGroupTitleTextView.setTextColor(ContextCompat.getColor(this,
group.isHelpGroup() ? R.color.colorHelpGroup : R.color.colorTeamGroup));
}
@OnClick(R.id.chat_toolbar_back_arrow)
void backPressed() {
onBackPressed();
}
@Override
public void onDestroy() {
mChatMembersRecyclerView.setAdapter(null);
mChatMembersRecyclerView.removeItemDecoration(mHorizontalDividerItemDecoration);
super.onDestroy();
}
}

View File

@@ -0,0 +1,91 @@
package com.p3.bewoplaner.page.chatmembers;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.p3.bewoplaner.R;
import com.p3.bewoplaner.model.User;
import android.content.Intent;
import android.net.Uri;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Locale;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import de.hdodenhof.circleimageview.CircleImageView;
/**
* Created by imarneanu on 2/24/17.
*/
class ChatMembersAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private ArrayList<User> mUsers;
ChatMembersAdapter(ArrayList<User> users) {
mUsers = users;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_chat_member, parent,
false);
return new ContactViewHolder(view);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
ContactViewHolder viewHolder = (ContactViewHolder) holder;
User user = mUsers.get(position);
viewHolder.contactTextView.setText(String.format(Locale.getDefault(), "%s %s", user.firstName, user.lastName));
viewHolder.contactTextView.setTextColor(ContextCompat.getColor(viewHolder.contactTextView.getContext(),
user.isEmployee ? R.color.colorBlack : R.color.colorHelpGroup));
viewHolder.phoneImageView.setVisibility(TextUtils.isEmpty(user.mobile) ? View.GONE : View.VISIBLE);
if (TextUtils.isEmpty(user.avatar)) {
viewHolder.avatarCircleImageView.setImageResource(R.drawable.avatar_list);
return;
}
Glide.with(viewHolder.avatarCircleImageView.getContext())
.load(user.avatar)
.placeholder(R.drawable.avatar)
.dontAnimate()
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(viewHolder.avatarCircleImageView);
}
@Override
public int getItemCount() {
return mUsers.size();
}
class ContactViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.chat_member_avatar)
CircleImageView avatarCircleImageView;
@BindView(R.id.chat_member_name)
TextView contactTextView;
@BindView(R.id.chat_member_phone)
ImageView phoneImageView;
ContactViewHolder(View view) {
super(view);
ButterKnife.bind(this, view);
}
@OnClick(R.id.chat_member_phone)
void onPhoneClicked() {
Intent intent = new Intent(Intent.ACTION_DIAL, Uri.fromParts("tel",
mUsers.get(getAdapterPosition()).mobile,
null));
phoneImageView.getContext().startActivity(intent);
}
}
}

View File

@@ -0,0 +1,22 @@
package com.p3.bewoplaner.page.common;
import android.support.v4.app.Fragment;
import butterknife.Unbinder;
/**
* Created by imarneanu on 2/22/17.
*/
public class BaseFragment extends Fragment {
protected Unbinder mUnbinder;
@Override
public void onDestroyView() {
super.onDestroyView();
if (mUnbinder == null) {
return;
}
mUnbinder.unbind();
}
}

View File

@@ -0,0 +1,17 @@
package com.p3.bewoplaner.page.common;
/**
* Callback for requesting permission.
* <p>
* Created by Rares on 29/08/16.
*/
public interface PermissionGrantedListener {
/**
* Method called when permission is granted.
*
* @param requestCode For given permission
*/
void onPermissionsGranted(int requestCode);
}

View File

@@ -0,0 +1,109 @@
package com.p3.bewoplaner.page.common;
import com.p3.bewoplaner.R;
import com.p3.bewoplaner.utils.DialogHandler;
import android.app.Dialog;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.SparseIntArray;
/**
* This class handles runtime permissions from activities.
* <p>
* Created by Rares on 29/08/16.
*/
public abstract class RuntimePermissionActivity extends AppCompatActivity {
private SparseIntArray mErrorString;
private PermissionGrantedListener mPermissionGrantedListener;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mErrorString = new SparseIntArray();
}
/**
* @param requestedPermissions the permissions that we need to grant
* @param stringId the id of the message that we show while requesting
* @param requestCode the request code for this permission request
* @param permissionGrantedListener the listener which it's method will be called when
* permission is granted
*/
public void requestAppPermissions(final String[] requestedPermissions,
final int stringId,
final int requestCode,
@NonNull PermissionGrantedListener permissionGrantedListener) {
mPermissionGrantedListener = permissionGrantedListener;
mErrorString.put(requestCode, stringId);
int permissionCheck = PackageManager.PERMISSION_GRANTED;
boolean shouldShowRequestPermissionRationale = false;
for (String permission : requestedPermissions) {
permissionCheck = permissionCheck + ContextCompat.checkSelfPermission(this, permission);
shouldShowRequestPermissionRationale = shouldShowRequestPermissionRationale || ActivityCompat.shouldShowRequestPermissionRationale(this, permission);
}
if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
if (shouldShowRequestPermissionRationale) {
Dialog dialog = DialogHandler.createNoTitleDialog(this,
stringId,
true,
R.string.permission_grant,
(dialogInterface, i) -> requestPermissionCorrectly(requestedPermissions, requestCode));
dialog.show();
} else {
requestPermissionCorrectly(requestedPermissions, requestCode);
}
} else {
mPermissionGrantedListener.onPermissionsGranted(requestCode);
}
}
protected void requestPermissionCorrectly(String[] requestedPermissions, int requestCode) {
ActivityCompat.requestPermissions(this, requestedPermissions, requestCode);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (mErrorString.size() == 0 || mErrorString.get(requestCode) == 0) {
return;
}
int permissionCheck = PackageManager.PERMISSION_GRANTED;
for (int permission : grantResults) {
permissionCheck = permissionCheck + permission;
}
if ((grantResults.length > 0) && permissionCheck == PackageManager.PERMISSION_GRANTED) {
mPermissionGrantedListener.onPermissionsGranted(requestCode);
} else {
Dialog dialog = DialogHandler.createNoTitleDialog(this,
mErrorString.get(requestCode),
true,
R.string.permission_enable,
(dialogInterface, i) -> {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setData(Uri.parse("package:" + getPackageName()));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
startActivity(intent);
});
dialog.show();
}
}
}

View File

@@ -0,0 +1,174 @@
package com.p3.bewoplaner.page.contacts;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.p3.bewoplaner.R;
import com.p3.bewoplaner.model.Group;
import com.p3.bewoplaner.utils.DateUtils;
import com.p3.bewoplaner.utils.ui.ViewUtils;
import android.graphics.Typeface;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.util.ArrayList;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import de.hdodenhof.circleimageview.CircleImageView;
import hani.momanii.supernova_emoji_library.Helper.EmojiconTextView;
/**
* Created by imarneanu on 2/21/17.
*/
class ContactsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int GROUP_TYPE = 0;
private static final int CONTACT_TYPE = 1;
private ArrayList<Group> mGroups;
private OnOpenClickListener mItemClickListener;
private long mUserId;
interface OnOpenClickListener {
void onOpenGroupMessage(Group group);
void onOpenPrivateMessage(Group contact);
}
ContactsAdapter(OnOpenClickListener openClickListener, long userId) {
mItemClickListener = openClickListener;
mGroups = new ArrayList<>();
mUserId = userId;
}
void setGroups(ArrayList<Group> groups) {
mGroups.clear();
if (groups != null) {
mGroups.addAll(groups);
}
notifyDataSetChanged();
}
@Override
public final int getItemViewType(int position) {
if (mGroups.get(position).users.size() > 2) {
return GROUP_TYPE;
}
return CONTACT_TYPE;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_contact, parent,
false);
return new ContactViewHolder(view);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
Group group = mGroups.get(position);
ContactViewHolder viewHolder = (ContactViewHolder) holder;
setLastMessage(group, viewHolder);
int type = getItemViewType(position);
if (type == GROUP_TYPE) {
viewHolder.name.setText(group.name);
viewHolder.name.setTextColor(ContextCompat.getColor(viewHolder.name.getContext(),
group.isHelpGroup() ? R.color.colorHelpGroup : R.color.colorTeamGroup));
viewHolder.avatar.setImageResource(group.isHelpGroup() ?
R.drawable.avatar_help_group : R.drawable.avatar_team_group);
return;
}
if(type == CONTACT_TYPE) {
viewHolder.name.setTextColor(ContextCompat.getColor(viewHolder.name.getContext(),
group.isEmployee(mGroups.get(position).users) ? R.color.colorBlack : R.color.colorHelpGroup));
}
viewHolder.name.setText(group.name);
Glide.with(viewHolder.avatar.getContext()).load(group.avatar)
.placeholder(R.drawable.avatar_list)
.dontAnimate()
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(viewHolder.avatar);
}
private void setLastMessage(Group group, ContactViewHolder viewHolder) {
if (group.lastMessage == null) {
viewHolder.lastMessage.setText(null);
viewHolder.messageTimestamp.setText(null);
viewHolder.messageStatus.setImageResource(0);
return;
}
boolean isMessageSentByUser = group.lastMessage.senderId == mUserId;
if (!group.isRead() && !isMessageSentByUser) {
viewHolder.lastMessage.setTypeface(null, Typeface.BOLD);
viewHolder.lastMessage.setTextColor(ContextCompat.getColor(viewHolder.lastMessage.getContext(), R.color.colorDefaultBright));
} else {
viewHolder.lastMessage.setTypeface(null);
viewHolder.lastMessage.setTextColor(ContextCompat.getColor(viewHolder.lastMessage.getContext(), R.color.colorSubtitle));
}
viewHolder.lastMessage.setText(TextUtils.isEmpty(group.lastMessage.message) ? group.lastMessage.originalFilename : group.lastMessage.message);
viewHolder.messageTimestamp.setText(DateUtils.toHourOrDateFormatHour(group.lastMessage.createdAt));
if (!isMessageSentByUser) {
viewHolder.messageStatus.setImageResource(0);
return;
}
int resourceId = group.lastMessage.getStatusResourceId();
if (resourceId != 0) {
viewHolder.messageStatusPending.setVisibility(resourceId == -1 ? View.VISIBLE : View.GONE);
if (resourceId != -1) {
viewHolder.messageStatus.setImageResource(group.lastMessage.getStatusResourceId());
} else {
ViewUtils.setProgressBarColor(viewHolder.messageStatusPending, R.color.colorHint);
}
}
}
@Override
public int getItemCount() {
return mGroups.size();
}
class ContactViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.contact_avatar)
CircleImageView avatar;
@BindView(R.id.contact_name)
TextView name;
@BindView(R.id.contact_message_last)
EmojiconTextView lastMessage;
@BindView(R.id.contact_message_timestamp)
TextView messageTimestamp;
@BindView(R.id.contact_message_status)
ImageView messageStatus;
@BindView(R.id.contact_message_pending)
ProgressBar messageStatusPending;
ContactViewHolder(View view) {
super(view);
ButterKnife.bind(this, view);
}
@OnClick(R.id.contact_container)
void onClickContact() {
int type = getItemViewType();
if (type == GROUP_TYPE) {
mItemClickListener.onOpenGroupMessage(mGroups.get(getAdapterPosition()));
return;
}
mItemClickListener.onOpenPrivateMessage(mGroups.get(getAdapterPosition()));
}
}
}

View File

@@ -0,0 +1,17 @@
package com.p3.bewoplaner.page.contacts;
import com.p3.bewoplaner.di.ApplicationComponent;
import com.p3.bewoplaner.di.scopes.ActivityScope;
import com.p3.bewoplaner.page.chat.ChatModule;
import dagger.Component;
/**
* Created by imarneanu on 2/20/17.
*/
@ActivityScope
@Component(dependencies = ApplicationComponent.class, modules = ChatModule.class)
public interface ContactsComponent {
void inject(ContactsFragment contactsFragment);
}

View File

@@ -0,0 +1,257 @@
package com.p3.bewoplaner.page.contacts;
import com.p3.bewoplaner.R;
import com.p3.bewoplaner.data.AppPrefsConstants;
import com.p3.bewoplaner.data.events.GroupUpdatedEvent;
import com.p3.bewoplaner.data.prefs.LongPreference;
import com.p3.bewoplaner.di.InjectionHelper;
import com.p3.bewoplaner.model.Group;
import com.p3.bewoplaner.model.Message;
import com.p3.bewoplaner.page.chat.GroupChatActivity;
import com.p3.bewoplaner.page.chat.SingleChatActivity;
import com.p3.bewoplaner.page.common.BaseFragment;
import com.p3.bewoplaner.utils.Constants;
import com.p3.bewoplaner.utils.ui.EmptyLayout;
import com.p3.bewoplaner.utils.ui.HorizontalDividerItemDecoration;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar;
import android.support.v4.content.ContextCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import javax.inject.Inject;
import javax.inject.Named;
import butterknife.BindView;
import butterknife.ButterKnife;
/**
* Created by imarneanu on 2/13/17.
*/
public class ContactsFragment extends BaseFragment
implements ContactsView, ContactsAdapter.OnOpenClickListener, SearchView.OnQueryTextListener {
@BindView(R.id.contacts_recycler_view)
RecyclerView mContactsRecyclerView;
@BindView(R.id.contacts_empty_layout)
EmptyLayout mEmptyLayout;
@BindView(R.id.contacts_coordinator_layout)
CoordinatorLayout mCoordinatorLayout;
@BindView(R.id.contacts_swipe_refresh_layout)
SwipeRefreshLayout mSwipeRefreshLayout;
@Inject
ContactsPresenter mContactsPresenter;
@Inject
@Named(AppPrefsConstants.USER_ID)
LongPreference mUserIdPreference;
private Callback mCallback;
private ContactsAdapter mContactsAdapter;
private RecyclerView.ItemDecoration mHorizontalDividerItemDecoration;
private Snackbar mSnackbar;
private SearchView mSearchView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
InjectionHelper.getContactsComponent(context).inject(this);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_contacts, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mUnbinder = ButterKnife.bind(this, view);
getActivity().setTitle(R.string.title_contacts);
EventBus.getDefault().register(this);
mContactsPresenter.attachView(this);
IntentFilter filter = new IntentFilter(Constants.NOTIFICATION_BROADCAST);
getActivity().registerReceiver(mReceiver, filter);
mHorizontalDividerItemDecoration = new HorizontalDividerItemDecoration.Builder(getActivity())
.colorResId(R.color.colorFill).build();
mContactsRecyclerView.addItemDecoration(mHorizontalDividerItemDecoration);
mContactsRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
mContactsAdapter = new ContactsAdapter(this, mUserIdPreference.get());
mContactsRecyclerView.setAdapter(mContactsAdapter);
mSwipeRefreshLayout.setColorSchemeResources(R.color.colorDefault, R.color.colorDefaultBright);
mSwipeRefreshLayout.setOnRefreshListener(this::refresh);
}
private void refresh() {
mSwipeRefreshLayout.setRefreshing(false);
resetSearch();
mCallback.loadGroups();
}
@Override
public void onResume() {
super.onResume();
resetSearch();
mCallback.loadGroups();
mCallback.loadGroupsFromDB();
}
private void resetSearch() {
if (mSearchView != null && !mSearchView.isIconified()) {
mSearchView.setQuery(null, false);
mSearchView.setIconified(true);
}
}
@Override
public void showError(@StringRes int message) {
if (mSnackbar == null) {
mSnackbar = Snackbar.make(mCoordinatorLayout, message, Snackbar.LENGTH_LONG);
mSnackbar.setActionTextColor(ContextCompat.getColor(getActivity(), R.color.colorDefault));
}
mSnackbar.setText(message);
mSnackbar.show();
}
@Override
public void onDestroyView() {
EventBus.getDefault().unregister(this);
mContactsAdapter = null;
mContactsRecyclerView.setAdapter(null);
mContactsRecyclerView.removeItemDecoration(mHorizontalDividerItemDecoration);
mSwipeRefreshLayout.setOnRefreshListener(null);
super.onDestroyView();
getActivity().unregisterReceiver(mReceiver);
mContactsPresenter.detachView();
}
@Override
public void setCallback(Callback callback) {
mCallback = callback;
}
@Override
public void showProgress() {
mEmptyLayout.showLoading();
}
@Override
public void hideProgress() {
mEmptyLayout.setVisibility(View.GONE);
}
@Override
public void setGroups(ArrayList<Group> groups) {
mContactsAdapter.setGroups(groups);
}
@Override
public void onOpenGroupMessage(Group group) {
if (TextUtils.isEmpty(mSearchView.getQuery())) {
mSearchView.setIconified(true);
}
startActivity(GroupChatActivity.createIntent(getActivity(), group));
}
@Override
public void onOpenPrivateMessage(Group group) {
startActivity(SingleChatActivity.createIntent(getActivity(), group));
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.activity_main_menu, menu);
MenuItem menuItem = menu.findItem(R.id.action_search);
mSearchView = (SearchView) menuItem.getActionView();
mSearchView.setOnQueryTextListener(this);
}
@Override
public boolean onQueryTextSubmit(String query) {
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
if (TextUtils.isEmpty(newText)) {
mCallback.loadGroupsFromDB();
} else {
mCallback.loadGroupsContainingName(newText);
}
return false;
}
@SuppressWarnings("unused")
@Subscribe(threadMode = ThreadMode.MAIN)
public void onInvalidToken(GroupUpdatedEvent groupUpdatedEvent) {
if (mCallback == null) {
mContactsPresenter.attachView(this);
}
mCallback.loadGroupsFromDB();
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (mCallback == null) {
return;
}
Bundle bundle = intent.getBundleExtra(Constants.MESSAGE);
Message message = new Message(bundle.getString(Constants.MESSAGE_ID),
bundle.getLong(Constants.GROUP_ID),
bundle.getLong(Constants.CREATED_AT),
bundle.getString(Constants.MESSAGE_TEXT),
bundle.getString(Constants.FILE),
bundle.getString(Constants.SMALLER_IMAGE),
bundle.getString(Constants.FILENAME),
null,
bundle.getLong(Constants.USER_ID),
bundle.getString(Constants.USERNAME),
bundle.getString(Constants.USER_AVATAR),
Message.Status.DELIVERED,
false,
mUserIdPreference.get(),
false);
mCallback.storeGroupLocally(bundle.getLong(Constants.GROUP_ID), message);
}
};
}

View File

@@ -0,0 +1,197 @@
package com.p3.bewoplaner.page.contacts;
import com.p3.bewoplaner.R;
import com.p3.bewoplaner.data.AppPrefsConstants;
import com.p3.bewoplaner.data.api.common.ErrorHandlingResponseConverter;
import com.p3.bewoplaner.data.api.responses.GroupResponse;
import com.p3.bewoplaner.data.api.services.ChatService;
import com.p3.bewoplaner.data.prefs.LongPreference;
import com.p3.bewoplaner.data.repository.GroupRealmRepository;
import com.p3.bewoplaner.data.repository.UserRealmRepository;
import com.p3.bewoplaner.data.repository.specifications.GroupByIdSpecification;
import com.p3.bewoplaner.data.repository.specifications.GroupByNameSpecification;
import com.p3.bewoplaner.data.repository.specifications.GroupSpecification;
import com.p3.bewoplaner.data.repository.specifications.UserByIdSpecification;
import com.p3.bewoplaner.model.Group;
import com.p3.bewoplaner.model.Message;
import com.p3.bewoplaner.page.mvp.MVPAbstractPresenter;
import com.p3.bewoplaner.utils.SystemUtils;
import com.p3.bewoplaner.utils.rx.RxUtils;
import android.support.annotation.NonNull;
import android.util.Log;
import java.util.ArrayList;
import javax.inject.Inject;
import javax.inject.Named;
import rx.Observable;
import rx.Subscriber;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Func1;
import rx.schedulers.Schedulers;
/**
* Created by imarneanu on 2/20/17.
*/
class ContactsPresenter extends MVPAbstractPresenter<ContactsView>
implements ContactsView.Callback {
@Inject
ChatService mChatService;
@Inject
GroupRealmRepository mGroupRealmRepository;
@Inject
UserRealmRepository mUserRealmRepository;
@Inject
SystemUtils mSystemUtils;
@Inject
@Named(AppPrefsConstants.USER_ID)
LongPreference mUserIdReference;
@Inject
ContactsPresenter() {
}
@Override
protected void afterAttachView(@NonNull ContactsView view) {
super.afterAttachView(view);
view.setCallback(this);
view.showProgress();
}
@Override
protected void beforeDetachView(@NonNull ContactsView view) {
view.setCallback(null);
super.beforeDetachView(view);
}
/**
* Get all user groups from server.
*/
@Override
public void loadGroups() {
Subscription subscription = mChatService.getGroups()
.flatMap(new ErrorHandlingResponseConverter<>())
.flatMap(new Func1<GroupResponse, Observable<ArrayList<Group>>>() {
@Override
public Observable<ArrayList<Group>> call(GroupResponse groupResponse) {
if (groupResponse.response.groups != null) {
for (Group group : groupResponse.response.groups) {
group.userId = mUserIdReference.get();
if (group.lastMessage != null) {
group.lastMessage.status = Message.Status.DELIVERED;
group.lastMessage.userId = mUserIdReference.get();
}
mUserRealmRepository.update(group.users, new UserByIdSpecification()).subscribe();
}
}
return Observable.just(groupResponse.response.groups);
}
})
.compose(RxUtils.provideDefaultTransformer())
.subscribe(this::groupsSuccess, this::groupsError);
addSubscription(subscription);
}
/**
* Send error messages to view.
*/
private void groupsError(Throwable throwable) {
if (mSystemUtils.isNetworkUnavailable()) {
getView().showError(R.string.error_no_internet_connection);
return;
}
getView().showError(R.string.error_something_went_wrong);
}
private void groupsSuccess(ArrayList<Group> groups) {
mGroupRealmRepository.update(groups, new GroupByIdSpecification(mUserIdReference.get()))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Object>() {
@Override
public void onCompleted() {
loadGroupsFromDB();
}
@Override
public void onError(Throwable e) {
Log.e(ContactsPresenter.class.getSimpleName(), e.getMessage(), e);
}
@Override
public void onNext(Object o) {
// nop here
}
});
}
/**
* Load groups from local database.
*/
@Override
public void loadGroupsFromDB() {
GroupSpecification groupSpecification = new GroupSpecification(mUserIdReference.get());
mGroupRealmRepository.queryForAll(groupSpecification)
.map(ArrayList::new)
.compose(RxUtils.provideDefaultTransformer())
.subscribe(this::loadGroupsFromDBSuccessful, this::loadGroupsError);
}
/**
* Load groups by name.
*/
@Override
public void loadGroupsContainingName(String name) {
GroupByNameSpecification groupByNameSpecification = new GroupByNameSpecification(name, mUserIdReference.get());
mGroupRealmRepository.queryForAll(groupByNameSpecification)
.map(ArrayList::new)
.compose(RxUtils.provideDefaultTransformer())
.subscribe(this::loadGroupsFromDBSuccessful, this::loadGroupsError);
}
private void loadGroupsFromDBSuccessful(ArrayList<Group> groups) {
if (!isAttached()) {
return;
}
getView().hideProgress();
getView().setGroups(groups);
}
@Override
public void storeGroupLocally(long groupId, Message message) {
Group group = new Group();
group.id = groupId;
group.lastMessage = message;
mGroupRealmRepository.updateGroupMessage(group, new GroupByIdSpecification(mUserIdReference.get()))
.subscribe(new Subscriber<Object>() {
@Override
public void onCompleted() {
loadGroupsFromDB();
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Object o) {
}
});
}
private void loadGroupsError(Throwable throwable) {
Log.e(ContactsPresenter.class.getSimpleName(), throwable.getMessage(), throwable);
}
}

View File

@@ -0,0 +1,35 @@
package com.p3.bewoplaner.page.contacts;
import com.p3.bewoplaner.model.Group;
import com.p3.bewoplaner.model.Message;
import com.p3.bewoplaner.page.mvp.MVPView;
import android.support.annotation.StringRes;
import java.util.ArrayList;
/**
* Created by imarneanu on 2/20/17.
*/
interface ContactsView extends MVPView {
void setCallback(Callback setCallback);
void showProgress();
void hideProgress();
void setGroups(ArrayList<Group> groups);
void showError(@StringRes int message);
interface Callback {
void storeGroupLocally(long groupId, Message message);
void loadGroups();
void loadGroupsFromDB();
void loadGroupsContainingName(String name);
}
}

View File

@@ -0,0 +1,73 @@
package com.p3.bewoplaner.page.impressum;
import com.p3.bewoplaner.R;
import android.annotation.TargetApi;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
/**
* Created by laszloffiferenc on 2/23/17.
*/
public class ImpressumActivity extends AppCompatActivity {
@BindView(R.id.impressum_web_view)
WebView mWebView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web_view);
ButterKnife.bind(this);
mWebView.setWebViewClient(new Callback());
mWebView.loadUrl("file:///android_asset/impressum.html");
}
@OnClick(R.id.impressum_toolbar_back_arrow)
void backButton() {
onBackPressed();
}
private class Callback extends WebViewClient {
@SuppressWarnings("deprecation")
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
final Uri uri = Uri.parse(url);
return handleUri(uri);
}
@TargetApi(Build.VERSION_CODES.N)
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
final Uri uri = request.getUrl();
return handleUri(uri);
}
private boolean handleUri(final Uri uri) {
String url = uri.toString();
if (url.startsWith("mailto:")) {
Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.parse(url));
if (getPackageManager().queryIntentActivities(intent, 0).size() > 0) {
startActivity(intent);
}
}
if (url.startsWith("tel:")) {
Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
if (getPackageManager().queryIntentActivities(intent, 0).size() > 0) {
startActivity(intent);
}
}
return true;
}
}
}

View File

@@ -0,0 +1,197 @@
package com.p3.bewoplaner.page.login;
import com.google.firebase.iid.FirebaseInstanceId;
import com.p3.bewoplaner.BewoplanerApplication;
import com.p3.bewoplaner.R;
import com.p3.bewoplaner.data.AppPrefsConstants;
import com.p3.bewoplaner.data.prefs.AppPrefs;
import com.p3.bewoplaner.di.InjectionHelper;
import com.p3.bewoplaner.page.main.MainActivity;
import com.p3.bewoplaner.utils.AppStoppedService;
import com.p3.bewoplaner.utils.DialogHandler;
import com.p3.bewoplaner.utils.ui.ViewUtils;
import com.securepreferences.SecurePreferences;
import android.content.Intent;
import android.os.Bundle;
import android.provider.Settings;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.design.widget.Snackbar;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ProgressBar;
import java.util.Map;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.OnEditorAction;
/**
* Created by imarneanu on 2/10/17.
*/
public class LoginActivity extends AppCompatActivity
implements LoginView {
@BindView(R.id.login_chat_code)
EditText mChatCodeEditText;
@BindView(R.id.login_account)
EditText mAccountEditText;
@BindView(R.id.login_username)
EditText mUsernameEditText;
@BindView(R.id.login_password)
EditText mPasswordEditText;
@BindView(R.id.login_button)
Button mLoginButton;
@BindView(R.id.login_progress_bar)
ProgressBar mProgressBar;
@BindView(R.id.login_save_password_checkBox)
CheckBox mSavePasswordCheckbox;
@Inject
LoginPresenter mLoginPresenter;
@Inject
@AppPrefs
SecurePreferences mSecuredPrefs;
private Snackbar mSnackbar;
private Callback mCallback;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
InjectionHelper.getLoginComponent(this).inject(this);
ButterKnife.bind(this);
mLoginPresenter.attachView(this);
decryptPreference();
ViewUtils.setProgressBarColor(mProgressBar, R.color.colorDefault);
mCallback.logout(Settings.Secure.getString(this.getContentResolver(),
Settings.Secure.ANDROID_ID), true, null);
}
@OnEditorAction(R.id.login_password)
public boolean passwordDoneClicked(int actionId) {
if (actionId == EditorInfo.IME_ACTION_DONE) {
ViewUtils.hideKeyboard(this);
}
return false;
}
@OnClick(R.id.login_button)
void loginButtonClicked() {
mCallback.checkVoucher(mAccountEditText.getText().toString().trim(),
mChatCodeEditText.getText().toString().trim(),
mPasswordEditText.getText().toString(),
mSavePasswordCheckbox.isChecked());
}
@Override
public void voucherIsNotValid() {
DialogHandler.createNoTitleDialog(this,
R.string.login_voucher_code_is_not_valid,
false,
android.R.string.ok,
(dialog, which) -> dialog.dismiss()).show();
}
@Override
public void voucherIsValid() {
((BewoplanerApplication) getApplication()).resetApplicationComponent();
verifyIfIsChecked();
}
private void verifyIfIsChecked() {
mLoginPresenter.detachView();
InjectionHelper.destroyLoginComponent();
InjectionHelper.getLoginComponent(this).inject(this);
mLoginPresenter.attachView(this);
mCallback.tryToLogin(mUsernameEditText.getText().toString().trim(), mPasswordEditText.getText().toString());
}
@Override
public void loginSuccessful() {
mCallback.tryToRegisterDevice(Settings.Secure.getString(this.getContentResolver(),
Settings.Secure.ANDROID_ID), FirebaseInstanceId.getInstance().getToken());
if (!mSavePasswordCheckbox.isChecked()) {
startService(new Intent(getBaseContext(), AppStoppedService.class));
}
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
finish();
}
private void decryptPreference() {
for (Map.Entry<String, ?> entry : mSecuredPrefs.getAll().entrySet()) {
final String key = entry.getKey();
if (key == null) {
continue;
} else if (key.equals(SecurePreferences.hashPrefKey(AppPrefsConstants.CHAT_CODE))) {
mChatCodeEditText.setText(String.valueOf(mSecuredPrefs.getString(AppPrefsConstants.CHAT_CODE, null)));
} else if (key.equals(SecurePreferences.hashPrefKey(AppPrefsConstants.ACCOUNT_NO))) {
mAccountEditText.setText(String.valueOf(mSecuredPrefs.getString(AppPrefsConstants.ACCOUNT_NO, null)));
}
}
}
@Override
public void setCallback(Callback callback) {
mCallback = callback;
}
@Override
public void showLoading() {
mSavePasswordCheckbox.setEnabled(false);
mUsernameEditText.setEnabled(false);
mPasswordEditText.setEnabled(false);
mChatCodeEditText.setEnabled(false);
mAccountEditText.setEnabled(false);
mProgressBar.setVisibility(View.VISIBLE);
mLoginButton.setVisibility(View.GONE);
}
@Override
public void showContent() {
mSavePasswordCheckbox.setEnabled(true);
mUsernameEditText.setEnabled(true);
mPasswordEditText.setEnabled(true);
mChatCodeEditText.setEnabled(true);
mAccountEditText.setEnabled(true);
mProgressBar.setVisibility(View.GONE);
mLoginButton.setVisibility(View.VISIBLE);
}
@Override
public void showError(@StringRes int messageRes) {
showError(getString(messageRes));
}
@Override
public void showError(String message) {
if (mSnackbar == null) {
mSnackbar = Snackbar.make(findViewById(R.id.login_coordinator_layout), message, Snackbar.LENGTH_LONG)
.setAction("Retry", view -> verifyIfIsChecked());
mSnackbar.setActionTextColor(ContextCompat.getColor(this, R.color.colorDefault));
}
mSnackbar.setText(message);
mSnackbar.show();
}
@Override
protected void onDestroy() {
InjectionHelper.destroyLoginComponent();
mLoginPresenter.detachView();
super.onDestroy();
}
}

View File

@@ -0,0 +1,15 @@
package com.p3.bewoplaner.page.login;
import com.p3.bewoplaner.di.ApplicationComponent;
import com.p3.bewoplaner.di.scopes.ActivityScope;
import dagger.Component;
/**
* Created by laszloffiferenc on 2/21/17.
*/
@ActivityScope
@Component(dependencies = ApplicationComponent.class, modules = LoginModule.class)
public interface LoginComponent {
void inject(LoginActivity loginActivity);
}

View File

@@ -0,0 +1,37 @@
package com.p3.bewoplaner.page.login;
import com.p3.bewoplaner.data.api.services.LoginService;
import com.p3.bewoplaner.data.api.services.RegisterService;
import com.p3.bewoplaner.data.api.services.VoucherService;
import com.p3.bewoplaner.di.P3DSEndpoint;
import com.p3.bewoplaner.di.VoucherEndpoint;
import com.p3.bewoplaner.di.scopes.ActivityScope;
import dagger.Module;
import dagger.Provides;
import retrofit2.Retrofit;
/**
* Created by laszloffiferenc on 2/21/17.
*/
@Module
public class LoginModule {
@Provides
@ActivityScope
LoginService provideLoginService(@P3DSEndpoint Retrofit retrofit) {
return retrofit.create(LoginService.class);
}
@Provides
@ActivityScope
RegisterService provideRegisterService(@P3DSEndpoint Retrofit retrofit) {
return retrofit.create(RegisterService.class);
}
@Provides
@ActivityScope
VoucherService provideVoucherService(@VoucherEndpoint Retrofit retrofit) {
return retrofit.create(VoucherService.class);
}
}

View File

@@ -0,0 +1,473 @@
package com.p3.bewoplaner.page.login;
import com.google.gson.JsonObject;
import com.bumptech.glide.Glide;
import com.p3.bewoplaner.R;
import com.p3.bewoplaner.data.AppPrefsConstants;
import com.p3.bewoplaner.data.api.common.ErrorHandlingResponseConverter;
import com.p3.bewoplaner.data.api.common.ErrorThrowable;
import com.p3.bewoplaner.data.api.common.ErrorType;
import com.p3.bewoplaner.data.api.common.ErrorUtils;
import com.p3.bewoplaner.data.api.responses.RegisterResponse;
import com.p3.bewoplaner.data.api.services.LoginService;
import com.p3.bewoplaner.data.api.services.RegisterService;
import com.p3.bewoplaner.data.api.services.VoucherService;
import com.p3.bewoplaner.data.prefs.AppPrefs;
import com.p3.bewoplaner.data.prefs.BooleanPreference;
import com.p3.bewoplaner.data.prefs.LongPreference;
import com.p3.bewoplaner.data.prefs.StringPreference;
import com.p3.bewoplaner.data.repository.GroupRealmRepository;
import com.p3.bewoplaner.data.repository.MessageRealmRepository;
import com.p3.bewoplaner.data.repository.RealmProvider;
import com.p3.bewoplaner.data.repository.UserRealmRepository;
import com.p3.bewoplaner.model.User;
import com.p3.bewoplaner.page.main.LogoutNavigation;
import com.p3.bewoplaner.page.main.MainNavigation;
import com.p3.bewoplaner.page.mvp.MVPAbstractPresenter;
import com.p3.bewoplaner.utils.SystemUtils;
import com.p3.bewoplaner.utils.rx.RxUtils;
import com.securepreferences.SecurePreferences;
import android.app.NotificationManager;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
import java.io.File;
import java.net.SocketTimeoutException;
import javax.inject.Inject;
import javax.inject.Named;
import io.realm.Realm;
import rx.Observable;
import rx.Subscriber;
import rx.Subscription;
import rx.functions.Func1;
import rx.schedulers.Schedulers;
/**
* Created by laszloffiferenc on 2/21/17.
*/
public class LoginPresenter extends MVPAbstractPresenter<LoginView>
implements LoginView.Callback {
private static final String TAG = "error";
@Inject
LoginService mLoginService;
@Inject
RegisterService mRegisterService;
@Inject
VoucherService mVoucherService;
@Inject
@Named(AppPrefsConstants.API_ENDPOINT)
StringPreference mApiEndpointPreference;
@Inject
@Named(AppPrefsConstants.LOGIN_TOKEN)
StringPreference mLoginTokenPreference;
@Inject
@Named(AppPrefsConstants.CHAT_CODE)
StringPreference mChatCodePreference;
@Inject
@Named(AppPrefsConstants.SAVE_PASSWORD)
BooleanPreference mSavePasswordPreference;
@Inject
@Named(AppPrefsConstants.USER_ID)
LongPreference mUserIdReference;
@Inject
@Named(AppPrefsConstants.ACCOUNT_NO)
StringPreference mAccountNumberPreference;
@Inject
@Named(AppPrefsConstants.AVATAR)
StringPreference mAvatarPreference;
@Inject
@Named(AppPrefsConstants.NAME)
StringPreference mProfileNamePreference;
@Inject
@Named(AppPrefsConstants.PHONE_NUMBER)
StringPreference mPhoneNumberPreference;
@Inject
@AppPrefs
SharedPreferences mSharedPreferences;
@Inject
@AppPrefs
SecurePreferences mSecurePreferences;
@Inject
SystemUtils mSystemUtils;
@Inject
GroupRealmRepository mGroupRealmRepository;
@Inject
MessageRealmRepository mMessageRealmRepository;
@Inject
UserRealmRepository mUserRealmRepository;
@Inject
RealmProvider mRealmProvider;
@Inject
NotificationManager mNotificationManager;
@Inject
Glide mGlide;
@Inject
Context mContext;
private String mLastVoucher;
@Inject
LoginPresenter() {
}
@Override
protected void afterAttachView(@NonNull LoginView view) {
super.afterAttachView(view);
view.setCallback(this);
}
@Override
protected void beforeDetachView(@NonNull LoginView view) {
view.setCallback(null);
super.beforeDetachView(view);
}
/**
* Try to login with the given parameters
* @param username login username as {@code String}
* @param password login password as {@code String}
*/
private void loginCall(String username, String password) {
getView().showLoading();
Subscription subscription = mLoginService.login(username, password)
.compose(RxUtils.provideDefaultTransformer())
.flatMap(new ErrorHandlingResponseConverter<>())
.map(loginResponse -> loginResponse.response.user)
.subscribe(this::loginSuccess, this::loginError);
addSubscription(subscription);
}
/**
* Error to show in view if something went wrong during the login.
*/
private void loginError(Throwable throwable) {
getView().showContent();
if (mSystemUtils.isNetworkUnavailable()) {
getView().showError(R.string.error_no_internet_connection);
return;
}
if (ErrorUtils.getErrorType(throwable) == ErrorType.DATA_INPUT) {
ErrorThrowable errorThrowable = (ErrorThrowable) throwable;
if (errorThrowable.getError().loginFailed != null && errorThrowable.getError().loginFailed.size() > 0) {
getView().showError(errorThrowable.getError().loginFailed.get(0));
return;
}
}
getView().showError(R.string.error_something_went_wrong);
}
/**
* Set user data in preferences if login is successful.
* @param user {@code User} the current logged in user.
*/
private void loginSuccess(User user) {
mLoginTokenPreference.set(user.token);
mUserIdReference.set(user.id);
mAvatarPreference.set(user.avatar);
mPhoneNumberPreference.set(user.mobile);
mProfileNamePreference.set(user.firstName.concat(" ").concat(user.lastName));
getView().loginSuccessful();
}
/**
*Checks the {@code String} username and {@code String} password
* if are corresponding to criteria.
*/
@Override
public void tryToLogin(String username, String password) {
if (!checkPassword(password)) {
return;
}
if (!checkUsername(username)) {
return;
}
loginCall(username, password);
}
/**
* Register the current device. Used if the login is successful.
*/
@Override
public void tryToRegisterDevice(String deviceId, String fcmToken) {
Subscription subscription = mRegisterService.registerDevice(deviceId, fcmToken)
.compose(RxUtils.provideDefaultTransformer())
.flatMap(new ErrorHandlingResponseConverter<>())
.map(registerResponse -> registerResponse.response)
.subscribe(this::registerSuccessful, this::registerError);
addSubscription(subscription);
}
/**
* Checks if the voucher necessary for login is correct.
*/
@Override
public void checkVoucher(String customerId, String voucher, String password, boolean isChecked) {
mLastVoucher = voucher;
mAccountNumberPreference.set(customerId);
mSavePasswordPreference.set(isChecked);
if (!checkPassword(password)) {
return;
}
if (TextUtils.isEmpty(voucher)) {
getView().showError(R.string.error_voucher_code_must_not_be_null);
return;
}
if (TextUtils.isEmpty(customerId)) {
getView().showError(R.string.error_customer_id_must_not_be_null);
return;
}
getView().showLoading();
Subscription subscription = mVoucherService.checkVoucher(customerId, voucher)
.flatMap(new Func1<JsonObject, Observable<Boolean>>() {
@Override
public Observable<Boolean> call(JsonObject response) {
if (response.has(voucher) && response.get(voucher) != null) {
JsonObject voucherObject = response.getAsJsonObject(voucher);
if (voucherObject.has("CustomerID")
&& !TextUtils.isEmpty(voucherObject.get("CustomerID").getAsString())
&& voucherObject.has("ServerName")
&& !TextUtils.isEmpty(voucherObject.get("ServerName").getAsString())) {
String serverName = voucherObject.get("ServerName").getAsString();
if (!serverName.startsWith("https")) {
serverName = "https://" + serverName + "/";
}
mApiEndpointPreference.set(serverName);
return Observable.just(true);
}
}
return Observable.just(false);
}
})
.compose(RxUtils.provideDefaultTransformer())
.subscribe(this::checkVoucherSuccessful, this::checkVoucherError);
addSubscription(subscription);
}
private void checkVoucherError(Throwable throwable) {
getView().showContent();
if (mSystemUtils.isNetworkUnavailable()) {
getView().showError(R.string.error_no_internet_connection);
return;
}
getView().showError(R.string.error_something_went_wrong);
}
private void checkVoucherSuccessful(Boolean vouchIsValid) {
getView().showContent();
if (vouchIsValid) {
mChatCodePreference.set(mLastVoucher);
getView().voucherIsValid();
return;
}
deleteData(true);
getView().voucherIsNotValid();
}
/**
* Logout from application deletes all login preferences and redirects to login screen.
*/
@Override
public void logout(String deviceId, boolean clearAllPrefs, MainNavigation mainNavigation) {
mNotificationManager.cancelAll();
if (!mLoginTokenPreference.isSet()) {
if (mainNavigation != null) {
mainNavigation.goToLoginActivity();
}
return;
}
mRegisterService.unregisterDevice(deviceId)
.compose(RxUtils.provideDefaultTransformer())
.flatMap(new ErrorHandlingResponseConverter<>())
.subscribe(new Subscriber<RegisterResponse>() {
@Override
public void onCompleted() {
//after device unregister we remove also the token
mLoginTokenPreference.delete();
deleteData(clearAllPrefs);
if (mainNavigation != null) {
mainNavigation.goToLoginActivity();
}
}
@Override
public void onError(Throwable e) {
Log.d(TAG, "onError: " + e);
//after device unregister we remove also the token
mLoginTokenPreference.delete();
deleteData(clearAllPrefs);
if (mainNavigation != null) {
mainNavigation.goToLoginActivity();
}
}
@Override
public void onNext(RegisterResponse registerResponse) {
Log.d(TAG, "onNext: " + registerResponse);
}
});
}
private void registerError(Throwable throwable) {
if (ErrorUtils.getErrorType(throwable) == ErrorType.DATA_INPUT) {
ErrorThrowable errorThrowable = (ErrorThrowable) throwable;
if (errorThrowable.getError().fcmToken != null && errorThrowable.getError().fcmToken.size() > 0) {
Log.d(TAG, "fcmToken error: " + errorThrowable.getError().fcmToken.get(0));
return;
}
if (errorThrowable.getError().deviceId != null && errorThrowable.getError().deviceId.size() > 0) {
Log.d(TAG, "fcmToken error: " + errorThrowable.getError().deviceId.get(0));
}
}
}
@Override
public void tryToLogout(String deviceId, MainNavigation mainNavigation) {
if (mSavePasswordPreference.get()) {
return;
}
logout(deviceId, false, mainNavigation);
}
@Override
public void checkIfVoucherStillAvailable(LogoutNavigation logoutNavigation) {
if (mAccountNumberPreference.get() == null) {
return;
}
String voucher = mChatCodePreference.get();
String customerId = mAccountNumberPreference.get();
Subscription subscription = mVoucherService.checkVoucher(customerId, voucher)
.compose(RxUtils.provideDefaultTransformer())
.subscribe(new Subscriber<JsonObject>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
if (mSystemUtils.isNetworkUnavailable() || e instanceof SocketTimeoutException) {
return;
}
Toast.makeText(mContext, "Please make sure you have internet connection", Toast.LENGTH_SHORT).show();
}
@Override
public void onNext(JsonObject response) {
if (!response.has(voucher) || response.get(voucher) == null) {
logoutNavigation.logoutDirectly();
return;
}
JsonObject voucherObject = response.getAsJsonObject(voucher);
if (!voucherObject.has("CustomerID")
|| TextUtils.isEmpty(voucherObject.get("CustomerID").getAsString())
|| !voucherObject.has("ServerName")
|| TextUtils.isEmpty(voucherObject.get("ServerName").getAsString())) {
logoutNavigation.logoutDirectly();
}
}
});
addSubscription(subscription);
}
/**
* Clears all login preferences.
*/
private void deleteData(boolean clearAllPrefs) {
if (clearAllPrefs) {
Observable.create(subscriber -> mGlide.clearDiskCache())
.subscribeOn(Schedulers.io())
.subscribe();
mGlide.clearMemory();
mSharedPreferences.edit().clear().apply();
mAccountNumberPreference.deleteFromSecurePreference();
mChatCodePreference.deleteFromSecurePreference();
mSavePasswordPreference.deleteFromSecurePreference();
File dir = mContext.getExternalFilesDir(null);
if (dir != null) {
if (dir.isDirectory()) {
String[] children = dir.list();
for (String aChildren : children) {
//noinspection ResultOfMethodCallIgnored
new File(dir, aChildren).delete();
}
} else {
Log.e("deleteData", dir.getAbsolutePath() + " is not a directory!");
}
} else {
dir = new File(Environment.getExternalStorageDirectory(), "BeWoPlaner");
if (dir.isDirectory()) {
String[] children = dir.list();
for (String aChildren : children) {
//noinspection ResultOfMethodCallIgnored
new File(dir, aChildren).delete();
}
} else {
Log.e("deleteData", dir.getAbsolutePath() + " is not a directory!");
}
}
Realm realm = mRealmProvider.provide();
realm.executeTransaction(realm1 -> realm1.deleteAll());
} else {
mApiEndpointPreference.delete();
}
}
private void registerSuccessful(String s) {
}
private boolean checkPassword(String password) {
if (password.length() >= 8) {
return true;
}
getView().showError(R.string.error_password_too_short);
return false;
}
private boolean checkUsername(String username) {
if (username.length() >= 4) {
return true;
}
getView().showError(R.string.error_username_too_short);
return false;
}
}

View File

@@ -0,0 +1,33 @@
package com.p3.bewoplaner.page.login;
import com.p3.bewoplaner.page.main.LogoutNavigation;
import com.p3.bewoplaner.page.main.MainNavigation;
import com.p3.bewoplaner.page.mvp.MVPLceView;
/**
* Created by laszloffiferenc on 2/21/17.
*/
interface LoginView extends MVPLceView {
void loginSuccessful();
void setCallback(Callback callback);
void voucherIsValid();
void voucherIsNotValid();
interface Callback {
void checkVoucher(String customerId, String voucher, String password, boolean isChecked);
void tryToLogin(String email, String password);
void tryToRegisterDevice(String deviceId, String fcmToken);
void logout(String deviceId, boolean clearAllPrefs, MainNavigation mainNavigation);
void tryToLogout(String deviceId, MainNavigation mainNavigation);
void checkIfVoucherStillAvailable(LogoutNavigation logoutNavigation);
}
}

View File

@@ -0,0 +1,7 @@
package com.p3.bewoplaner.page.main;
public interface LogoutNavigation {
void logoutDirectly();
}

View File

@@ -0,0 +1,279 @@
package com.p3.bewoplaner.page.main;
import com.bumptech.glide.Glide;
import com.p3.bewoplaner.R;
import com.p3.bewoplaner.data.AppPrefsConstants;
import com.p3.bewoplaner.data.events.InvalidTokenEvent;
import com.p3.bewoplaner.data.events.LogoutDirectlyEvent;
import com.p3.bewoplaner.data.prefs.BooleanPreference;
import com.p3.bewoplaner.data.prefs.StringPreference;
import com.p3.bewoplaner.di.InjectionHelper;
import com.p3.bewoplaner.page.chat.GroupChatActivity;
import com.p3.bewoplaner.page.chat.SingleChatActivity;
import com.p3.bewoplaner.page.contacts.ContactsFragment;
import com.p3.bewoplaner.page.impressum.ImpressumActivity;
import com.p3.bewoplaner.page.login.LoginActivity;
import com.p3.bewoplaner.page.login.LoginPresenter;
import com.p3.bewoplaner.page.profile.ProfileFragment;
import com.p3.bewoplaner.utils.Constants;
import com.p3.bewoplaner.utils.DialogHandler;
import com.p3.bewoplaner.utils.SystemUtils;
import com.p3.bewoplaner.utils.Utils;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.NavigationView;
import android.support.v4.app.Fragment;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import javax.inject.Inject;
import javax.inject.Named;
import butterknife.BindView;
import butterknife.ButterKnife;
import de.hdodenhof.circleimageview.CircleImageView;
/**
* Created by imarneanu on 2/9/17.
*/
public class MainActivity extends AppCompatActivity
implements MainNavigation, LogoutNavigation {
private static final String REDIRECT_TO_CHAT = "redirect_to_chat";
private static final String BUNDLE = "bundle";
private static final String LOGOUT = "logout";
@BindView(R.id.toolbar)
Toolbar mToolbar;
@BindView(R.id.drawer_layout)
DrawerLayout mDrawer;
@BindView(R.id.nav_view)
NavigationView mNavigationView;
@BindView(R.id.nav_listView)
ListView mNavigationListView;
@Inject
LoginPresenter mLoginPresenter;
@Inject
@Named(AppPrefsConstants.AVATAR)
StringPreference mAvatarPreference;
@Inject
@Named(AppPrefsConstants.LOGIN_TOKEN)
StringPreference mLoginTokenPreference;
@Inject
@Named(AppPrefsConstants.CHAT_OPENED)
BooleanPreference mChatOpenedPref;
@Inject
SystemUtils mSystemUtils;
private boolean mLogoutClicked;
public static Intent createIntent(Context context, Bundle bundle, int numberOfUsers, int groupId) {
Intent intent = new Intent(context, MainActivity.class);
intent.putExtra(REDIRECT_TO_CHAT, true);
intent.putExtra(BUNDLE, bundle);
intent.putExtra(Constants.GROUP_ID, groupId);
intent.putExtra(Constants.GROUP_NR_USERS, numberOfUsers);
intent.putExtra(LOGOUT, false);
return intent;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
InjectionHelper.getMainComponent(this).inject(this);
EventBus.getDefault().register(this);
setSupportActionBar(mToolbar);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, mDrawer, mToolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close) {
@Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
Utils.hideKeyboard(MainActivity.this);
}
};
mDrawer.closeDrawer(GravityCompat.START);
mDrawer.addDrawerListener(toggle);
View headerLayout = mNavigationView.getHeaderView(0);
CircleImageView avatar = (CircleImageView) headerLayout.findViewById(R.id.menu_profile_imageView);
Glide.with(this).load(mAvatarPreference.get()).into(avatar);
mDrawer.setOnTouchListener((view, motionEvent) -> {
Glide.with(view.getContext()).load(mAvatarPreference.get()).into(avatar);
return false;
});
toggle.syncState();
ArrayAdapter<String> adapter = new NavigationMenuAdapter(this,
getResources().getStringArray(R.array.nav_menu), this);
mNavigationListView.setAdapter(adapter);
switchFragment(new ContactsFragment());
onNewIntent(getIntent());
}
@Override
protected void onSaveInstanceState(Bundle outState) {
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (intent.hasExtra(REDIRECT_TO_CHAT) && !mChatOpenedPref.get()) {
Bundle message = intent.getBundleExtra(BUNDLE);
Intent startIntent;
if (intent.getIntExtra(Constants.GROUP_NR_USERS, 2) > 2) {
startIntent = GroupChatActivity.createIntent(this, message);
} else {
startIntent = SingleChatActivity.createIntent(this, message);
}
mChatOpenedPref.set(true);
startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(startIntent);
intent.removeExtra(REDIRECT_TO_CHAT);
setIntent(intent);
}
}
@Override
protected void onResume() {
super.onResume();
mLoginPresenter.checkIfVoucherStillAvailable(this);
}
private void switchFragment(@NonNull Fragment fragment) {
switchFragment(fragment, true);
}
private void switchFragment(@NonNull Fragment fragment, boolean clearBackStack) {
Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.content_main);
if (fragment.getClass().isInstance(currentFragment)) {
return;
}
if (clearBackStack) {
Utils.clearFullBackStack(this);
}
addFragmentToBackStack(fragment);
}
private void addFragmentToBackStack(@NonNull Fragment fragment) {
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.content_main, fragment, fragment.getTag())
.addToBackStack(fragment.getClass().getSimpleName())
.commitAllowingStateLoss();
}
@Override
public void onBackPressed() {
if (mDrawer.isDrawerOpen(GravityCompat.START)) {
mDrawer.closeDrawer(GravityCompat.START);
}
if (getSupportFragmentManager().getBackStackEntryCount() <= 1) {
finish();
}
mLoginPresenter.checkIfVoucherStillAvailable(this);
super.onBackPressed();
}
@Override
public void goToContacts() {
mLoginPresenter.checkIfVoucherStillAvailable(this);
mDrawer.closeDrawer(GravityCompat.START);
switchFragment(new ContactsFragment());
}
@Override
public void goToProfile() {
mLoginPresenter.checkIfVoucherStillAvailable(this);
mDrawer.closeDrawer(GravityCompat.START);
switchFragment(new ProfileFragment(), false);
}
@Override
public void logout() {
Dialog alertDialog = DialogHandler.createNoTitleDialog(this,
R.string.logout_confirmation,
false,
android.R.string.yes,
android.R.string.no,
(dialog, which) -> {
mLogoutClicked = true;
mDrawer.closeDrawer(GravityCompat.START);
mLoginPresenter.logout(Settings.Secure.getString(MainActivity.this.getContentResolver(),
Settings.Secure.ANDROID_ID), false, this);
},
(dialog, which) -> dialog.dismiss());
if (!isFinishing()) {
alertDialog.show();
}
}
@Override
public void goToImpressum() {
mDrawer.closeDrawer(GravityCompat.START);
Intent intent = new Intent(this, ImpressumActivity.class);
startActivity(intent);
}
@Override
public void logoutDirectly() {
mLoginPresenter.logout(Settings.Secure.getString(MainActivity.this.getContentResolver(),
Settings.Secure.ANDROID_ID), true, this);
}
@Override
public void goToLoginActivity() {
if (isFinishing()) {
return;
}
startActivity(new Intent(MainActivity.this, LoginActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP));
finish();
}
@SuppressWarnings("unused")
@Subscribe(threadMode = ThreadMode.MAIN)
public void onInvalidToken(InvalidTokenEvent invalidTokenEvent) {
Toast.makeText(this, R.string.login_again, Toast.LENGTH_LONG).show();
logoutDirectly();
}
@SuppressWarnings("unused")
@Subscribe(threadMode = ThreadMode.MAIN)
public void onLogoutDirectlyEvent(LogoutDirectlyEvent logoutDirectlyEvent) {
logoutDirectly();
}
@Override
protected void onDestroy() {
EventBus.getDefault().unregister(this);
mChatOpenedPref.set(false);
if (!mLogoutClicked && getIntent().getBooleanExtra(LOGOUT, true)) {
mLoginPresenter.tryToLogout(Settings.Secure.getString(this.getContentResolver(),
Settings.Secure.ANDROID_ID), this);
}
getIntent().removeExtra(LOGOUT);
InjectionHelper.destroyMainComponents();
super.onDestroy();
}
}

Some files were not shown because too many files have changed in this diff Show More