diff --git a/Habitica/build.gradle b/Habitica/build.gradle index cabd65237..560b4b0e3 100644 --- a/Habitica/build.gradle +++ b/Habitica/build.gradle @@ -55,9 +55,6 @@ dependencies { releaseImplementation('com.crashlytics.sdk.android:crashlytics:2.9.4@aar') { transitive = true } - // View Elements Binding :) - implementation 'com.jakewharton:butterknife:8.8.1' - kapt 'com.jakewharton:butterknife-compiler:8.8.1' //Dependency Injection implementation 'com.google.dagger:dagger:2.16' kapt 'com.google.dagger:dagger-compiler:2.16' diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/HabiticaBaseApplication.java b/Habitica/src/main/java/com/habitrpg/android/habitica/HabiticaBaseApplication.java deleted file mode 100644 index 3c741efe4..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/HabiticaBaseApplication.java +++ /dev/null @@ -1,265 +0,0 @@ -package com.habitrpg.android.habitica; - -import android.app.Application; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.res.Resources; -import android.database.DatabaseErrorHandler; -import android.database.sqlite.SQLiteDatabase; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v7.app.AppCompatDelegate; -import android.util.Log; - -import com.amplitude.api.Amplitude; -import com.amplitude.api.Identify; -import com.facebook.FacebookSdk; -import com.facebook.drawee.backends.pipeline.Fresco; -import com.facebook.imagepipeline.core.ImagePipelineConfig; -import com.habitrpg.android.habitica.api.HostConfig; -import com.habitrpg.android.habitica.components.AppComponent; -import com.habitrpg.android.habitica.data.ApiClient; -import com.habitrpg.android.habitica.data.InventoryRepository; -import com.habitrpg.android.habitica.helpers.RxErrorHandler; -import com.habitrpg.android.habitica.proxy.CrashlyticsProxy; -import com.habitrpg.android.habitica.ui.activities.IntroActivity; -import com.habitrpg.android.habitica.ui.activities.LoginActivity; -import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper; -import com.squareup.leakcanary.LeakCanary; -import com.squareup.leakcanary.RefWatcher; - -import org.solovyev.android.checkout.Billing; -import org.solovyev.android.checkout.Cache; -import org.solovyev.android.checkout.Checkout; -import org.solovyev.android.checkout.PurchaseVerifier; - -import javax.inject.Inject; - -import io.realm.Realm; -import io.realm.RealmConfiguration; - -//contains all HabiticaApplicationLogic except dagger componentInitialisation -public abstract class HabiticaBaseApplication extends Application { - - private static AppComponent component; - public RefWatcher refWatcher; - @Inject - ApiClient lazyApiHelper; - @Inject - InventoryRepository inventoryRepository; - @Inject - SharedPreferences sharedPrefs; - @Inject - CrashlyticsProxy crashlyticsProxy; - /** - * For better performance billing class should be used as singleton - */ - private Billing billing; - /** - * Application wide {@link Checkout} instance (can be used - * anywhere in the app). - * This instance contains all available products in the app. - */ - private Checkout checkout; - - public static HabiticaBaseApplication getInstance(Context context) { - return (HabiticaBaseApplication) context.getApplicationContext(); - } - - public static void logout(Context context) { - Realm realm = Realm.getDefaultInstance(); - getInstance(context).deleteDatabase(realm.getPath()); - realm.close(); - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); - boolean use_reminder = preferences.getBoolean("use_reminder", false); - String reminder_time = preferences.getString("reminder_time", "19:00"); - SharedPreferences.Editor editor = preferences.edit(); - editor.clear(); - editor.putBoolean("use_reminder", use_reminder); - editor.putString("reminder_time", reminder_time); - editor.apply(); - getInstance(context).lazyApiHelper.updateAuthenticationCredentials(null, null); - startActivity(LoginActivity.class, context); - } - - public static boolean checkUserAuthentication(Context context, HostConfig hostConfig) { - if (hostConfig == null || hostConfig.getApi() == null || hostConfig.getApi().equals("") || hostConfig.getUser() == null || hostConfig.getUser().equals("")) { - startActivity(IntroActivity.class, context); - - return false; - } - - return true; - } - - private static void startActivity(Class activityClass, Context context) { - Intent intent = new Intent(context, activityClass); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - } - - public static AppComponent getComponent() { - return component; - } - - @Override - public void onCreate() { - super.onCreate(); - if (LeakCanary.isInAnalyzerProcess(this)) { - // This process is dedicated to LeakCanary for heap analysis. - // You should not init your app in this process. - return; - } - setupRealm(); - setupDagger(); - setupLeakCanary(); - setupFacebookSdk(); - createBillingAndCheckout(); - HabiticaIconsHelper.init(this); - - AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); - - if (!BuildConfig.DEBUG) { - try { - Amplitude.getInstance().initialize(this, getString(R.string.amplitude_app_id)).enableForegroundTracking(this); - Identify identify = new Identify().setOnce("androidStore", BuildConfig.STORE); - Amplitude.getInstance().identify(identify); - } catch (Resources.NotFoundException ignored) { - } - } - ImagePipelineConfig config = ImagePipelineConfig.newBuilder(this) - .setDownsampleEnabled(true) - .build(); - Fresco.initialize(this, config); - - RxErrorHandler.init(crashlyticsProxy); - - checkIfNewVersion(); - } - - protected void setupRealm() { - Realm.init(this); - RealmConfiguration.Builder builder = new RealmConfiguration.Builder() - .schemaVersion(1) - .deleteRealmIfMigrationNeeded(); - try { - Realm.setDefaultConfiguration(builder.build()); - } catch (UnsatisfiedLinkError ignored) { - //Catch crash in tests - } - } - - private void checkIfNewVersion() { - PackageInfo info = null; - try { - info = getPackageManager().getPackageInfo(getPackageName(), 0); - } catch (PackageManager.NameNotFoundException e) { - Log.e("MyApplication", "couldn't get package info!"); - } - - if (info == null) { - return; - } - - int lastInstalledVersion = sharedPrefs.getInt("last_installed_version", 0); - if (lastInstalledVersion < info.versionCode) { - sharedPrefs.edit().putInt("last_installed_version", info.versionCode).apply(); - inventoryRepository.retrieveContent().subscribe(contentResult -> {}, RxErrorHandler.handleEmptyError()); - } - } - - private void setupDagger() { - component = initDagger(); - component.inject(this); - } - - protected abstract AppComponent initDagger(); - - private void setupLeakCanary() { - refWatcher = LeakCanary.install(this); - } - - private void setupFacebookSdk() { - String fbApiKey = null; - try { - ApplicationInfo ai = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA); - Bundle bundle = ai.metaData; - fbApiKey = bundle.getString(FacebookSdk.APPLICATION_ID_PROPERTY); - } catch (PackageManager.NameNotFoundException e) { - Log.e("FB Error", "Failed to load meta-data, NameNotFound: " + e.getMessage()); - } catch (NullPointerException e) { - Log.e("FB Error", "Failed to load meta-data, NullPointer: " + e.getMessage()); - } - if (fbApiKey != null) { - FacebookSdk.sdkInitialize(getApplicationContext()); - } - } - - @Override - public SQLiteDatabase openOrCreateDatabase(String name, - int mode, SQLiteDatabase.CursorFactory factory) { - return super.openOrCreateDatabase(getDatabasePath(name).getAbsolutePath(), mode, factory); - } - - @Override - public SQLiteDatabase openOrCreateDatabase(String name, - int mode, SQLiteDatabase.CursorFactory factory, DatabaseErrorHandler errorHandler) { - return super.openOrCreateDatabase(getDatabasePath(name).getAbsolutePath(), mode, factory, errorHandler); - } - - // endregion - - // region IAP - Specific - - @Override - public boolean deleteDatabase(String name) { - Realm realm = Realm.getDefaultInstance(); - realm.executeTransaction(realm1 -> { - realm1.deleteAll(); - realm1.close(); - }); - return true; - } - - private void createBillingAndCheckout() { - billing = new Billing(this, new Billing.DefaultConfiguration() { - @NonNull - @Override - public String getPublicKey() { - return "DONT-NEED-IT"; - } - - @Nullable - @Override - public Cache getCache() { - return Billing.newCache(); - } - - @NonNull - @Override - public PurchaseVerifier getPurchaseVerifier() { - return new HabiticaPurchaseVerifier(HabiticaBaseApplication.this, lazyApiHelper); - } - }); - - - checkout = Checkout.forApplication(billing); - } - - @NonNull - public Checkout getCheckout() { - return checkout; - } - - // endregion - - public Billing getBilling() { - return billing; - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/HabiticaBaseApplication.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/HabiticaBaseApplication.kt new file mode 100644 index 000000000..928b40405 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/HabiticaBaseApplication.kt @@ -0,0 +1,244 @@ +package com.habitrpg.android.habitica + +import android.app.Application +import android.content.Context +import android.content.Intent +import android.content.SharedPreferences +import android.content.pm.PackageInfo +import android.content.pm.PackageManager +import android.content.res.Resources +import android.database.DatabaseErrorHandler +import android.database.sqlite.SQLiteDatabase +import android.preference.PreferenceManager +import android.support.v7.app.AppCompatDelegate +import android.util.Log + +import com.amplitude.api.Amplitude +import com.amplitude.api.Identify +import com.facebook.FacebookSdk +import com.facebook.drawee.backends.pipeline.Fresco +import com.facebook.imagepipeline.core.ImagePipelineConfig +import com.habitrpg.android.habitica.api.HostConfig +import com.habitrpg.android.habitica.components.AppComponent +import com.habitrpg.android.habitica.data.ApiClient +import com.habitrpg.android.habitica.data.InventoryRepository +import com.habitrpg.android.habitica.extensions.notNull +import com.habitrpg.android.habitica.helpers.RxErrorHandler +import com.habitrpg.android.habitica.proxy.CrashlyticsProxy +import com.habitrpg.android.habitica.ui.activities.IntroActivity +import com.habitrpg.android.habitica.ui.activities.LoginActivity +import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper +import com.squareup.leakcanary.LeakCanary +import com.squareup.leakcanary.RefWatcher +import io.reactivex.functions.Consumer + +import org.solovyev.android.checkout.Billing +import org.solovyev.android.checkout.Cache +import org.solovyev.android.checkout.Checkout +import org.solovyev.android.checkout.PurchaseVerifier + +import javax.inject.Inject + +import io.realm.Realm +import io.realm.RealmConfiguration + +//contains all HabiticaApplicationLogic except dagger componentInitialisation +abstract class HabiticaBaseApplication : Application() { + var refWatcher: RefWatcher? = null + @Inject + internal lateinit var lazyApiHelper: ApiClient + @Inject + internal lateinit var inventoryRepository: InventoryRepository + @Inject + internal lateinit var sharedPrefs: SharedPreferences + @Inject + internal lateinit var crashlyticsProxy: CrashlyticsProxy + /** + * For better performance billing class should be used as singleton + */ + // endregion + + var billing: Billing? = null + private set + /** + * Application wide [Checkout] instance (can be used + * anywhere in the app). + * This instance contains all available products in the app. + */ + var checkout: Checkout? = null + private set + + override fun onCreate() { + super.onCreate() + if (LeakCanary.isInAnalyzerProcess(this)) { + // This process is dedicated to LeakCanary for heap analysis. + // You should not init your app in this process. + return + } + setupRealm() + setupDagger() + refWatcher = LeakCanary.install(this) + setupFacebookSdk() + createBillingAndCheckout() + HabiticaIconsHelper.init(this) + + AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) + + if (!BuildConfig.DEBUG) { + try { + Amplitude.getInstance().initialize(this, getString(R.string.amplitude_app_id)).enableForegroundTracking(this) + val identify = Identify().setOnce("androidStore", BuildConfig.STORE) + Amplitude.getInstance().identify(identify) + } catch (ignored: Resources.NotFoundException) { + } + + } + val config = ImagePipelineConfig.newBuilder(this) + .setDownsampleEnabled(true) + .build() + Fresco.initialize(this, config) + + RxErrorHandler.init(crashlyticsProxy) + + checkIfNewVersion() + } + + protected open fun setupRealm() { + Realm.init(this) + val builder = RealmConfiguration.Builder() + .schemaVersion(1) + .deleteRealmIfMigrationNeeded() + try { + Realm.setDefaultConfiguration(builder.build()) + } catch (ignored: UnsatisfiedLinkError) { + //Catch crash in tests + } + + } + + private fun checkIfNewVersion() { + var info: PackageInfo? = null + try { + info = packageManager.getPackageInfo(packageName, 0) + } catch (e: PackageManager.NameNotFoundException) { + Log.e("MyApplication", "couldn't get package info!") + } + + if (info == null) { + return + } + + val lastInstalledVersion = sharedPrefs.getInt("last_installed_version", 0) + if (lastInstalledVersion < info.versionCode) { + sharedPrefs.edit().putInt("last_installed_version", info.versionCode).apply() + inventoryRepository.retrieveContent().subscribe(Consumer { }, RxErrorHandler.handleEmptyError()) + } + } + + private fun setupDagger() { + component = initDagger() + component?.inject(this) + } + + protected abstract fun initDagger(): AppComponent + + private fun setupFacebookSdk() { + var fbApiKey: String? = null + try { + val ai = packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA) + val bundle = ai.metaData + fbApiKey = bundle.getString(FacebookSdk.APPLICATION_ID_PROPERTY) + } catch (e: PackageManager.NameNotFoundException) { + Log.e("FB Error", "Failed to load meta-data, NameNotFound: " + e.message) + } catch (e: NullPointerException) { + Log.e("FB Error", "Failed to load meta-data, NullPointer: " + e.message) + } + + if (fbApiKey != null) { + FacebookSdk.sdkInitialize(applicationContext) + } + } + + override fun openOrCreateDatabase(name: String, + mode: Int, factory: SQLiteDatabase.CursorFactory): SQLiteDatabase { + return super.openOrCreateDatabase(getDatabasePath(name).absolutePath, mode, factory) + } + + override fun openOrCreateDatabase(name: String, + mode: Int, factory: SQLiteDatabase.CursorFactory, errorHandler: DatabaseErrorHandler?): SQLiteDatabase { + return super.openOrCreateDatabase(getDatabasePath(name).absolutePath, mode, factory, errorHandler) + } + + // endregion + + // region IAP - Specific + + override fun deleteDatabase(name: String): Boolean { + val realm = Realm.getDefaultInstance() + realm.executeTransaction { realm1 -> + realm1.deleteAll() + realm1.close() + } + return true + } + + private fun createBillingAndCheckout() { + billing = Billing(this, object : Billing.DefaultConfiguration() { + override fun getPublicKey(): String { + return "DONT-NEED-IT" + } + + override fun getCache(): Cache? { + return Billing.newCache() + } + + override fun getPurchaseVerifier(): PurchaseVerifier { + return HabiticaPurchaseVerifier(this@HabiticaBaseApplication, lazyApiHelper) + } + }) + + billing.notNull { checkout = Checkout.forApplication(it) } + } + + companion object { + + var component: AppComponent? = null + private set + + fun getInstance(context: Context): HabiticaBaseApplication { + return context.applicationContext as HabiticaBaseApplication + } + + fun logout(context: Context) { + val realm = Realm.getDefaultInstance() + getInstance(context).deleteDatabase(realm.path) + realm.close() + val preferences = PreferenceManager.getDefaultSharedPreferences(context) + val useReminder = preferences.getBoolean("use_reminder", false) + val reminderTime = preferences.getString("reminder_time", "19:00") + val editor = preferences.edit() + editor.clear() + editor.putBoolean("use_reminder", useReminder) + editor.putString("reminder_time", reminderTime) + editor.apply() + getInstance(context).lazyApiHelper.updateAuthenticationCredentials(null, null) + startActivity(LoginActivity::class.java, context) + } + + fun checkUserAuthentication(context: Context, hostConfig: HostConfig?): Boolean { + if (hostConfig == null || hostConfig.api == null || hostConfig.api == "" || hostConfig.user == null || hostConfig.user == "") { + startActivity(IntroActivity::class.java, context) + + return false + } + + return true + } + + private fun startActivity(activityClass: Class<*>, context: Context) { + val intent = Intent(context, activityClass) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) + context.startActivity(intent) + } + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt index a45a7b20b..2498460e4 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/ApiClientImpl.kt @@ -88,7 +88,7 @@ class ApiClientImpl//private OnHabitsAPIResult mResultListener; init { this.popupNotificationsManager.setApiClient(this) - HabiticaBaseApplication.getComponent().inject(this) + HabiticaBaseApplication.component?.inject(this) crashlyticsProxy.setUserIdentifier(this.hostConfig.user) crashlyticsProxy.setUserName(this.hostConfig.user) Amplitude.getInstance().userId = this.hostConfig.user diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/FirstDayOfTheWeekHelper.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/FirstDayOfTheWeekHelper.java index 80888c923..b2ea103ed 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/FirstDayOfTheWeekHelper.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/FirstDayOfTheWeekHelper.java @@ -8,7 +8,7 @@ import java.util.Calendar; public class FirstDayOfTheWeekHelper { private int firstDayOfTheWeek; - private int dailyTaskFormOffset; + public int dailyTaskFormOffset; private FirstDayOfTheWeekHelper(int dailyTaskFormOffset, int firstDayOfTheWeek) { this.dailyTaskFormOffset = dailyTaskFormOffset; diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/RemindersManager.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/RemindersManager.java index 0ab468eac..d23fd40c0 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/RemindersManager.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/RemindersManager.java @@ -20,6 +20,7 @@ import java.text.DateFormat; import java.text.ParseException; import java.util.Calendar; import java.util.Date; +import java.util.Objects; import java.util.TimeZone; import java.util.UUID; @@ -32,7 +33,7 @@ public class RemindersManager { private DateFormat dateFormater; public RemindersManager(String taskType) { - HabiticaBaseApplication.getComponent().inject(this); + Objects.requireNonNull(HabiticaBaseApplication.Companion.getComponent()).inject(this); if (taskType.equals("todo")) { dateFormater = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT); } else { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/SoundFileLoader.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/SoundFileLoader.kt index aff466c48..103bc3be7 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/SoundFileLoader.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/SoundFileLoader.kt @@ -4,6 +4,7 @@ import android.annotation.SuppressLint import android.content.Context import android.os.Environment import com.habitrpg.android.habitica.HabiticaApplication +import com.habitrpg.android.habitica.HabiticaBaseApplication import io.reactivex.Maybe import io.reactivex.Observable import io.reactivex.Single @@ -21,7 +22,7 @@ class SoundFileLoader(private val context: Context) { private val externalCacheDir: String? get() { - val cacheDir = HabiticaApplication.getInstance(context).getExternalFilesDir(Environment.DIRECTORY_NOTIFICATIONS) + val cacheDir = HabiticaBaseApplication.getInstance(context).getExternalFilesDir(Environment.DIRECTORY_NOTIFICATIONS) return cacheDir?.path } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/SoundManager.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/SoundManager.kt index b067c2037..2c67ec9c3 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/SoundManager.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/SoundManager.kt @@ -17,7 +17,7 @@ class SoundManager { private val loadedSoundFiles: MutableMap = HashMap() init { - HabiticaBaseApplication.getComponent().inject(this) + HabiticaBaseApplication.component?.inject(this) } fun preloadAllFiles(): Maybe> { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseInstanceIDService.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseInstanceIDService.java index c3e1f59b0..9b0545ff0 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseInstanceIDService.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseInstanceIDService.java @@ -4,6 +4,8 @@ import com.google.firebase.iid.FirebaseInstanceId; import com.google.firebase.iid.FirebaseInstanceIdService; import com.habitrpg.android.habitica.HabiticaApplication; +import java.util.Objects; + import javax.inject.Inject; /** @@ -16,7 +18,7 @@ public class HabiticaFirebaseInstanceIDService extends FirebaseInstanceIdService @Override public void onTokenRefresh() { - HabiticaApplication.getComponent().inject(this); + Objects.requireNonNull(HabiticaApplication.Companion.getComponent()).inject(this); String refreshedToken = FirebaseInstanceId.getInstance().getToken(); pushNotificationManager.setRefreshedToken(refreshedToken); } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseMessagingService.java b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseMessagingService.java index f3b1ad009..132222b1a 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseMessagingService.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/notifications/HabiticaFirebaseMessagingService.java @@ -4,6 +4,8 @@ import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.RemoteMessage; import com.habitrpg.android.habitica.HabiticaApplication; +import java.util.Objects; + import javax.inject.Inject; /** @@ -16,7 +18,7 @@ public class HabiticaFirebaseMessagingService extends FirebaseMessagingService { @Override public void onMessageReceived(RemoteMessage remoteMessage) { - HabiticaApplication.getComponent().inject(this); + Objects.requireNonNull(HabiticaApplication.Companion.getComponent()).inject(this); pushNotificationManager.displayNotification(remoteMessage); } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/LocalNotificationActionReceiver.java b/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/LocalNotificationActionReceiver.java index 3953b8929..455f2b7d5 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/LocalNotificationActionReceiver.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/LocalNotificationActionReceiver.java @@ -15,6 +15,8 @@ import com.habitrpg.android.habitica.data.UserRepository; import com.habitrpg.android.habitica.helpers.RxErrorHandler; import com.habitrpg.android.habitica.models.user.User; +import java.util.Objects; + import javax.inject.Inject; public class LocalNotificationActionReceiver extends BroadcastReceiver { @@ -33,7 +35,7 @@ public class LocalNotificationActionReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - HabiticaBaseApplication.getComponent().inject(this); + Objects.requireNonNull(HabiticaBaseApplication.Companion.getComponent()).inject(this); this.resources = context.getResources(); this.action = intent.getAction(); diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/NotificationPublisher.java b/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/NotificationPublisher.java index 523e1dcaa..3a0c37539 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/NotificationPublisher.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/NotificationPublisher.java @@ -11,6 +11,7 @@ import android.support.v4.content.ContextCompat; import android.support.v4.content.WakefulBroadcastReceiver; import com.habitrpg.android.habitica.HabiticaApplication; +import com.habitrpg.android.habitica.HabiticaBaseApplication; import com.habitrpg.android.habitica.R; import com.habitrpg.android.habitica.data.TaskRepository; import com.habitrpg.android.habitica.helpers.RxErrorHandler; @@ -19,6 +20,8 @@ import com.habitrpg.android.habitica.models.tasks.Task; import com.habitrpg.android.habitica.modules.AppModule; import com.habitrpg.android.habitica.ui.activities.MainActivity; +import java.util.Objects; + import javax.inject.Inject; import javax.inject.Named; @@ -39,7 +42,7 @@ public class NotificationPublisher extends WakefulBroadcastReceiver { public void onReceive(Context context, Intent intent) { this.context = context; if (taskRepository == null) { - HabiticaApplication.getComponent().inject(this); + Objects.requireNonNull(HabiticaBaseApplication.Companion.getComponent()).inject(this); } boolean check_dailies = intent.getBooleanExtra(CHECK_DAILIES, false); diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/TaskAlarmBootReceiver.java b/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/TaskAlarmBootReceiver.java index 70be45d24..7442d86c0 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/TaskAlarmBootReceiver.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/TaskAlarmBootReceiver.java @@ -7,6 +7,8 @@ import android.content.Intent; import com.habitrpg.android.habitica.HabiticaApplication; import com.habitrpg.android.habitica.helpers.TaskAlarmManager; +import java.util.Objects; + import javax.inject.Inject; public class TaskAlarmBootReceiver extends BroadcastReceiver { @@ -16,7 +18,7 @@ public class TaskAlarmBootReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent arg1) { - HabiticaApplication.getComponent().inject(this); + Objects.requireNonNull(HabiticaApplication.Companion.getComponent()).inject(this); taskAlarmManager.scheduleAllSavedAlarms(); } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/TaskReceiver.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/TaskReceiver.kt index 2ac07ecba..00c8cc168 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/TaskReceiver.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/TaskReceiver.kt @@ -10,6 +10,7 @@ import android.media.RingtoneManager import android.os.Build import android.support.v4.app.NotificationCompat import com.habitrpg.android.habitica.HabiticaApplication +import com.habitrpg.android.habitica.HabiticaBaseApplication import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.helpers.TaskAlarmManager import com.habitrpg.android.habitica.ui.activities.MainActivity @@ -22,7 +23,7 @@ class TaskReceiver : BroadcastReceiver() { lateinit var taskAlarmManager: TaskAlarmManager override fun onReceive(context: Context, intent: Intent) { - HabiticaApplication.getComponent().inject(this) + HabiticaBaseApplication.component?.inject(this) val extras = intent.extras if (extras != null) { val taskTitle = extras.getString(TaskAlarmManager.TASK_NAME_INTENT_KEY) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/AboutActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/AboutActivity.kt index 26316ccc8..17dc97e59 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/AboutActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/AboutActivity.kt @@ -61,8 +61,8 @@ class AboutActivity : BaseActivity() { tabLayout.setupWithViewPager(pager) } - override fun injectActivity(component: AppComponent) { - component.inject(this) + override fun injectActivity(component: AppComponent?) { + component?.inject(this) } override fun onOptionsItemSelected(item: MenuItem): Boolean { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/AddTaskWidgetActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/AddTaskWidgetActivity.java deleted file mode 100644 index 7c2541dfd..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/AddTaskWidgetActivity.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.habitrpg.android.habitica.ui.activities; - -import android.appwidget.AppWidgetManager; -import android.content.Intent; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.preference.PreferenceManager; - -import com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.models.tasks.Task; -import com.habitrpg.android.habitica.widget.AddTaskWidgetProvider; - -import butterknife.ButterKnife; -import butterknife.OnClick; - -public class AddTaskWidgetActivity extends AppCompatActivity { - - private int widgetId; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setResult(RESULT_CANCELED); - setContentView(R.layout.widget_configure_add_task); - ButterKnife.bind(this); - - Intent intent = getIntent(); - Bundle extras = intent.getExtras(); - if (extras != null) { - widgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); - } - - // If this activity was started with an intent without an app widget ID, - // finish with an error. - if (widgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { - finish(); - } - } - - @OnClick(R.id.add_habit_button) - public void addHabitSelected() { - finishWithSelection(Task.TYPE_HABIT); - } - - @OnClick(R.id.add_daily_button) - public void addDailySelected() { - finishWithSelection(Task.TYPE_DAILY); - } - - @OnClick(R.id.add_todo_button) - public void addToDoSelected() { - finishWithSelection(Task.TYPE_TODO); - } - - @OnClick(R.id.add_reward_button) - public void addRewardSelected() { - finishWithSelection(Task.TYPE_REWARD); - } - - private void finishWithSelection(String selectedTaskType) { - storeSelectedTaskType(selectedTaskType); - - Intent resultValue = new Intent(); - resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId); - setResult(RESULT_OK, resultValue); - finish(); - - Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE, null, this, AddTaskWidgetProvider.class); - intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[]{widgetId}); - sendBroadcast(intent); - } - - private void storeSelectedTaskType(String selectedTaskType) { - SharedPreferences.Editor preferences = PreferenceManager.getDefaultSharedPreferences(this).edit(); - preferences.putString("add_task_widget_" + widgetId, selectedTaskType); - preferences.apply(); - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/AddTaskWidgetActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/AddTaskWidgetActivity.kt new file mode 100644 index 000000000..82cae3099 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/AddTaskWidgetActivity.kt @@ -0,0 +1,81 @@ +package com.habitrpg.android.habitica.ui.activities + +import android.app.Activity +import android.appwidget.AppWidgetManager +import android.content.Intent +import android.os.Bundle +import android.support.v7.app.AppCompatActivity +import android.support.v7.preference.PreferenceManager +import android.widget.Button +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.models.tasks.Task +import com.habitrpg.android.habitica.ui.helpers.bindView +import com.habitrpg.android.habitica.widget.AddTaskWidgetProvider + +class AddTaskWidgetActivity : AppCompatActivity() { + + private var widgetId: Int = 0 + + private val addHabitButton: Button by bindView(R.id.add_habit_button) + private val addDailyButton: Button by bindView(R.id.add_daily_button) + private val addToDoButton: Button by bindView(R.id.add_todo_button) + private val addRewardButton: Button by bindView(R.id.add_reward_button) + + public override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setResult(Activity.RESULT_CANCELED) + setContentView(R.layout.widget_configure_add_task) + + val intent = intent + val extras = intent.extras + if (extras != null) { + widgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID) + } + + // If this activity was started with an intent without an app widget ID, + // finish with an error. + if (widgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { + finish() + } + + addHabitButton.setOnClickListener { addHabitSelected() } + addDailyButton.setOnClickListener { addDailySelected() } + addToDoButton.setOnClickListener { addToDoSelected() } + addRewardButton.setOnClickListener { addRewardSelected() } + } + + private fun addHabitSelected() { + finishWithSelection(Task.TYPE_HABIT) + } + + private fun addDailySelected() { + finishWithSelection(Task.TYPE_DAILY) + } + + private fun addToDoSelected() { + finishWithSelection(Task.TYPE_TODO) + } + + private fun addRewardSelected() { + finishWithSelection(Task.TYPE_REWARD) + } + + private fun finishWithSelection(selectedTaskType: String) { + storeSelectedTaskType(selectedTaskType) + + val resultValue = Intent() + resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId) + setResult(Activity.RESULT_OK, resultValue) + finish() + + val intent = Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE, null, this, AddTaskWidgetProvider::class.java) + intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, intArrayOf(widgetId)) + sendBroadcast(intent) + } + + private fun storeSelectedTaskType(selectedTaskType: String) { + val preferences = PreferenceManager.getDefaultSharedPreferences(this).edit() + preferences.putString("add_task_widget_$widgetId", selectedTaskType) + preferences.apply() + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/BaseActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/BaseActivity.java deleted file mode 100644 index 161fd99ba..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/BaseActivity.java +++ /dev/null @@ -1,125 +0,0 @@ -package com.habitrpg.android.habitica.ui.activities; - -import android.content.Intent; -import android.os.Build; -import android.os.Bundle; -import android.provider.Settings; -import android.support.v7.app.ActionBar; -import android.support.v7.app.AlertDialog; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.Toolbar; - -import com.habitrpg.android.habitica.HabiticaApplication; -import com.habitrpg.android.habitica.HabiticaBaseApplication; -import com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.components.AppComponent; -import com.habitrpg.android.habitica.events.ShowConnectionProblemEvent; - -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; - -import butterknife.ButterKnife; -import io.reactivex.disposables.CompositeDisposable; - -public abstract class BaseActivity extends AppCompatActivity { - - private boolean destroyed; - - protected abstract int getLayoutResId(); - - public boolean isDestroyed() { - return destroyed; - } - - protected CompositeDisposable compositeSubscription; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getHabiticaApplication(); - injectActivity(HabiticaBaseApplication.getComponent()); - setContentView(getLayoutResId()); - ButterKnife.bind(this); - compositeSubscription = new CompositeDisposable(); - } - - @Override - protected void onStart() { - super.onStart(); - EventBus.getDefault().register(this); - } - - @Override - protected void onStop() { - if (EventBus.getDefault().isRegistered(this)) { - EventBus.getDefault().unregister(this); - } - super.onStop(); - } - - protected abstract void injectActivity(AppComponent component); - - protected void setupToolbar(Toolbar toolbar) { - if (toolbar != null) { - setSupportActionBar(toolbar); - - ActionBar actionBar = getSupportActionBar(); - if (actionBar != null) { - actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setDisplayShowHomeEnabled(true); - actionBar.setDisplayShowTitleEnabled(true); - actionBar.setDisplayUseLogoEnabled(false); - actionBar.setHomeButtonEnabled(true); - } - } - } - - @Override - protected void onDestroy() { - destroyed = true; - - if (compositeSubscription != null && !compositeSubscription.isDisposed()) { - compositeSubscription.dispose(); - } - super.onDestroy(); - } - - public HabiticaApplication getHabiticaApplication() { - return (HabiticaApplication) getApplication(); - } - - //Check for "Don't keep Activities" Developer setting - //TODO: Make this check obsolete. - boolean isAlwaysFinishActivitiesOptionEnabled() { - int alwaysFinishActivitiesInt = 0; - if (Build.VERSION.SDK_INT >= 17) { - alwaysFinishActivitiesInt = Settings.System.getInt(getApplicationContext().getContentResolver(), Settings.Global.ALWAYS_FINISH_ACTIVITIES, 0); - } else { - alwaysFinishActivitiesInt = Settings.System.getInt(getApplicationContext().getContentResolver(), Settings.System.ALWAYS_FINISH_ACTIVITIES, 0); - } - - return alwaysFinishActivitiesInt == 1; - } - - void showDeveloperOptionsScreen() { - Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS); - intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); - startActivity(intent); - } - - @Subscribe - public void onEvent(ShowConnectionProblemEvent event) { - - AlertDialog.Builder builder = new AlertDialog.Builder(this) - .setTitle(event.title) - .setMessage(event.message) - .setNeutralButton(android.R.string.ok, (dialog, which) -> { - }); - - if (!event.title.isEmpty()) { - builder.setIcon(R.drawable.ic_warning_black); - } - - builder.show(); - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/BaseActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/BaseActivity.kt new file mode 100644 index 000000000..0dfd6eee3 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/BaseActivity.kt @@ -0,0 +1,113 @@ +package com.habitrpg.android.habitica.ui.activities + +import android.content.Intent +import android.os.Build +import android.os.Bundle +import android.provider.Settings +import android.support.v7.app.AlertDialog +import android.support.v7.app.AppCompatActivity +import android.support.v7.widget.Toolbar +import com.habitrpg.android.habitica.HabiticaApplication +import com.habitrpg.android.habitica.HabiticaBaseApplication +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.components.AppComponent +import com.habitrpg.android.habitica.events.ShowConnectionProblemEvent +import io.reactivex.disposables.CompositeDisposable +import org.greenrobot.eventbus.EventBus +import org.greenrobot.eventbus.Subscribe + +abstract class BaseActivity : AppCompatActivity() { + + private var destroyed: Boolean = false + + protected abstract fun getLayoutResId(): Int + + protected var compositeSubscription = CompositeDisposable() + + private val habiticaApplication: HabiticaApplication + get() = application as HabiticaApplication + + //Check for "Don't keep Activities" Developer setting + //TODO: Make this check obsolete. + internal val isAlwaysFinishActivitiesOptionEnabled: Boolean + get() { + var alwaysFinishActivitiesInt = 0 + alwaysFinishActivitiesInt = if (Build.VERSION.SDK_INT >= 17) { + Settings.System.getInt(applicationContext.contentResolver, Settings.Global.ALWAYS_FINISH_ACTIVITIES, 0) + } else { + Settings.System.getInt(applicationContext.contentResolver, Settings.System.ALWAYS_FINISH_ACTIVITIES, 0) + } + + return alwaysFinishActivitiesInt == 1 + } + + override fun isDestroyed(): Boolean { + return destroyed + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + habiticaApplication + injectActivity(HabiticaBaseApplication.component) + setContentView(getLayoutResId()) + compositeSubscription = CompositeDisposable() + } + + override fun onStart() { + super.onStart() + EventBus.getDefault().register(this) + } + + override fun onStop() { + if (EventBus.getDefault().isRegistered(this)) { + EventBus.getDefault().unregister(this) + } + super.onStop() + } + + protected abstract fun injectActivity(component: AppComponent?) + + protected fun setupToolbar(toolbar: Toolbar?) { + if (toolbar != null) { + setSupportActionBar(toolbar) + + val actionBar = supportActionBar + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(true) + actionBar.setDisplayShowHomeEnabled(true) + actionBar.setDisplayShowTitleEnabled(true) + actionBar.setDisplayUseLogoEnabled(false) + actionBar.setHomeButtonEnabled(true) + } + } + } + + override fun onDestroy() { + destroyed = true + + if (compositeSubscription != null && !compositeSubscription!!.isDisposed) { + compositeSubscription!!.dispose() + } + super.onDestroy() + } + + internal fun showDeveloperOptionsScreen() { + val intent = Intent(android.provider.Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS) + intent.flags = Intent.FLAG_ACTIVITY_NO_HISTORY + startActivity(intent) + } + + @Subscribe + fun onEvent(event: ShowConnectionProblemEvent) { + val builder = AlertDialog.Builder(this) + .setTitle(event.title) + .setMessage(event.message) + .setNeutralButton(android.R.string.ok) { _, _ -> } + + if (!event.title.isEmpty()) { + builder.setIcon(R.drawable.ic_warning_black) + } + + builder.show() + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ChallengeDetailActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ChallengeDetailActivity.java deleted file mode 100644 index a5c157f21..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ChallengeDetailActivity.java +++ /dev/null @@ -1,433 +0,0 @@ -package com.habitrpg.android.habitica.ui.activities; - -import android.app.AlertDialog; -import android.content.Intent; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.design.widget.TabLayout; -import android.support.v4.app.FragmentTransaction; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.Toolbar; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.TextView; - -import com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.components.AppComponent; -import com.habitrpg.android.habitica.data.ChallengeRepository; -import com.habitrpg.android.habitica.data.UserRepository; -import com.habitrpg.android.habitica.events.HabitScoreEvent; -import com.habitrpg.android.habitica.events.TaskUpdatedEvent; -import com.habitrpg.android.habitica.events.commands.BuyRewardCommand; -import com.habitrpg.android.habitica.events.commands.ChecklistCheckedCommand; -import com.habitrpg.android.habitica.events.commands.TaskCheckedCommand; -import com.habitrpg.android.habitica.helpers.RxErrorHandler; -import com.habitrpg.android.habitica.interactors.BuyRewardUseCase; -import com.habitrpg.android.habitica.interactors.ChecklistCheckUseCase; -import com.habitrpg.android.habitica.interactors.DailyCheckUseCase; -import com.habitrpg.android.habitica.interactors.DisplayItemDropUseCase; -import com.habitrpg.android.habitica.interactors.HabitScoreUseCase; -import com.habitrpg.android.habitica.interactors.NotifyUserUseCase; -import com.habitrpg.android.habitica.interactors.TodoCheckUseCase; -import com.habitrpg.android.habitica.models.LeaveChallengeBody; -import com.habitrpg.android.habitica.models.responses.TaskScoringResult; -import com.habitrpg.android.habitica.models.social.Challenge; -import com.habitrpg.android.habitica.models.tasks.Task; -import com.habitrpg.android.habitica.models.user.User; -import com.habitrpg.android.habitica.modules.AppModule; -import com.habitrpg.android.habitica.ui.fragments.social.challenges.ChallengeDetailDialogHolder; -import com.habitrpg.android.habitica.ui.fragments.social.challenges.ChallengeTasksRecyclerViewFragment; -import com.habitrpg.android.habitica.ui.helpers.MarkdownParser; -import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper; -import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar; -import com.habitrpg.android.habitica.utils.Action1; - -import net.pherth.android.emoji_library.EmojiParser; -import net.pherth.android.emoji_library.EmojiTextView; - -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import javax.inject.Inject; -import javax.inject.Named; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; - - -public class ChallengeDetailActivity extends BaseActivity { - - public static String CHALLENGE_ID = "CHALLENGE_ID"; - - @BindView(R.id.detail_tabs) - TabLayout detail_tabs; - @BindView(R.id.toolbar) - Toolbar toolbar; - - @Inject - ChallengeRepository challengeRepository; - @Inject - @Named(AppModule.NAMED_USER_ID) - String userId; - @Inject - UserRepository userRepository; - - @BindView(R.id.floating_menu_wrapper) - FrameLayout floatingMenuWrapper; - - // region UseCases - - @Inject - HabitScoreUseCase habitScoreUseCase; - - @Inject - DailyCheckUseCase dailyCheckUseCase; - - @Inject - TodoCheckUseCase todoCheckUseCase; - - @Inject - BuyRewardUseCase buyRewardUseCase; - - @Inject - ChecklistCheckUseCase checklistCheckUseCase; - - @Inject - DisplayItemDropUseCase displayItemDropUseCase; - - @Inject - NotifyUserUseCase notifyUserUseCase; - - // endregion - - @Nullable - private Challenge challenge; - private User user; - - @Override - protected int getLayoutResId() { - return R.layout.activity_challenge_detail; - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.menu_challenge_details, menu); - - if(challenge != null && challenge.leaderId != null && !challenge.leaderId.equals(userId)){ - menu.setGroupVisible(R.id.challenge_edit_action_group, false); - } - - return true; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setupToolbar(toolbar); - - if (getSupportActionBar() != null) { - getSupportActionBar().setTitle(R.string.challenge_details); - } - detail_tabs.setVisibility(View.GONE); - - Bundle extras = getIntent().getExtras(); - - String challengeId = extras.getString(CHALLENGE_ID); - - List fullList = new ArrayList<>(); - - userRepository.getUser(userId).firstElement().subscribe(user -> { - ChallengeDetailActivity.this.user = user; - createTaskRecyclerFragment(fullList); - }, RxErrorHandler.handleEmptyError()); - - if (challengeId != null) { - challengeRepository.getChallengeTasks(challengeId) - .firstElement() - .subscribe(taskList -> { - ArrayList resultList = new ArrayList<>(); - - ArrayList todos = new ArrayList<>(); - ArrayList habits = new ArrayList<>(); - ArrayList dailies = new ArrayList<>(); - ArrayList rewards = new ArrayList<>(); - - for (Map.Entry entry : taskList.tasks.entrySet()) { - switch (entry.getValue().getType()) { - case Task.TYPE_TODO: - todos.add(entry.getValue()); - break; - case Task.TYPE_HABIT: - - habits.add(entry.getValue()); - break; - case Task.TYPE_DAILY: - - dailies.add(entry.getValue()); - break; - case Task.TYPE_REWARD: - - rewards.add(entry.getValue()); - break; - } - } - - - if (!habits.isEmpty()) { - Task dividerTask = new Task(); - dividerTask.setId("divhabits"); - dividerTask.setType("divider"); - dividerTask.setText("Challenge Habits"); - - resultList.add(dividerTask); - resultList.addAll(habits); - } - - - if (!dailies.isEmpty()) { - Task dividerTask = new Task(); - dividerTask.setId("divdailies"); - dividerTask.setType("divider"); - dividerTask.setText("Challenge Dailies"); - - resultList.add(dividerTask); - resultList.addAll(dailies); - } - - - if (!todos.isEmpty()) { - Task dividerTask = new Task(); - dividerTask.setId("divtodos"); - dividerTask.setType("divider"); - dividerTask.setText("Challenge To-Dos"); - - resultList.add(dividerTask); - resultList.addAll(todos); - } - - if (!rewards.isEmpty()) { - Task dividerTask = new Task(); - dividerTask.setId("divrewards"); - dividerTask.setType("divider"); - dividerTask.setText("Challenge Rewards"); - - resultList.add(dividerTask); - resultList.addAll(rewards); - } - - - fullList.addAll(resultList); - }, RxErrorHandler.handleEmptyError()); - } - - if (challengeId != null) { - challengeRepository.getChallenge(challengeId).subscribe(challenge -> { - ChallengeDetailActivity.this.challenge = challenge; - ChallengeViewHolder challengeViewHolder = new ChallengeViewHolder(findViewById(R.id.challenge_header)); - challengeViewHolder.bind(challenge); - }, RxErrorHandler.handleEmptyError()); - } - } - - private void createTaskRecyclerFragment(List fullList) { - ChallengeTasksRecyclerViewFragment fragment = ChallengeTasksRecyclerViewFragment.newInstance(user, fullList); - - if (getSupportFragmentManager().getFragments() == null) { - getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, fragment).commitAllowingStateLoss(); - } else { - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out, android.R.anim.fade_in, android.R.anim.fade_out); - transaction.replace(R.id.fragment_container, fragment).addToBackStack(null).commitAllowingStateLoss(); - } - } - - @Override - protected void onDestroy() { - challengeRepository.close(); - userRepository.close(); - super.onDestroy(); - } - - protected void injectActivity(AppComponent component) { - component.inject(this); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_edit: - openChallengeEditActivity(); - return true; - case R.id.action_leave: - showChallengeLeaveDialog(); - return true; - } - return super.onOptionsItemSelected(item); - } - - private void openChallengeEditActivity(){ - Intent intent = new Intent(this, CreateChallengeActivity.class); - if (challenge != null) { - intent.putExtra(CreateChallengeActivity.CHALLENGE_ID_KEY, challenge.id); - } - - startActivity(intent); - - } - - private void showChallengeLeaveDialog(){ - new AlertDialog.Builder(this) - .setTitle(this.getString(R.string.challenge_leave_title)) - .setMessage(this.getString(R.string.challenge_leave_text, challenge != null ? challenge.name : "")) - .setPositiveButton(this.getString(R.string.yes), (dialog, which) -> { - dialog.dismiss(); - - showRemoveTasksDialog(keepTasks -> this.challengeRepository.leaveChallenge(challenge, new LeaveChallengeBody(keepTasks)) - .subscribe(aVoid -> finish(), RxErrorHandler.handleEmptyError())); - }) - .setNegativeButton(this.getString(R.string.no), (dialog, which) -> dialog.dismiss()).show(); - } - - // refactor as an UseCase later - see ChallengeDetailDialogHolder - private void showRemoveTasksDialog(Action1 callback){ - new AlertDialog.Builder(this) - .setTitle(this.getString(R.string.challenge_remove_tasks_title)) - .setMessage(this.getString(R.string.challenge_remove_tasks_text)) - .setPositiveButton(this.getString(R.string.remove_tasks), (dialog, which) -> { - callback.call("remove-all"); - dialog.dismiss(); - }) - .setNegativeButton(this.getString(R.string.keep_tasks), (dialog, which) -> { - callback.call("keep-all"); - dialog.dismiss(); - }).show(); - } - - @Override - public boolean onSupportNavigateUp() { - finish(); - return true; - } - - @Override - public void onBackPressed() { - finish(); - } - - class ChallengeViewHolder extends RecyclerView.ViewHolder { - @BindView(R.id.challenge_name) - EmojiTextView challengeName; - - @BindView(R.id.challenge_description) - EmojiTextView challengeDescription; - - @BindView(R.id.challenge_member_count) - TextView memberCountTextView; - - @BindView(R.id.gem_amount) - TextView gemPrizeTextView; - - private Challenge challenge; - - ChallengeViewHolder(View itemView) { - super(itemView); - - ButterKnife.bind(this, itemView); - - Drawable gemDrawable = new BitmapDrawable(itemView.getResources(), HabiticaIconsHelper.imageOfGem()); - gemPrizeTextView.setCompoundDrawablesWithIntrinsicBounds(gemDrawable, null, null, null); - } - - public void bind(Challenge challenge) { - this.challenge = challenge; - - if (challengeName != null) { - challengeName.setText(EmojiParser.parseEmojis(challenge.name)); - } - challengeDescription.setText(MarkdownParser.INSTANCE.parseMarkdown(challenge.description)); - - memberCountTextView.setText(String.valueOf(challenge.memberCount)); - - if (challenge.prize == 0) { - gemPrizeTextView.setVisibility(View.GONE); - } else { - gemPrizeTextView.setVisibility(View.VISIBLE); - gemPrizeTextView.setText(String.valueOf(challenge.prize)); - } - } - - @OnClick(R.id.btn_show_more) - void onShowMore() { - ChallengeDetailDialogHolder.Companion.showDialog(ChallengeDetailActivity.this, - ChallengeDetailActivity.this.challengeRepository, - challenge, - challenge1 -> ChallengeDetailActivity.this.onBackPressed()); - } - } - - @Subscribe - public void onEvent(TaskCheckedCommand event) { - switch (event.Task.getType()) { - case Task.TYPE_DAILY: { - dailyCheckUseCase.observable(new DailyCheckUseCase.RequestValues(user, event.Task, !event.Task.getCompleted())) - .subscribe(this::onTaskDataReceived, RxErrorHandler.handleEmptyError()); - } - break; - case Task.TYPE_TODO: { - todoCheckUseCase.observable(new TodoCheckUseCase.RequestValues(user, event.Task, !event.Task.getCompleted())) - .subscribe(this::onTaskDataReceived, RxErrorHandler.handleEmptyError()); - } - break; - } - } - - @Subscribe - public void onEvent(ChecklistCheckedCommand event) { - checklistCheckUseCase.observable(new ChecklistCheckUseCase.RequestValues(event.task.getId(), event.item.getId())) - .subscribe(res -> EventBus.getDefault().post(new TaskUpdatedEvent(event.task)), RxErrorHandler.handleEmptyError()); - } - - @Subscribe - public void onEvent(HabitScoreEvent event) { - habitScoreUseCase.observable(new HabitScoreUseCase.RequestValues(user, event.habit, event.Up)) - .subscribe(this::onTaskDataReceived, RxErrorHandler.handleEmptyError()); - } - - @Subscribe - public void onEvent(final BuyRewardCommand event) { - if (user.getStats().getGp() < event.Reward.getValue()) { - HabiticaSnackbar.Companion.showSnackbar(floatingMenuWrapper, getString(R.string.no_gold), HabiticaSnackbar.SnackbarDisplayType.FAILURE); - return; - } - - - if (event.Reward.getSpecialTag() == null || !event.Reward.getSpecialTag().equals("item")) { - - buyRewardUseCase.observable(new BuyRewardUseCase.RequestValues(user, event.Reward)) - .subscribe(res -> HabiticaSnackbar.Companion.showSnackbar(floatingMenuWrapper, getString(R.string.notification_purchase_reward), HabiticaSnackbar.SnackbarDisplayType.NORMAL), RxErrorHandler.handleEmptyError()); - } - - } - - public void onTaskDataReceived(TaskScoringResult data) { - if (user != null) { - notifyUserUseCase.observable(new NotifyUserUseCase.RequestValues(this, floatingMenuWrapper, - user, data.getExperienceDelta(), data.getHealthDelta(), data.getGoldDelta(), data.getManaDelta(), data.getQuestDamage(), data.getHasLeveledUp())); - } - - displayItemDropUseCase.observable(new DisplayItemDropUseCase.RequestValues(data, this, floatingMenuWrapper)) - .subscribe(aVoid -> {}, RxErrorHandler.handleEmptyError()); - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ChallengeDetailActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ChallengeDetailActivity.kt new file mode 100644 index 000000000..36b0fed99 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ChallengeDetailActivity.kt @@ -0,0 +1,397 @@ +package com.habitrpg.android.habitica.ui.activities + +import android.app.AlertDialog +import android.content.Intent +import android.graphics.drawable.BitmapDrawable +import android.os.Bundle +import android.support.design.widget.TabLayout +import android.support.v7.widget.RecyclerView +import android.support.v7.widget.Toolbar +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.widget.Button +import android.widget.FrameLayout +import android.widget.TextView +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.components.AppComponent +import com.habitrpg.android.habitica.data.ChallengeRepository +import com.habitrpg.android.habitica.data.UserRepository +import com.habitrpg.android.habitica.events.HabitScoreEvent +import com.habitrpg.android.habitica.events.TaskUpdatedEvent +import com.habitrpg.android.habitica.events.commands.BuyRewardCommand +import com.habitrpg.android.habitica.events.commands.ChecklistCheckedCommand +import com.habitrpg.android.habitica.events.commands.TaskCheckedCommand +import com.habitrpg.android.habitica.extensions.notNull +import com.habitrpg.android.habitica.helpers.RxErrorHandler +import com.habitrpg.android.habitica.interactors.* +import com.habitrpg.android.habitica.models.LeaveChallengeBody +import com.habitrpg.android.habitica.models.responses.TaskScoringResult +import com.habitrpg.android.habitica.models.social.Challenge +import com.habitrpg.android.habitica.models.tasks.Task +import com.habitrpg.android.habitica.models.user.User +import com.habitrpg.android.habitica.modules.AppModule +import com.habitrpg.android.habitica.ui.fragments.social.challenges.ChallengeDetailDialogHolder +import com.habitrpg.android.habitica.ui.fragments.social.challenges.ChallengeTasksRecyclerViewFragment +import com.habitrpg.android.habitica.ui.helpers.MarkdownParser +import com.habitrpg.android.habitica.ui.helpers.bindView +import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper +import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar +import com.habitrpg.android.habitica.utils.Action1 +import io.reactivex.functions.Consumer +import net.pherth.android.emoji_library.EmojiParser +import net.pherth.android.emoji_library.EmojiTextView +import org.greenrobot.eventbus.EventBus +import org.greenrobot.eventbus.Subscribe +import java.util.* +import javax.inject.Inject +import javax.inject.Named + + +class ChallengeDetailActivity : BaseActivity() { + + + @Inject + internal lateinit var challengeRepository: ChallengeRepository + @field:[Inject Named(AppModule.NAMED_USER_ID)] + internal lateinit var userId: String + @Inject + internal lateinit var userRepository: UserRepository + + private val floatingMenuWrapper: FrameLayout by bindView(R.id.floating_menu_wrapper) + private val detailTabs: TabLayout by bindView(R.id.detail_tabs) + private val toolbar: Toolbar by bindView(R.id.toolbar) + // region UseCases + + @Inject + internal lateinit var habitScoreUseCase: HabitScoreUseCase + + @Inject + internal lateinit var dailyCheckUseCase: DailyCheckUseCase + + @Inject + internal lateinit var todoCheckUseCase: TodoCheckUseCase + + @Inject + internal lateinit var buyRewardUseCase: BuyRewardUseCase + + @Inject + internal lateinit var checklistCheckUseCase: ChecklistCheckUseCase + + @Inject + internal lateinit var displayItemDropUseCase: DisplayItemDropUseCase + + @Inject + internal lateinit var notifyUserUseCase: NotifyUserUseCase + + // endregion + + private var challenge: Challenge? = null + private var user: User? = null + + override fun getLayoutResId(): Int { + return R.layout.activity_challenge_detail + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + val inflater = menuInflater + inflater.inflate(R.menu.menu_challenge_details, menu) + + if (challenge?.leaderId != userId) { + menu.setGroupVisible(R.id.challenge_edit_action_group, false) + } + + return true + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setupToolbar(toolbar) + + supportActionBar?.setTitle(R.string.challenge_details) + detailTabs.visibility = View.GONE + + val extras = intent.extras + + val challengeId = extras.getString(CHALLENGE_ID) + + val fullList = ArrayList() + + userRepository.getUser(userId).firstElement().subscribe(Consumer { user -> + this@ChallengeDetailActivity.user = user + createTaskRecyclerFragment(fullList) + }, RxErrorHandler.handleEmptyError()) + + if (challengeId != null) { + challengeRepository.getChallengeTasks(challengeId) + .firstElement() + .subscribe(Consumer { taskList -> + val resultList = ArrayList() + + val todos = ArrayList() + val habits = ArrayList() + val dailies = ArrayList() + val rewards = ArrayList() + + for (entry in taskList.tasks.entries) { + when (entry.value.type) { + Task.TYPE_TODO -> todos.add(entry.value) + Task.TYPE_HABIT -> + + habits.add(entry.value) + Task.TYPE_DAILY -> + + dailies.add(entry.value) + Task.TYPE_REWARD -> + + rewards.add(entry.value) + } + } + + + if (!habits.isEmpty()) { + val dividerTask = Task() + dividerTask.id = "divhabits" + dividerTask.type = "divider" + dividerTask.text = "Challenge Habits" + + resultList.add(dividerTask) + resultList.addAll(habits) + } + + + if (!dailies.isEmpty()) { + val dividerTask = Task() + dividerTask.id = "divdailies" + dividerTask.type = "divider" + dividerTask.text = "Challenge Dailies" + + resultList.add(dividerTask) + resultList.addAll(dailies) + } + + + if (!todos.isEmpty()) { + val dividerTask = Task() + dividerTask.id = "divtodos" + dividerTask.type = "divider" + dividerTask.text = "Challenge To-Dos" + + resultList.add(dividerTask) + resultList.addAll(todos) + } + + if (!rewards.isEmpty()) { + val dividerTask = Task() + dividerTask.id = "divrewards" + dividerTask.type = "divider" + dividerTask.text = "Challenge Rewards" + + resultList.add(dividerTask) + resultList.addAll(rewards) + } + + + fullList.addAll(resultList) + }, RxErrorHandler.handleEmptyError()) + } + + if (challengeId != null) { + challengeRepository.getChallenge(challengeId).subscribe(Consumer { challenge -> + this@ChallengeDetailActivity.challenge = challenge + val challengeViewHolder = ChallengeViewHolder(findViewById(R.id.challenge_header)) + challengeViewHolder.bind(challenge) + }, RxErrorHandler.handleEmptyError()) + } + } + + private fun createTaskRecyclerFragment(fullList: List) { + val fragment = ChallengeTasksRecyclerViewFragment.newInstance(user, fullList) + + if (supportFragmentManager.fragments == null) { + supportFragmentManager.beginTransaction().add(R.id.fragment_container, fragment).commitAllowingStateLoss() + } else { + val transaction = supportFragmentManager.beginTransaction() + transaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out, android.R.anim.fade_in, android.R.anim.fade_out) + transaction.replace(R.id.fragment_container, fragment).addToBackStack(null).commitAllowingStateLoss() + } + } + + override fun onDestroy() { + challengeRepository.close() + userRepository.close() + super.onDestroy() + } + + override fun injectActivity(component: AppComponent?) { + component?.inject(this) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.action_edit -> { + openChallengeEditActivity() + return true + } + R.id.action_leave -> { + showChallengeLeaveDialog() + return true + } + } + return super.onOptionsItemSelected(item) + } + + private fun openChallengeEditActivity() { + val intent = Intent(this, CreateChallengeActivity::class.java) + intent.putExtra(CreateChallengeActivity.CHALLENGE_ID_KEY, challenge?.id) + + startActivity(intent) + + } + + private fun showChallengeLeaveDialog() { + AlertDialog.Builder(this) + .setTitle(this.getString(R.string.challenge_leave_title)) + .setMessage(this.getString(R.string.challenge_leave_text, challenge?.name ?: "")) + .setPositiveButton(this.getString(R.string.yes)) { dialog, _ -> + dialog.dismiss() + + showRemoveTasksDialog(object: Action1 { + override fun call(t: String) { + challengeRepository.leaveChallenge(challenge, LeaveChallengeBody(t)) + .subscribe(Consumer { finish() }, RxErrorHandler.handleEmptyError()) + } + }) + } + .setNegativeButton(this.getString(R.string.no)) { dialog, _ -> dialog.dismiss() }.show() + } + + // refactor as an UseCase later - see ChallengeDetailDialogHolder + private fun showRemoveTasksDialog(callback: Action1) { + AlertDialog.Builder(this) + .setTitle(this.getString(R.string.challenge_remove_tasks_title)) + .setMessage(this.getString(R.string.challenge_remove_tasks_text)) + .setPositiveButton(this.getString(R.string.remove_tasks)) { dialog, _ -> + callback.call("remove-all") + dialog.dismiss() + } + .setNegativeButton(this.getString(R.string.keep_tasks)) { dialog, _ -> + callback.call("keep-all") + dialog.dismiss() + }.show() + } + + override fun onSupportNavigateUp(): Boolean { + finish() + return true + } + + override fun onBackPressed() { + finish() + } + + internal inner class ChallengeViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + private val challengeName: EmojiTextView by bindView(itemView, R.id.challenge_name) + private val challengeDescription: EmojiTextView by bindView(itemView, R.id.challenge_description) + private val memberCountTextView: TextView by bindView(itemView, R.id.challenge_member_count) + private val gemPrizeTextView: TextView by bindView(itemView, R.id.gem_amount) + private val showMoreButton: Button by bindView(itemView, R.id.btn_show_more) + + private var challenge: Challenge? = null + + init { + showMoreButton.setOnClickListener { onShowMore() } + + val gemDrawable = BitmapDrawable(itemView.resources, HabiticaIconsHelper.imageOfGem()) + gemPrizeTextView.setCompoundDrawablesWithIntrinsicBounds(gemDrawable, null, null, null) + } + + fun bind(challenge: Challenge) { + this.challenge = challenge + + challengeName.text = EmojiParser.parseEmojis(challenge.name) + challengeDescription.text = MarkdownParser.parseMarkdown(challenge.description) + + memberCountTextView.text = challenge.memberCount.toString() + + if (challenge.prize == 0) { + gemPrizeTextView.visibility = View.GONE + } else { + gemPrizeTextView.visibility = View.VISIBLE + gemPrizeTextView.text = challenge.prize.toString() + } + } + + fun onShowMore() { + challenge.notNull { + ChallengeDetailDialogHolder.showDialog(this@ChallengeDetailActivity, + this@ChallengeDetailActivity.challengeRepository, + it, + object: Action1 { + override fun call(t: Challenge) { + onBackPressed() + } + }) + } + + } + } + + @Subscribe + fun onEvent(event: TaskCheckedCommand) { + when (event.Task.type) { + Task.TYPE_DAILY -> { + dailyCheckUseCase.observable(DailyCheckUseCase.RequestValues(user, event.Task, !event.Task.completed)) + .subscribe(Consumer { this.onTaskDataReceived(it) }, RxErrorHandler.handleEmptyError()) + } + Task.TYPE_TODO -> { + todoCheckUseCase.observable(TodoCheckUseCase.RequestValues(user, event.Task, !event.Task.completed)) + .subscribe(Consumer { this.onTaskDataReceived(it) }, RxErrorHandler.handleEmptyError()) + } + } + } + + @Subscribe + fun onEvent(event: ChecklistCheckedCommand) { + checklistCheckUseCase.observable(ChecklistCheckUseCase.RequestValues(event.task.id, event.item.id)) + .subscribe(Consumer { EventBus.getDefault().post(TaskUpdatedEvent(event.task)) }, RxErrorHandler.handleEmptyError()) + } + + @Subscribe + fun onEvent(event: HabitScoreEvent) { + habitScoreUseCase.observable(HabitScoreUseCase.RequestValues(user, event.habit, event.Up)) + .subscribe(Consumer { this.onTaskDataReceived(it) }, RxErrorHandler.handleEmptyError()) + } + + @Subscribe + fun onEvent(event: BuyRewardCommand) { + if ((user?.stats?.gp ?: 0.toDouble()) < event.Reward.value) { + HabiticaSnackbar.showSnackbar(floatingMenuWrapper, getString(R.string.no_gold), HabiticaSnackbar.SnackbarDisplayType.FAILURE) + return + } + + + if (event.Reward.specialTag == null || event.Reward.specialTag != "item") { + + buyRewardUseCase.observable(BuyRewardUseCase.RequestValues(user, event.Reward)) + .subscribe(Consumer { HabiticaSnackbar.showSnackbar(floatingMenuWrapper, getString(R.string.notification_purchase_reward), HabiticaSnackbar.SnackbarDisplayType.NORMAL) }, RxErrorHandler.handleEmptyError()) + } + + } + + private fun onTaskDataReceived(data: TaskScoringResult) { + if (user != null) { + notifyUserUseCase.observable(NotifyUserUseCase.RequestValues(this, floatingMenuWrapper, + user, data.experienceDelta, data.healthDelta, data.goldDelta, data.manaDelta, data.questDamage, data.hasLeveledUp)) + } + + displayItemDropUseCase.observable(DisplayItemDropUseCase.RequestValues(data, this, floatingMenuWrapper)) + .subscribe(Consumer { }, RxErrorHandler.handleEmptyError()) + } + + companion object { + + var CHALLENGE_ID = "CHALLENGE_ID" + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ClassSelectionActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ClassSelectionActivity.java deleted file mode 100644 index 72b03d4ca..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ClassSelectionActivity.java +++ /dev/null @@ -1,228 +0,0 @@ -package com.habitrpg.android.habitica.ui.activities; - -import android.app.ProgressDialog; -import android.content.Intent; -import android.os.Bundle; -import android.support.v7.app.AlertDialog; - -import com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.components.AppComponent; -import com.habitrpg.android.habitica.data.UserRepository; -import com.habitrpg.android.habitica.helpers.RxErrorHandler; -import com.habitrpg.android.habitica.models.user.Gear; -import com.habitrpg.android.habitica.models.user.Hair; -import com.habitrpg.android.habitica.models.user.Items; -import com.habitrpg.android.habitica.models.user.Outfit; -import com.habitrpg.android.habitica.models.user.Preferences; -import com.habitrpg.android.habitica.models.user.Stats; -import com.habitrpg.android.habitica.models.user.User; -import com.habitrpg.android.habitica.ui.AvatarView; - -import javax.inject.Inject; - -import butterknife.BindView; -import butterknife.OnClick; -import io.reactivex.functions.Consumer; - -public class ClassSelectionActivity extends BaseActivity implements Consumer { - - String currentClass; - Boolean isInitialSelection; - Boolean classWasUnset = false; - Boolean shouldFinish = false; - - @BindView(R.id.healerAvatarView) - AvatarView healerAvatarView; - @BindView(R.id.mageAvatarView) - AvatarView mageAvatarView; - @BindView(R.id.rogueAvatarView) - AvatarView rogueAvatarView; - @BindView(R.id.warriorAvatarView) - AvatarView warriorAvatarView; - - @Inject - UserRepository userRepository; - - ProgressDialog progressDialog; - - @Override - protected int getLayoutResId() { - return R.layout.activity_class_selection; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - Intent intent = getIntent(); - Bundle bundle = intent.getExtras(); - isInitialSelection = bundle.getBoolean("isInitialSelection"); - currentClass = bundle.getString("currentClass"); - - Preferences preferences = new Preferences(); - preferences.setHair(new Hair()); - preferences.setCostume(false); - preferences.setSize(bundle.getString("size")); - preferences.setSkin(bundle.getString("skin")); - preferences.setShirt(bundle.getString("shirt")); - preferences.getHair().setBangs(bundle.getInt("hairBangs")); - preferences.getHair().setBase(bundle.getInt("hairBase")); - preferences.getHair().setColor(bundle.getString("hairColor")); - preferences.getHair().setMustache(bundle.getInt("hairMustache")); - preferences.getHair().setBeard(bundle.getInt("hairBeard")); - - - Outfit healerOutfit = new Outfit(); - healerOutfit.setArmor("armor_healer_5"); - healerOutfit.setHead("head_healer_5"); - healerOutfit.setShield("shield_healer_5"); - healerOutfit.setWeapon("weapon_healer_6"); - User healer = this.makeUser(preferences, healerOutfit); - healerAvatarView.setAvatar(healer); - - Outfit mageOutfit = new Outfit(); - mageOutfit.setArmor("armor_wizard_5"); - mageOutfit.setHead("head_wizard_5"); - mageOutfit.setWeapon("weapon_wizard_6"); - User mage = this.makeUser(preferences, mageOutfit); - mageAvatarView.setAvatar(mage); - - Outfit rogueOutfit = new Outfit(); - rogueOutfit.setArmor("armor_rogue_5"); - rogueOutfit.setHead("head_rogue_5"); - rogueOutfit.setShield("shield_rogue_6"); - rogueOutfit.setWeapon("weapon_rogue_6"); - User rogue = this.makeUser(preferences, rogueOutfit); - rogueAvatarView.setAvatar(rogue); - - Outfit warriorOutfit = new Outfit(); - warriorOutfit.setArmor("armor_warrior_5"); - warriorOutfit.setHead("head_warrior_5"); - warriorOutfit.setShield("shield_warrior_5"); - warriorOutfit.setWeapon("weapon_warrior_6"); - User warrior = this.makeUser(preferences, warriorOutfit); - warriorAvatarView.setAvatar(warrior); - - if (!isInitialSelection) { - userRepository.changeClass() - .subscribe(user -> classWasUnset = true, RxErrorHandler.handleEmptyError()); - } - } - - @Override - protected void injectActivity(AppComponent component) { - component.inject(this); - } - - public User makeUser(Preferences preferences, Outfit outfit) { - User user = new User(); - user.setPreferences(preferences); - user.setItems(new Items()); - user.getItems().setGear(new Gear()); - user.getItems().getGear().setEquipped(outfit); - return user; - } - - @OnClick(R.id.healerWrapper) - public void healerSelected() { - displayConfirmationDialogForClass(getString(R.string.healer), Stats.HEALER); - } - - @OnClick(R.id.mageWrapper) - public void mageSelected() { - displayConfirmationDialogForClass(getString(R.string.mage), Stats.MAGE); - } - - @OnClick(R.id.rogueWrapper) - public void rogueSelected() { - displayConfirmationDialogForClass(getString(R.string.rogue), Stats.ROGUE); - } - - @OnClick(R.id.warriorWrapper) - public void warriorSelected() { - displayConfirmationDialogForClass(getString(R.string.warrior), Stats.WARRIOR); - } - - @OnClick(R.id.optOutWrapper) - public void optOutSelected() { - if (!this.isInitialSelection && !this.classWasUnset) { - return; - } - AlertDialog alert = new AlertDialog.Builder(this) - .setTitle(getString(R.string.opt_out_confirmation)) - .setNegativeButton(getString(R.string.dialog_go_back), (dialog, which) -> { - dialog.dismiss(); - }) - .setPositiveButton(getString(R.string.opt_out_class), (dialog, which) -> { - optOutOfClasses(); - }).create(); - alert.show(); - } - - private void displayConfirmationDialogForClass(String className, String classIdentifier) { - - if (!this.isInitialSelection && !this.classWasUnset) { - AlertDialog.Builder builder = new AlertDialog.Builder(this) - .setTitle(getString(R.string.change_class_confirmation)) - .setMessage(getString(R.string.change_class_equipment_warning, currentClass)) - .setNegativeButton(getString(R.string.dialog_go_back), (dialog, which) -> { - dialog.dismiss(); - }) - .setPositiveButton(getString(R.string.choose_class), (dialog, which) -> { - selectClass(classIdentifier); - displayClassChanged(className); - }); - AlertDialog alert = builder.create(); - alert.show(); - } else { - AlertDialog.Builder builder = new AlertDialog.Builder(this) - .setTitle(getString(R.string.class_confirmation, className)) - .setNegativeButton(getString(R.string.dialog_go_back), (dialog, which) -> { - dialog.dismiss(); - }) - .setPositiveButton(getString(R.string.choose_class), (dialog, which) -> { - selectClass(classIdentifier); - }); - AlertDialog alert = builder.create(); - alert.show(); - } - } - - private void displayClassChanged(String newClassName) { - AlertDialog.Builder changeConfirmedBuilder = new AlertDialog.Builder(this) - .setTitle(getString(R.string.class_changed, newClassName)) - .setMessage(getString(R.string.class_changed_description)) - .setPositiveButton(getString(R.string.complete_tutorial), (dialog, which) -> { - dialog.dismiss(); - }); - AlertDialog changeDoneAlert = changeConfirmedBuilder.create(); - changeDoneAlert.show(); - } - - private void optOutOfClasses() { - shouldFinish = true; - this.displayProgressDialog(); - userRepository.disableClasses().subscribe(this, RxErrorHandler.handleEmptyError()); - } - - private void selectClass(String selectedClass) { - shouldFinish = true; - this.displayProgressDialog(); - userRepository.changeClass(selectedClass).subscribe(this, RxErrorHandler.handleEmptyError()); - } - - private void displayProgressDialog() { - progressDialog = ProgressDialog.show(this, getString(R.string.changing_class_progress), null, true); - } - - @Override - public void accept(User user) { - if (shouldFinish) { - if (progressDialog != null) { - progressDialog.dismiss(); - } - setResult(MainActivity.SELECT_CLASS_RESULT); - finish(); - } - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ClassSelectionActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ClassSelectionActivity.kt new file mode 100644 index 000000000..d00e6857a --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ClassSelectionActivity.kt @@ -0,0 +1,202 @@ +package com.habitrpg.android.habitica.ui.activities + +import android.app.ProgressDialog +import android.os.Bundle +import android.support.v7.app.AlertDialog +import android.view.View +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.components.AppComponent +import com.habitrpg.android.habitica.data.UserRepository +import com.habitrpg.android.habitica.helpers.RxErrorHandler +import com.habitrpg.android.habitica.models.user.* +import com.habitrpg.android.habitica.ui.AvatarView +import com.habitrpg.android.habitica.ui.helpers.bindView +import io.reactivex.functions.Consumer +import javax.inject.Inject + +class ClassSelectionActivity : BaseActivity(), Consumer { + + private var currentClass: String? = null + private var isInitialSelection: Boolean = false + private var classWasUnset: Boolean? = false + private var shouldFinish: Boolean? = false + + internal val healerAvatarView: AvatarView by bindView(R.id.healerAvatarView) + private val healerWrapper: View by bindView(R.id.healerWrapper) + internal val mageAvatarView: AvatarView by bindView(R.id.mageAvatarView) + private val mageWrapper: View by bindView(R.id.mageWrapper) + internal val rogueAvatarView: AvatarView by bindView(R.id.rogueAvatarView) + private val rogueWrapper: View by bindView(R.id.rogueWrapper) + internal val warriorAvatarView: AvatarView by bindView(R.id.warriorAvatarView) + private val warriorWrapper: View by bindView(R.id.warriorWrapper) + private val optOutWrapper: View by bindView(R.id.optOutWrapper) + + @Inject + lateinit var userRepository: UserRepository + + private var progressDialog: ProgressDialog? = null + + override fun getLayoutResId(): Int { + return R.layout.activity_class_selection + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val intent = intent + val bundle = intent.extras + isInitialSelection = bundle?.getBoolean("isInitialSelection") ?: false + currentClass = bundle.getString("currentClass") + + val preferences = Preferences() + preferences.setHair(Hair()) + preferences.costume = false + preferences.setSize(bundle.getString("size")!!) + preferences.setSkin(bundle.getString("skin")!!) + preferences.setShirt(bundle.getString("shirt")!!) + preferences.hair?.bangs = bundle.getInt("hairBangs") + preferences.hair?.base = bundle.getInt("hairBase") + preferences.hair?.color = bundle.getString("hairColor") + preferences.hair?.mustache = bundle.getInt("hairMustache") + preferences.hair?.beard = bundle.getInt("hairBeard") + + + val healerOutfit = Outfit() + healerOutfit.armor = "armor_healer_5" + healerOutfit.head = "head_healer_5" + healerOutfit.shield = "shield_healer_5" + healerOutfit.weapon = "weapon_healer_6" + val healer = this.makeUser(preferences, healerOutfit) + healerAvatarView.setAvatar(healer) + + val mageOutfit = Outfit() + mageOutfit.armor = "armor_wizard_5" + mageOutfit.head = "head_wizard_5" + mageOutfit.weapon = "weapon_wizard_6" + val mage = this.makeUser(preferences, mageOutfit) + mageAvatarView.setAvatar(mage) + + val rogueOutfit = Outfit() + rogueOutfit.armor = "armor_rogue_5" + rogueOutfit.head = "head_rogue_5" + rogueOutfit.shield = "shield_rogue_6" + rogueOutfit.weapon = "weapon_rogue_6" + val rogue = this.makeUser(preferences, rogueOutfit) + rogueAvatarView.setAvatar(rogue) + + val warriorOutfit = Outfit() + warriorOutfit.armor = "armor_warrior_5" + warriorOutfit.head = "head_warrior_5" + warriorOutfit.shield = "shield_warrior_5" + warriorOutfit.weapon = "weapon_warrior_6" + val warrior = this.makeUser(preferences, warriorOutfit) + warriorAvatarView.setAvatar(warrior) + + if (!isInitialSelection) { + userRepository.changeClass() + .subscribe(Consumer { classWasUnset = true }, RxErrorHandler.handleEmptyError()) + } + + healerWrapper.setOnClickListener { healerSelected() } + mageWrapper.setOnClickListener { mageSelected() } + rogueWrapper.setOnClickListener { rogueSelected() } + warriorWrapper.setOnClickListener { warriorSelected() } + optOutWrapper.setOnClickListener { optOutSelected() } + } + + override fun injectActivity(component: AppComponent?) { + component?.inject(this) + } + + private fun makeUser(preferences: Preferences, outfit: Outfit): User { + val user = User() + user.preferences = preferences + user.items = Items() + user.items.gear = Gear() + user.items.gear.equipped = outfit + return user + } + + fun healerSelected() { + displayConfirmationDialogForClass(getString(R.string.healer), Stats.HEALER) + } + + fun mageSelected() { + displayConfirmationDialogForClass(getString(R.string.mage), Stats.MAGE) + } + + fun rogueSelected() { + displayConfirmationDialogForClass(getString(R.string.rogue), Stats.ROGUE) + } + + fun warriorSelected() { + displayConfirmationDialogForClass(getString(R.string.warrior), Stats.WARRIOR) + } + + fun optOutSelected() { + if (!this.isInitialSelection && this.classWasUnset == false) { + return + } + val alert = AlertDialog.Builder(this) + .setTitle(getString(R.string.opt_out_confirmation)) + .setNegativeButton(getString(R.string.dialog_go_back)) { dialog, _ -> dialog.dismiss() } + .setPositiveButton(getString(R.string.opt_out_class)) { _, _ -> optOutOfClasses() }.create() + alert.show() + } + + private fun displayConfirmationDialogForClass(className: String, classIdentifier: String) { + + if (!this.isInitialSelection && this.classWasUnset == false) { + val builder = AlertDialog.Builder(this) + .setTitle(getString(R.string.change_class_confirmation)) + .setMessage(getString(R.string.change_class_equipment_warning, currentClass)) + .setNegativeButton(getString(R.string.dialog_go_back)) { dialog, _ -> dialog.dismiss() } + .setPositiveButton(getString(R.string.choose_class)) { _, _ -> + selectClass(classIdentifier) + displayClassChanged(className) + } + val alert = builder.create() + alert.show() + } else { + val builder = AlertDialog.Builder(this) + .setTitle(getString(R.string.class_confirmation, className)) + .setNegativeButton(getString(R.string.dialog_go_back)) { dialog, _ -> dialog.dismiss() } + .setPositiveButton(getString(R.string.choose_class)) { _, _ -> selectClass(classIdentifier) } + val alert = builder.create() + alert.show() + } + } + + private fun displayClassChanged(newClassName: String) { + val changeConfirmedBuilder = AlertDialog.Builder(this) + .setTitle(getString(R.string.class_changed, newClassName)) + .setMessage(getString(R.string.class_changed_description)) + .setPositiveButton(getString(R.string.complete_tutorial)) { dialog, _ -> dialog.dismiss() } + val changeDoneAlert = changeConfirmedBuilder.create() + changeDoneAlert.show() + } + + private fun optOutOfClasses() { + shouldFinish = true + this.displayProgressDialog() + userRepository.disableClasses().subscribe(this, RxErrorHandler.handleEmptyError()) + } + + private fun selectClass(selectedClass: String) { + shouldFinish = true + this.displayProgressDialog() + userRepository.changeClass(selectedClass).subscribe(this, RxErrorHandler.handleEmptyError()) + } + + private fun displayProgressDialog() { + progressDialog = ProgressDialog.show(this, getString(R.string.changing_class_progress), null, true) + } + + override fun accept(user: User) { + if (shouldFinish == true) { + progressDialog?.dismiss() + setResult(MainActivity.SELECT_CLASS_RESULT) + finish() + } + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/CreateChallengeActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/CreateChallengeActivity.java deleted file mode 100644 index 4ab9eab5c..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/CreateChallengeActivity.java +++ /dev/null @@ -1,589 +0,0 @@ -package com.habitrpg.android.habitica.ui.activities; - -import android.app.ProgressDialog; -import android.content.Context; -import android.content.Intent; -import android.content.res.Resources; -import android.graphics.drawable.ColorDrawable; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.design.widget.TextInputLayout; -import android.support.v4.content.ContextCompat; -import android.support.v7.app.ActionBar; -import android.support.v7.app.AlertDialog; -import android.support.v7.widget.AppCompatCheckedTextView; -import android.support.v7.widget.AppCompatTextView; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.Spinner; -import android.widget.TextView; - -import com.github.underscore.U; -import com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.components.AppComponent; -import com.habitrpg.android.habitica.data.ChallengeRepository; -import com.habitrpg.android.habitica.data.SocialRepository; -import com.habitrpg.android.habitica.data.UserRepository; -import com.habitrpg.android.habitica.events.TaskTappedEvent; -import com.habitrpg.android.habitica.helpers.RxErrorHandler; -import com.habitrpg.android.habitica.models.social.Challenge; -import com.habitrpg.android.habitica.models.social.Group; -import com.habitrpg.android.habitica.models.tasks.Task; -import com.habitrpg.android.habitica.models.user.User; -import com.habitrpg.android.habitica.modules.AppModule; -import com.habitrpg.android.habitica.ui.adapter.social.challenges.ChallengeTasksRecyclerViewAdapter; -import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper; - -import org.greenrobot.eventbus.Subscribe; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.UUID; - -import javax.inject.Inject; -import javax.inject.Named; - -import butterknife.BindView; -import butterknife.OnClick; -import io.reactivex.Flowable; - -public class CreateChallengeActivity extends BaseActivity { - public static final String CHALLENGE_ID_KEY = "challengeId"; - - @BindView(R.id.create_challenge_title_input_layout) - TextInputLayout createChallengeTitleInputLayout; - - @BindView(R.id.create_challenge_title) - EditText createChallengeTitle; - - @BindView(R.id.create_challenge_description_input_layout) - TextInputLayout createChallengeDescriptionInputLayout; - - @BindView(R.id.create_challenge_description) - EditText createChallengeDescription; - - @BindView(R.id.create_challenge_prize) - EditText createChallengePrize; - - - @BindView(R.id.create_challenge_tag_input_layout) - TextInputLayout createChallengeTagInputLayout; - - @BindView(R.id.create_challenge_tag) - EditText createChallengeTag; - - @BindView(R.id.create_challenge_gem_error) - TextView createChallengeGemError; - - @BindView(R.id.create_challenge_task_error) - TextView createChallengeTaskError; - - @BindView(R.id.challenge_location_spinner) - Spinner challengeLocationSpinner; - - @BindView(R.id.challenge_add_gem_btn) - Button challengeAddGemBtn; - - @BindView(R.id.challenge_remove_gem_btn) - Button challengeRemoveGemBtn; - - @BindView(R.id.create_challenge_task_list) - RecyclerView createChallengeTaskList; - - @BindView(R.id.gem_icon) - ImageView gemIconView; - - @Inject - ChallengeRepository challengeRepository; - @Inject - SocialRepository socialRepository; - @Inject - UserRepository userRepository; - @Inject - @Named(AppModule.NAMED_USER_ID) - String userId; - - private ChallengeTasksRecyclerViewAdapter challengeTasks; - - private GroupArrayAdapter locationAdapter; - private String challengeId; - private boolean editMode; - - private HashMap addedTasks = new HashMap<>(); - private HashMap updatedTasks = new HashMap<>(); - private HashMap removedTasks = new HashMap<>(); - - // Add {*} Items - Task addHabit; - Task addDaily; - Task addTodo; - Task addReward; - @Nullable - private User user; - - @Override - protected int getLayoutResId() { - return R.layout.activity_create_challenge; - } - - @Override - protected void injectActivity(AppComponent component) { - component.inject(this); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.menu_create_challenge, menu); - return true; - } - - private boolean savingInProgress = false; - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == R.id.action_save && !savingInProgress && validateAllFields()) { - savingInProgress = true; - ProgressDialog dialog = ProgressDialog.show(this, "", "Saving challenge data. Please wait...", true, false); - - Flowable observable; - - if (editMode) { - observable = updateChallenge(); - } else { - observable = createChallenge(); - } - - observable.subscribe(challenge -> { - dialog.dismiss(); - savingInProgress = false; - finish(); - }, throwable -> { - dialog.dismiss(); - savingInProgress = false; - RxErrorHandler.reportError(throwable); - }); - } else if(item.getItemId() == android.R.id.home){ - finish(); - - return true; - } - - return super.onOptionsItemSelected(item); - } - - private boolean validateAllFields() { - ArrayList errorMessages = new ArrayList<>(); - - if (getEditTextString(createChallengeTitle).isEmpty()) { - String titleEmptyError = getString(R.string.challenge_create_error_title); - createChallengeTitleInputLayout.setError(titleEmptyError); - errorMessages.add(titleEmptyError); - } else { - createChallengeTitleInputLayout.setErrorEnabled(false); - } - - if (getEditTextString(createChallengeTag).isEmpty()) { - String tagEmptyError = getString(R.string.challenge_create_error_tag); - - createChallengeTagInputLayout.setError(tagEmptyError); - errorMessages.add(tagEmptyError); - } else { - createChallengeTagInputLayout.setErrorEnabled(false); - } - - String prizeError = checkPrizeAndMinimumForTavern(); - - if(!prizeError.isEmpty()){ - errorMessages.add(prizeError); - } - - // all "Add {*}"-Buttons are one task itself, so we need atleast more than 4 - if (challengeTasks.getTaskList().size() <= 4) { - createChallengeTaskError.setVisibility(View.VISIBLE); - errorMessages.add(getString(R.string.challenge_create_error_no_tasks)); - } else { - createChallengeTaskError.setVisibility(View.GONE); - } - - AlertDialog.Builder builder = new AlertDialog.Builder(this) - .setMessage(U.join(errorMessages, "\n")); - - AlertDialog alert = builder.create(); - alert.show(); - - return errorMessages.size() == 0; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - Intent intent = getIntent(); - Bundle bundle = intent.getExtras(); - - if (bundle != null) { - challengeId = bundle.getString(CHALLENGE_ID_KEY, null); - } - - fillControls(); - - if (challengeId != null) { - fillControlsByChallenge(); - } - - userRepository.getUser(userId).subscribe(user1 -> this.user = user1, RxErrorHandler.handleEmptyError()); - gemIconView.setImageBitmap(HabiticaIconsHelper.imageOfGem()); - } - - - @Override - public void onDestroy() { - socialRepository.close(); - challengeRepository.close(); - super.onDestroy(); - } - - @Subscribe - public void onEvent(TaskTappedEvent tappedEvent) { - openNewTaskActivity(null, tappedEvent.Task); - } - - @OnClick(R.id.challenge_add_gem_btn) - public void onAddGem() { - String stringValue = createChallengePrize.getText().toString(); - if (stringValue.length() == 0) { - stringValue = "0"; - } - int currentVal = Integer.parseInt(stringValue); - currentVal++; - - createChallengePrize.setText(String.valueOf(currentVal)); - - checkPrizeAndMinimumForTavern(); - } - - @OnClick(R.id.challenge_remove_gem_btn) - public void onRemoveGem() { - String stringValue = createChallengePrize.getText().toString(); - if (stringValue.length() == 0) { - stringValue = "0"; - } - int currentVal = Integer.parseInt(stringValue); - currentVal--; - - createChallengePrize.setText(String.valueOf(currentVal)); - - checkPrizeAndMinimumForTavern(); - } - - private String checkPrizeAndMinimumForTavern() { - String errorResult = ""; - - String inputValue = createChallengePrize.getText().toString(); - - if (inputValue.isEmpty()) { - inputValue = "0"; - } - - int currentVal = Integer.parseInt(inputValue); - - // 0 is Tavern - int selectedLocation = challengeLocationSpinner.getSelectedItemPosition(); - - double gemCount = 0; - if (user != null) { - gemCount = user.getGemCount(); - } - - if (selectedLocation == 0 && currentVal == 0) { - createChallengeGemError.setVisibility(View.VISIBLE); - String error = getString(R.string.challenge_create_error_tavern_one_gem); - createChallengeGemError.setText(error); - errorResult = error; - } else if (currentVal > gemCount) { - createChallengeGemError.setVisibility(View.VISIBLE); - String error = getString(R.string.challenge_create_error_enough_gems); - createChallengeGemError.setText(error); - errorResult = error; - } else { - createChallengeGemError.setVisibility(View.GONE); - } - - challengeRemoveGemBtn.setEnabled(currentVal != 0); - - return errorResult; - } - - private void fillControls() { - Resources resources = getResources(); - - ActionBar supportActionBar = getSupportActionBar(); - if (supportActionBar != null) { - supportActionBar.setDisplayShowHomeEnabled(true); - supportActionBar.setHomeAsUpIndicator(R.drawable.ic_close_white_24dp); - - supportActionBar.setTitle(""); - supportActionBar.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.brand_200))); - supportActionBar.setElevation(0); - } - - locationAdapter = new GroupArrayAdapter(this); - locationAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - socialRepository.getGroups("guild").subscribe(groups -> { - Group tavern = new Group(); - tavern.setId("00000000-0000-4000-A000-000000000000"); - tavern.setName(getString(R.string.sidebar_tavern)); - - locationAdapter.add(tavern); - - for (Group group : groups) { - locationAdapter.add(group); - } - }, RxErrorHandler.handleEmptyError()); - - challengeLocationSpinner.setAdapter(locationAdapter); - challengeLocationSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView adapterView, View view, int i, long l) { - checkPrizeAndMinimumForTavern(); - } - - @Override - public void onNothingSelected(AdapterView adapterView) { - - } - }); - createChallengePrize.setOnKeyListener((view, i, keyEvent) -> { - checkPrizeAndMinimumForTavern(); - - return false; - }); - - addHabit = createTask(ChallengeTasksRecyclerViewAdapter.TASK_TYPE_ADD_ITEM, resources.getString(R.string.add_habit)); - addDaily = createTask(ChallengeTasksRecyclerViewAdapter.TASK_TYPE_ADD_ITEM, resources.getString(R.string.add_daily)); - addTodo = createTask(ChallengeTasksRecyclerViewAdapter.TASK_TYPE_ADD_ITEM, resources.getString(R.string.add_todo)); - addReward = createTask(ChallengeTasksRecyclerViewAdapter.TASK_TYPE_ADD_ITEM, resources.getString(R.string.add_reward)); - - - ArrayList taskList = new ArrayList<>(); - taskList.add(addHabit); - taskList.add(addDaily); - taskList.add(addTodo); - taskList.add(addReward); - - challengeTasks = new ChallengeTasksRecyclerViewAdapter(null, 0, this, "", null, false, true); - challengeTasks.setTasks(taskList); - challengeTasks.addItemObservable().subscribe(t -> { - if (t.equals(addHabit)) { - openNewTaskActivity(Task.TYPE_HABIT, null); - } else if (t.equals(addDaily)) { - openNewTaskActivity(Task.TYPE_DAILY, null); - } else if (t.equals(addTodo)) { - openNewTaskActivity(Task.TYPE_TODO, null); - } else if (t.equals(addReward)) { - openNewTaskActivity(Task.TYPE_REWARD, null); - } - }, RxErrorHandler.handleEmptyError()); - - createChallengeTaskList.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener() { - @Override - public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { - // Stop only scrolling. - return rv.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING; - } - }); - createChallengeTaskList.setAdapter(challengeTasks); - createChallengeTaskList.setLayoutManager(new LinearLayoutManager(this)); - } - - private static Task createTask(String taskType, String taskName) { - Task t = new Task(); - - t.setId(UUID.randomUUID().toString()); - t.setType(taskType); - t.setText(taskName); - - if (taskType.equals(Task.TYPE_HABIT)) { - t.setUp(true); - t.setDown(false); - } - - return t; - } - - private void fillControlsByChallenge() { - challengeRepository.getChallenge(challengeId).subscribe(challenge -> { - - createChallengeTitle.setText(challenge.name); - createChallengeDescription.setText(challenge.description); - createChallengeTag.setText(challenge.shortName); - createChallengePrize.setText(String.valueOf(challenge.prize)); - - for (int i = 0; i < locationAdapter.getCount(); i++) { - Group group = locationAdapter.getItem(i); - - if (group != null && challenge.groupId.equals(group.getId())) { - challengeLocationSpinner.setSelection(i); - break; - } - } - - checkPrizeAndMinimumForTavern(); - - challengeRepository.getChallengeTasks(challengeId).subscribe(tasks -> { - for (Task task : tasks.tasks.values()) { - addOrUpdateTaskInList(task); - } - }, RxErrorHandler.handleEmptyError(), () -> { - // activate editMode to track taskChanges - editMode = true; - }); - }, RxErrorHandler.handleEmptyError()); - } - - private void openNewTaskActivity(String type, Task task) { - Bundle bundle = new Bundle(); - - if (task == null) { - bundle.putString(TaskFormActivity.TASK_TYPE_KEY, type); - } else { - bundle.putParcelable(TaskFormActivity.PARCELABLE_TASK, task); - } - - bundle.putBoolean(TaskFormActivity.SAVE_TO_DB, false); - bundle.putBoolean(TaskFormActivity.SET_IGNORE_FLAG, true); - bundle.putBoolean(TaskFormActivity.SHOW_TAG_SELECTION, false); - bundle.putBoolean(TaskFormActivity.SHOW_CHECKLIST, false); - - if (user != null && user.getPreferences() != null) { - boolean allocationMode = user.getPreferences().hasTaskBasedAllocation(); - - bundle.putString(TaskFormActivity.USER_ID_KEY, user.getId()); - bundle.putBoolean(TaskFormActivity.ALLOCATION_MODE_KEY, allocationMode); - } - - Intent intent = new Intent(this, TaskFormActivity.class); - intent.putExtras(bundle); - - startActivityForResult(intent, 1); - } - - private Challenge getChallengeData() { - Challenge c = new Challenge(); - - int locationPos = challengeLocationSpinner.getSelectedItemPosition(); - Group locationGroup = locationAdapter.getItem(locationPos); - - if (challengeId != null) { - c.id = challengeId; - } - - if (locationGroup != null) { - c.groupId = locationGroup.getId(); - } - c.name = createChallengeTitle.getText().toString(); - c.description = createChallengeDescription.getText().toString(); - c.shortName = createChallengeTag.getText().toString(); - c.prize = Integer.parseInt(createChallengePrize.getText().toString()); - - return c; - } - - private Flowable createChallenge() { - Challenge c = getChallengeData(); - - List taskList = challengeTasks.getTaskList(); - taskList.remove(addHabit); - taskList.remove(addDaily); - taskList.remove(addTodo); - taskList.remove(addReward); - - return challengeRepository.createChallenge(c, taskList); - } - - private Flowable updateChallenge() { - Challenge c = getChallengeData(); - - List taskList = challengeTasks.getTaskList(); - taskList.remove(addHabit); - taskList.remove(addDaily); - taskList.remove(addTodo); - taskList.remove(addReward); - - return challengeRepository.updateChallenge(c, taskList, new ArrayList<>(addedTasks.values()), - new ArrayList<>(updatedTasks.values()), - new ArrayList<>(removedTasks.keySet()) - ); - } - - private void addOrUpdateTaskInList(Task task) { - if (!challengeTasks.replaceTask(task)) { - Task taskAbove; - - switch (task.getType()) { - case Task.TYPE_HABIT: - taskAbove = addHabit; - break; - case Task.TYPE_DAILY: - taskAbove = addDaily; - break; - case Task.TYPE_TODO: - taskAbove = addTodo; - break; - default: - taskAbove = addReward; - break; - } - - challengeTasks.addTaskUnder(task, taskAbove); - - if (editMode) { - addedTasks.put(task.getId(), task); - } - } else { - // don't need to add the task to updatedTasks if its already been added right now - if (editMode && !addedTasks.containsKey(task.getId())) { - updatedTasks.put(task.getId(), task); - } - } - } - - private String getEditTextString(EditText editText) { - return editText.getText().toString(); - } - - private class GroupArrayAdapter extends ArrayAdapter { - GroupArrayAdapter(@NonNull Context context) { - super(context, android.R.layout.simple_spinner_item); - } - - @NonNull - @Override - public View getView(int position, View convertView, @NonNull ViewGroup parent) { - AppCompatTextView checkedTextView = (AppCompatTextView) super.getView(position, convertView, parent); - checkedTextView.setText(getItem(position).getName()); - return checkedTextView; - } - - @Override - public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { - AppCompatCheckedTextView checkedTextView = (AppCompatCheckedTextView) super.getDropDownView(position, convertView, parent); - checkedTextView.setText(getItem(position).getName()); - return checkedTextView; - } - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/CreateChallengeActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/CreateChallengeActivity.kt new file mode 100644 index 000000000..018f3931c --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/CreateChallengeActivity.kt @@ -0,0 +1,500 @@ +package com.habitrpg.android.habitica.ui.activities + +import android.app.ProgressDialog +import android.content.Context +import android.content.Intent +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.support.design.widget.TextInputLayout +import android.support.v4.content.ContextCompat +import android.support.v7.app.AlertDialog +import android.support.v7.widget.AppCompatCheckedTextView +import android.support.v7.widget.AppCompatTextView +import android.support.v7.widget.LinearLayoutManager +import android.support.v7.widget.RecyclerView +import android.view.* +import android.widget.* +import com.github.underscore.U +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.components.AppComponent +import com.habitrpg.android.habitica.data.ChallengeRepository +import com.habitrpg.android.habitica.data.SocialRepository +import com.habitrpg.android.habitica.data.UserRepository +import com.habitrpg.android.habitica.events.TaskTappedEvent +import com.habitrpg.android.habitica.extensions.notNull +import com.habitrpg.android.habitica.helpers.RxErrorHandler +import com.habitrpg.android.habitica.models.social.Challenge +import com.habitrpg.android.habitica.models.social.Group +import com.habitrpg.android.habitica.models.tasks.Task +import com.habitrpg.android.habitica.models.user.User +import com.habitrpg.android.habitica.modules.AppModule +import com.habitrpg.android.habitica.ui.adapter.social.challenges.ChallengeTasksRecyclerViewAdapter +import com.habitrpg.android.habitica.ui.helpers.bindView +import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper +import io.reactivex.Flowable +import io.reactivex.functions.Consumer +import org.greenrobot.eventbus.Subscribe +import java.util.* +import javax.inject.Inject +import javax.inject.Named + +class CreateChallengeActivity : BaseActivity() { + + private val createChallengeTitleInputLayout: TextInputLayout by bindView(R.id.create_challenge_title_input_layout) + private val createChallengeTitle: EditText by bindView(R.id.create_challenge_title) + private val createChallengeDescription: EditText by bindView(R.id.create_challenge_description) + private val createChallengePrize: EditText by bindView(R.id.create_challenge_prize) + private val createChallengeTagInputLayout: TextInputLayout by bindView(R.id.create_challenge_tag_input_layout) + private val createChallengeTag: EditText by bindView(R.id.create_challenge_tag) + private val createChallengeGemError: TextView by bindView(R.id.create_challenge_gem_error) + private val createChallengeTaskError: TextView by bindView(R.id.create_challenge_task_error) + private val challengeLocationSpinner: Spinner by bindView(R.id.challenge_location_spinner) + private val challengeAddGemBtn: Button by bindView(R.id.challenge_add_gem_btn) + private val challengeRemoveGemBtn: Button by bindView(R.id.challenge_remove_gem_btn) + private val createChallengeTaskList: RecyclerView by bindView(R.id.create_challenge_task_list) + private val gemIconView: ImageView by bindView(R.id.gem_icon) + + @Inject + internal lateinit var challengeRepository: ChallengeRepository + @Inject + internal lateinit var socialRepository: SocialRepository + @Inject + internal lateinit var userRepository: UserRepository + @field:[Inject Named(AppModule.NAMED_USER_ID)] + internal lateinit var userId: String + + private var challengeTasks = ChallengeTasksRecyclerViewAdapter(null, 0, this, "", null, false, true) + + private var locationAdapter = GroupArrayAdapter(this) + private var challengeId: String? = null + private var editMode: Boolean = false + + private val addedTasks = HashMap() + private val updatedTasks = HashMap() + private val removedTasks = HashMap() + + // Add {*} Items + private var addHabit: Task? = null + private var addDaily: Task? = null + private var addTodo: Task? = null + private var addReward: Task? = null + private var user: User? = null + + private var savingInProgress = false + + private val challengeData: Challenge + get() { + val c = Challenge() + + val locationPos = challengeLocationSpinner.selectedItemPosition + val locationGroup = locationAdapter.getItem(locationPos) + + if (challengeId != null) { + c.id = challengeId + } + + if (locationGroup != null) { + c.groupId = locationGroup.id + } + c.name = createChallengeTitle.text.toString() + c.description = createChallengeDescription.text.toString() + c.shortName = createChallengeTag.text.toString() + c.prize = Integer.parseInt(createChallengePrize.text.toString()) + + return c + } + + override fun getLayoutResId(): Int { + return R.layout.activity_create_challenge + } + + override fun injectActivity(component: AppComponent?) { + component?.inject(this) + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + val inflater = menuInflater + inflater.inflate(R.menu.menu_create_challenge, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == R.id.action_save && !savingInProgress && validateAllFields()) { + savingInProgress = true + val dialog = ProgressDialog.show(this, "", "Saving challenge data. Please wait...", true, false) + + val observable: Flowable = if (editMode) { + updateChallenge() + } else { + createChallenge() + } + + observable.subscribe({ + dialog.dismiss() + savingInProgress = false + finish() + }, { throwable -> + dialog.dismiss() + savingInProgress = false + RxErrorHandler.reportError(throwable) + }) + } else if (item.itemId == android.R.id.home) { + finish() + return true + } + + return super.onOptionsItemSelected(item) + } + + private fun validateAllFields(): Boolean { + val errorMessages = ArrayList() + + if (getEditTextString(createChallengeTitle).isEmpty()) { + val titleEmptyError = getString(R.string.challenge_create_error_title) + createChallengeTitleInputLayout.error = titleEmptyError + errorMessages.add(titleEmptyError) + } else { + createChallengeTitleInputLayout.isErrorEnabled = false + } + + if (getEditTextString(createChallengeTag).isEmpty()) { + val tagEmptyError = getString(R.string.challenge_create_error_tag) + + createChallengeTagInputLayout.error = tagEmptyError + errorMessages.add(tagEmptyError) + } else { + createChallengeTagInputLayout.isErrorEnabled = false + } + + val prizeError = checkPrizeAndMinimumForTavern() + + if (!prizeError.isEmpty()) { + errorMessages.add(prizeError) + } + + // all "Add {*}"-Buttons are one task itself, so we need atleast more than 4 + if (challengeTasks.taskList?.size ?: 0 <= 4) { + createChallengeTaskError.visibility = View.VISIBLE + errorMessages.add(getString(R.string.challenge_create_error_no_tasks)) + } else { + createChallengeTaskError.visibility = View.GONE + } + + val builder = AlertDialog.Builder(this) + .setMessage(U.join(errorMessages, "\n")) + + val alert = builder.create() + alert.show() + + return errorMessages.size == 0 + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val intent = intent + val bundle = intent.extras + + if (bundle != null) { + challengeId = bundle.getString(CHALLENGE_ID_KEY, null) + } + + fillControls() + + if (challengeId != null) { + fillControlsByChallenge() + } + + userRepository.getUser(userId).subscribe(Consumer { this.user = it }, RxErrorHandler.handleEmptyError()) + gemIconView.setImageBitmap(HabiticaIconsHelper.imageOfGem()) + + challengeAddGemBtn.setOnClickListener { onAddGem() } + challengeRemoveGemBtn.setOnClickListener { onRemoveGem() } + } + + + public override fun onDestroy() { + socialRepository.close() + challengeRepository.close() + super.onDestroy() + } + + @Subscribe + fun onEvent(tappedEvent: TaskTappedEvent) { + openNewTaskActivity(null, tappedEvent.Task) + } + + fun onAddGem() { + var stringValue = createChallengePrize.text.toString() + if (stringValue.isEmpty()) { + stringValue = "0" + } + var currentVal = Integer.parseInt(stringValue) + currentVal++ + + createChallengePrize.setText(currentVal.toString()) + + checkPrizeAndMinimumForTavern() + } + + fun onRemoveGem() { + var stringValue = createChallengePrize.text.toString() + if (stringValue.isEmpty()) { + stringValue = "0" + } + var currentVal = Integer.parseInt(stringValue) + currentVal-- + + createChallengePrize.setText(currentVal.toString()) + + checkPrizeAndMinimumForTavern() + } + + private fun checkPrizeAndMinimumForTavern(): String { + var errorResult = "" + + var inputValue = createChallengePrize.text.toString() + + if (inputValue.isEmpty()) { + inputValue = "0" + } + + val currentVal = Integer.parseInt(inputValue) + + // 0 is Tavern + val selectedLocation = challengeLocationSpinner.selectedItemPosition + + val gemCount = user?.gemCount?.toDouble() ?: 0.toDouble() + + if (selectedLocation == 0 && currentVal == 0) { + createChallengeGemError.visibility = View.VISIBLE + val error = getString(R.string.challenge_create_error_tavern_one_gem) + createChallengeGemError.text = error + errorResult = error + } else if (currentVal > gemCount) { + createChallengeGemError.visibility = View.VISIBLE + val error = getString(R.string.challenge_create_error_enough_gems) + createChallengeGemError.text = error + errorResult = error + } else { + createChallengeGemError.visibility = View.GONE + } + + challengeRemoveGemBtn.isEnabled = currentVal != 0 + + return errorResult + } + + private fun fillControls() { + val resources = resources + + val supportActionBar = supportActionBar + if (supportActionBar != null) { + supportActionBar.setDisplayShowHomeEnabled(true) + supportActionBar.setHomeAsUpIndicator(R.drawable.ic_close_white_24dp) + + supportActionBar.title = "" + supportActionBar.setBackgroundDrawable(ColorDrawable(ContextCompat.getColor(this, R.color.brand_200))) + supportActionBar.elevation = 0f + } + + locationAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + socialRepository.getGroups("guild").subscribe(Consumer { groups -> + val tavern = Group() + tavern.id = "00000000-0000-4000-A000-000000000000" + tavern.name = getString(R.string.sidebar_tavern) + + locationAdapter.add(tavern) + + for (group in groups) { + locationAdapter.add(group) + } + }, RxErrorHandler.handleEmptyError()) + + challengeLocationSpinner.adapter = locationAdapter + challengeLocationSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(adapterView: AdapterView<*>, view: View, i: Int, l: Long) { + checkPrizeAndMinimumForTavern() + } + + override fun onNothingSelected(adapterView: AdapterView<*>) { + + } + } + createChallengePrize.setOnKeyListener { _, _, _ -> + checkPrizeAndMinimumForTavern() + + false + } + + addHabit = createTask(ChallengeTasksRecyclerViewAdapter.TASK_TYPE_ADD_ITEM, resources.getString(R.string.add_habit)) + addDaily = createTask(ChallengeTasksRecyclerViewAdapter.TASK_TYPE_ADD_ITEM, resources.getString(R.string.add_daily)) + addTodo = createTask(ChallengeTasksRecyclerViewAdapter.TASK_TYPE_ADD_ITEM, resources.getString(R.string.add_todo)) + addReward = createTask(ChallengeTasksRecyclerViewAdapter.TASK_TYPE_ADD_ITEM, resources.getString(R.string.add_reward)) + + + val taskList = ArrayList() + addHabit.notNull { taskList.add(it) } + addDaily.notNull { taskList.add(it) } + addTodo.notNull { taskList.add(it) } + addReward.notNull { taskList.add(it) } + + challengeTasks.setTasks(taskList) + challengeTasks.addItemObservable().subscribe(Consumer { t -> + when (t) { + addHabit -> openNewTaskActivity(Task.TYPE_HABIT, null) + addDaily -> openNewTaskActivity(Task.TYPE_DAILY, null) + addTodo -> openNewTaskActivity(Task.TYPE_TODO, null) + addReward -> openNewTaskActivity(Task.TYPE_REWARD, null) + } + }, RxErrorHandler.handleEmptyError()) + + createChallengeTaskList.addOnItemTouchListener(object : RecyclerView.SimpleOnItemTouchListener() { + override fun onInterceptTouchEvent(rv: RecyclerView?, e: MotionEvent?): Boolean { + // Stop only scrolling. + return rv?.scrollState == RecyclerView.SCROLL_STATE_DRAGGING + } + }) + createChallengeTaskList.adapter = challengeTasks + createChallengeTaskList.layoutManager = LinearLayoutManager(this) + } + + private fun fillControlsByChallenge() { + challengeRepository.getChallenge(challengeId).subscribe(Consumer { challenge -> + + createChallengeTitle.setText(challenge.name) + createChallengeDescription.setText(challenge.description) + createChallengeTag.setText(challenge.shortName) + createChallengePrize.setText(challenge.prize.toString()) + + for (i in 0 until locationAdapter.count) { + val group = locationAdapter.getItem(i) + + if (group != null && challenge.groupId == group.id) { + challengeLocationSpinner.setSelection(i) + break + } + } + + checkPrizeAndMinimumForTavern() + + challengeRepository.getChallengeTasks(challengeId).doOnComplete { editMode = true }.subscribe(Consumer { tasks -> + for (task in tasks.tasks.values) { + addOrUpdateTaskInList(task) + } + }, RxErrorHandler.handleEmptyError()) + }, RxErrorHandler.handleEmptyError()) + } + + private fun openNewTaskActivity(type: String?, task: Task?) { + val bundle = Bundle() + + if (task == null) { + bundle.putString(TaskFormActivity.TASK_TYPE_KEY, type) + } else { + bundle.putParcelable(TaskFormActivity.PARCELABLE_TASK, task) + } + + bundle.putBoolean(TaskFormActivity.SAVE_TO_DB, false) + bundle.putBoolean(TaskFormActivity.SET_IGNORE_FLAG, true) + bundle.putBoolean(TaskFormActivity.SHOW_TAG_SELECTION, false) + bundle.putBoolean(TaskFormActivity.SHOW_CHECKLIST, false) + + val allocationMode = user?.preferences?.hasTaskBasedAllocation() + + bundle.putString(TaskFormActivity.USER_ID_KEY, user?.id) + bundle.putBoolean(TaskFormActivity.ALLOCATION_MODE_KEY, allocationMode ?: false) + + val intent = Intent(this, TaskFormActivity::class.java) + intent.putExtras(bundle) + + startActivityForResult(intent, 1) + } + + private fun createChallenge(): Flowable { + val c = challengeData + + val taskList = challengeTasks.taskList + taskList.remove(addHabit) + taskList.remove(addDaily) + taskList.remove(addTodo) + taskList.remove(addReward) + + return challengeRepository.createChallenge(c, taskList) + } + + private fun updateChallenge(): Flowable { + val c = challengeData + + val taskList = challengeTasks.taskList + taskList.remove(addHabit) + taskList.remove(addDaily) + taskList.remove(addTodo) + taskList.remove(addReward) + + return challengeRepository.updateChallenge(c, taskList, ArrayList(addedTasks.values), + ArrayList(updatedTasks.values), + ArrayList(removedTasks.keys) + ) + } + + private fun addOrUpdateTaskInList(task: Task) { + if (!challengeTasks.replaceTask(task)) { + val taskAbove: Task? = when (task.type) { + Task.TYPE_HABIT -> addHabit + Task.TYPE_DAILY -> addDaily + Task.TYPE_TODO -> addTodo + else -> addReward + } + + challengeTasks.addTaskUnder(task, taskAbove) + + if (editMode) { + addedTasks[task.id ?: ""] = task + } + } else { + // don't need to add the task to updatedTasks if its already been added right now + if (editMode && !addedTasks.containsKey(task.id)) { + updatedTasks[task.id ?: ""] = task + } + } + } + + private fun getEditTextString(editText: EditText): String { + return editText.text.toString() + } + + private inner class GroupArrayAdapter internal constructor(context: Context) : ArrayAdapter(context, android.R.layout.simple_spinner_item) { + + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + val checkedTextView = super.getView(position, convertView, parent) as AppCompatTextView + checkedTextView.text = getItem(position).name + return checkedTextView + } + + override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View { + val checkedTextView = super.getDropDownView(position, convertView, parent) as AppCompatCheckedTextView + checkedTextView.text = getItem(position).name + return checkedTextView + } + } + + companion object { + const val CHALLENGE_ID_KEY = "challengeId" + + private fun createTask(taskType: String, taskName: String): Task { + val t = Task() + + t.id = UUID.randomUUID().toString() + t.type = taskType + t.text = taskName + + if (taskType == Task.TYPE_HABIT) { + t.up = true + t.down = false + } + + return t + } + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/FullProfileActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/FullProfileActivity.java deleted file mode 100644 index ad716e7ae..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/FullProfileActivity.java +++ /dev/null @@ -1,506 +0,0 @@ -package com.habitrpg.android.habitica.ui.activities; - -import android.content.Context; -import android.content.Intent; -import android.graphics.Typeface; -import android.os.Bundle; -import android.support.v4.content.ContextCompat; -import android.support.v7.app.AlertDialog; -import android.support.v7.widget.AppCompatImageView; -import android.support.v7.widget.CardView; -import android.support.v7.widget.GridLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Button; -import android.widget.ProgressBar; -import android.widget.ScrollView; -import android.widget.TableLayout; -import android.widget.TableRow; -import android.widget.TextView; - -import com.facebook.drawee.backends.pipeline.Fresco; -import com.facebook.drawee.controller.BaseControllerListener; -import com.facebook.drawee.view.SimpleDraweeView; -import com.facebook.imagepipeline.image.ImageInfo; -import com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.components.AppComponent; -import com.habitrpg.android.habitica.data.ApiClient; -import com.habitrpg.android.habitica.data.InventoryRepository; -import com.habitrpg.android.habitica.data.SocialRepository; -import com.habitrpg.android.habitica.helpers.RxErrorHandler; -import com.habitrpg.android.habitica.helpers.UserStatComputer; -import com.habitrpg.android.habitica.models.Achievement; -import com.habitrpg.android.habitica.models.AchievementGroup; -import com.habitrpg.android.habitica.models.AchievementResult; -import com.habitrpg.android.habitica.models.inventory.Equipment; -import com.habitrpg.android.habitica.models.members.Member; -import com.habitrpg.android.habitica.models.user.Buffs; -import com.habitrpg.android.habitica.models.user.Outfit; -import com.habitrpg.android.habitica.models.user.Profile; -import com.habitrpg.android.habitica.models.user.Stats; -import com.habitrpg.android.habitica.ui.AvatarView; -import com.habitrpg.android.habitica.ui.AvatarWithBarsViewModel; -import com.habitrpg.android.habitica.ui.adapter.social.AchievementAdapter; -import com.habitrpg.android.habitica.ui.helpers.MarkdownParser; -import com.habitrpg.android.habitica.ui.helpers.UiUtils; -import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar; - -import net.pherth.android.emoji_library.EmojiEditText; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import javax.inject.Inject; - -import butterknife.BindView; -import io.reactivex.Flowable; -import io.realm.RealmResults; - -import static com.habitrpg.android.habitica.ui.views.HabiticaSnackbar.SnackbarDisplayType; - -public class FullProfileActivity extends BaseActivity { - @Inject - InventoryRepository inventoryRepository; - @Inject - ApiClient apiClient; - @Inject - SocialRepository socialRepository; - @BindView(R.id.profile_image) - SimpleDraweeView profile_image; - @BindView(R.id.profile_blurb) - TextView blurbTextView; - @BindView(R.id.avatarView) - AvatarView avatarView; - @BindView(R.id.copy_userid) - Button copyUserIdButton; - @BindView(R.id.userid) - TextView userIdText; - @BindView(R.id.profile_attributes_card) - CardView attributesCardView; - @BindView(R.id.attributes_table) - TableLayout attributesTableLayout; - @BindView(R.id.attributes_collapse_icon) - AppCompatImageView attributesCollapseIcon; - @BindView(R.id.equipment_table) - TableLayout equipmentTableLayout; - @BindView(R.id.costume_table) - TableLayout costumeTableLayout; - @BindView(R.id.profile_costume_card) - CardView costumeCard; - @BindView(R.id.avatar_with_bars) - View avatar_with_bars; - @BindView(R.id.fullprofile_scrollview) - ScrollView fullprofile_scrollview; - @BindView(R.id.profile_pets_found_count) - TextView petsFoundCount; - @BindView(R.id.profile_mounts_tamed_count) - TextView mountsTamedCount; - @BindView(R.id.profile_achievements_card) - CardView achievementCard; - @BindView(R.id.avatar_achievements_progress) - ProgressBar achievementProgress; - @BindView(R.id.recyclerView) - RecyclerView achievementGroupList; - private String userId; - private String userName; - private AvatarWithBarsViewModel avatarWithBars; - private float attributeStrSum = 0; - private float attributeIntSum = 0; - private float attributeConSum = 0; - private float attributePerSum = 0; - private boolean attributeDetailsHidden = true; - private ArrayList attributeRows = new ArrayList<>(); - - public static void open(Context context, String userId) { - if (userId.equals("system")) { - return; - } - Bundle bundle = new Bundle(); - bundle.putString("userId", userId); - - Intent intent = new Intent(context, FullProfileActivity.class); - intent.putExtras(bundle); - intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - context.startActivity(intent); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - Intent intent = getIntent(); - Bundle bundle = intent.getExtras(); - this.userId = bundle.getString("userId"); - - setTitle(R.string.profile_loading_data); - - socialRepository.getMember(this.userId).subscribe(this::updateView, RxErrorHandler.handleEmptyError()); - - avatarWithBars = new AvatarWithBarsViewModel(this, avatar_with_bars); - avatarWithBars.valueBarLabelsToBlack(); - - avatar_with_bars.setBackgroundColor(ContextCompat.getColor(this, R.color.transparent)); - - attributeRows.clear(); - attributesCardView.setOnClickListener(view -> toggleAttributeDetails()); - } - - @Override - protected void onDestroy() { - inventoryRepository.close(); - super.onDestroy(); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - int id = item.getItemId(); - - if (id == R.id.private_message) { - showSendMessageToUserDialog(); - return true; - } - - if (id == android.R.id.home) { - // app icon in action bar clicked; goto parent activity. - this.finish(); - return true; - } - - return super.onOptionsItemSelected(item); - } - - private void showSendMessageToUserDialog() { - LayoutInflater factory = LayoutInflater.from(this); - final View newMessageView = factory.inflate(R.layout.profile_new_message_dialog, null); - - EmojiEditText emojiEditText = (EmojiEditText) newMessageView.findViewById(R.id.edit_new_message_text); - - TextView newMessageTitle = (TextView) newMessageView.findViewById(R.id.new_message_title); - newMessageTitle.setText(String.format(getString(R.string.profile_send_message_to), userName)); - - final AlertDialog addMessageDialog = new AlertDialog.Builder(this) - .setPositiveButton(android.R.string.ok, (dialogInterface, i) -> { - socialRepository.postPrivateMessage(userId, emojiEditText.getText().toString()) - .subscribe(postChatMessageResult -> HabiticaSnackbar.Companion.showSnackbar((ViewGroup) FullProfileActivity.this.fullprofile_scrollview.getChildAt(0), - String.format(getString(R.string.profile_message_sent_to), userName), SnackbarDisplayType.NORMAL), RxErrorHandler.handleEmptyError()); - - UiUtils.dismissKeyboard(this); - }) - .setNegativeButton(android.R.string.cancel, (dialogInterface, i) -> UiUtils.dismissKeyboard(this)) - - .create(); - - addMessageDialog.setView(newMessageView); - - addMessageDialog.show(); - } - - private void updateView(Member user) { - Profile profile = user.getProfile(); - Stats stats = user.getStats(); - - userName = profile.getName(); - - setTitle(profile.getName()); - - String imageUrl = profile.getImageUrl(); - if (imageUrl == null || imageUrl.isEmpty()) { - profile_image.setVisibility(View.GONE); - } else { - profile_image.setController(Fresco.newDraweeControllerBuilder() - .setUri(imageUrl) - .setControllerListener(new BaseControllerListener() { - @Override - public void onFailure(String id, Throwable throwable) { - profile_image.setVisibility(View.GONE); - } - }) - .build()); - } - - String blurbText = profile.getBlurb(); - if (blurbText != null && !blurbText.isEmpty()) { - blurbTextView.setText(MarkdownParser.INSTANCE.parseMarkdown(blurbText)); - } - userIdText.setText(userId); - copyUserIdButton.setVisibility(View.VISIBLE); - copyUserIdButton.setOnClickListener(view -> { - android.content.ClipboardManager clipboard = (android.content.ClipboardManager) view.getContext() - .getSystemService(CLIPBOARD_SERVICE); - android.content.ClipData clip = android.content.ClipData.newPlainText(userId, userId); - clipboard.setPrimaryClip(clip); - }); - - - avatarView.setAvatar(user); - avatarWithBars.updateData(user); - - loadItemDataByOutfit(user.getEquipped()).subscribe(gear -> this.gotGear(gear, user), RxErrorHandler.handleEmptyError()); - - if (user.getPreferences().getCostume()) { - loadItemDataByOutfit(user.getCostume()).subscribe(this::gotCostume, RxErrorHandler.handleEmptyError()); - } else { - costumeCard.setVisibility(View.GONE); - } - - //petsFoundCount.setText(String.valueOf(user.getPetsFoundCount())); - //mountsTamedCount.setText(String.valueOf(user.getMountsTamedCount())); - - // Load the members achievements now - socialRepository.getMemberAchievements(this.userId).subscribe(this::fillAchievements, RxErrorHandler.handleEmptyError()); - } - - // endregion - - // region Attributes - - private void fillAchievements(AchievementResult achievements) { - if (achievements == null) { - return; - } - List items = new ArrayList<>(); - - fillAchievements(achievements.basic, items); - fillAchievements(achievements.seasonal, items); - fillAchievements(achievements.special, items); - - AchievementAdapter adapter = new AchievementAdapter(); - adapter.setItemList(items); - - GridLayoutManager layoutManager = new GridLayoutManager(this, 3); - layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { - @Override - public int getSpanSize(int position) { - if (adapter.getItemViewType(position) == 0) { - return layoutManager.getSpanCount(); - } else { - return 1; - } - } - }); - achievementGroupList.setLayoutManager(layoutManager); - achievementGroupList.setAdapter(adapter); - - stopAndHideProgress(achievementProgress); - } - - private void fillAchievements(AchievementGroup achievementGroup, List targetList) { - // Order by ID first - ArrayList achievementList = new ArrayList<>(achievementGroup.achievements.values()); - Collections.sort(achievementList, (achievement, t1) -> Double.compare(achievement.index, t1.index)); - - targetList.add(achievementGroup.label); - targetList.addAll(achievementList); - } - - private void stopAndHideProgress(ProgressBar bar) { - bar.setIndeterminate(false); - bar.setVisibility(View.GONE); - } - - private String getFloorValueString(float val, boolean roundDown) { - if (roundDown) { - return String.valueOf(Math.floor(val)); - } else { - if (val == 0.0) { - return "0"; - } else { - return String.valueOf(val); - } - } - } - - private float getFloorValue(float val, boolean roundDown) { - return roundDown - ? ((int) Math.floor(val)) - : val; - } - - private TableRow addEquipmentRow(TableLayout table, String gearKey, String text, String stats) { - TableRow gearRow = (TableRow) getLayoutInflater().inflate(R.layout.profile_gear_tablerow, table, false); - - SimpleDraweeView draweeView = (SimpleDraweeView) gearRow.findViewById(R.id.gear_drawee); - - draweeView.setController(Fresco.newDraweeControllerBuilder() - .setUri(AvatarView.IMAGE_URI_ROOT + "shop_" + gearKey + ".png") - .setControllerListener(new BaseControllerListener() { - @Override - public void onFailure(String id, Throwable throwable) { - draweeView.setVisibility(View.GONE); - } - }) - .build()); - - TextView keyTextView = (TextView) gearRow.findViewById(R.id.tableRowTextView1); - keyTextView.setText(text); - - TextView valueTextView = (TextView) gearRow.findViewById(R.id.tableRowTextView2); - - if (!stats.isEmpty()) { - valueTextView.setText(stats); - } else { - valueTextView.setVisibility(View.GONE); - } - - table.addView(gearRow); - - return gearRow; - } - - private void addLevelAttributes(Member user) { - float byLevelStat = Math.min(user.getStats().getLvl() / 2.0f, 50f); - - addAttributeRow(getString(R.string.profile_level), byLevelStat, byLevelStat, byLevelStat, byLevelStat, true, false); - } - - private Flowable> loadItemDataByOutfit(Outfit outfit) { - ArrayList outfitList = new ArrayList<>(); - outfitList.add(outfit.getArmor()); - outfitList.add(outfit.getBack()); - outfitList.add(outfit.getBody()); - outfitList.add(outfit.getEyeWear()); - outfitList.add(outfit.getHead()); - outfitList.add(outfit.getHeadAccessory()); - outfitList.add(outfit.getShield()); - outfitList.add(outfit.getWeapon()); - - return inventoryRepository.getItems(outfitList); - } - - public void gotGear(List equipmentList, Member user) { - UserStatComputer userStatComputer = new UserStatComputer(); - List statsRows = userStatComputer.computeClassBonus(equipmentList, user); - - equipmentTableLayout.removeAllViews(); - for (int index = 1 ; index < attributesTableLayout.getChildCount() ; index++) { - attributesTableLayout.removeViewAt(index); - } - - addLevelAttributes(user); - - for (UserStatComputer.StatsRow row : statsRows) { - if (row.getClass().equals(UserStatComputer.EquipmentRow.class)) { - UserStatComputer.EquipmentRow equipmentRow = (UserStatComputer.EquipmentRow) row; - addEquipmentRow(equipmentTableLayout, equipmentRow.gearKey, equipmentRow.text, equipmentRow.stats); - } else if (row.getClass().equals(UserStatComputer.AttributeRow.class)) { - UserStatComputer.AttributeRow attributeRow2 = (UserStatComputer.AttributeRow) row; - addAttributeRow(getString(attributeRow2.labelId), attributeRow2.strVal, attributeRow2.intVal, attributeRow2.conVal, attributeRow2.perVal, attributeRow2.roundDown, attributeRow2.isSummary); - } - } - - addNormalAddBuffAttributes(user.getStats()); - } - - public void gotCostume(List obj) { - // fill costume table - costumeTableLayout.removeAllViews(); - for (Equipment i : obj) { - addEquipmentRow(costumeTableLayout, i.getKey(), i.getText(), ""); - } - } - - private void addNormalAddBuffAttributes(Stats stats) { - Buffs buffs = stats.getBuffs(); - - addAttributeRow(getString(R.string.profile_allocated), stats.getStr(), stats.get_int(), stats.getCon(), stats.getPer(), true, false); - addAttributeRow(getString(R.string.profile_boosts), buffs.getStr(), buffs.get_int(), buffs.getCon(), buffs.getPer(), true, false); - - // Summary row - addAttributeRow("", attributeStrSum, attributeIntSum, attributeConSum, attributePerSum, false, true); - } - - private TableRow addAttributeRow(String label, float strVal, float intVal, float conVal, float perVal, boolean roundDown, boolean isSummary) { - TableRow tableRow = (TableRow) getLayoutInflater().inflate(R.layout.profile_attributetablerow, attributesTableLayout, false); - TextView keyTextView = (TextView) tableRow.findViewById(R.id.tv_attribute_type); - keyTextView.setText(label); - - TextView strTextView = (TextView) tableRow.findViewById(R.id.tv_attribute_str); - strTextView.setText(getFloorValueString(strVal, roundDown)); - - TextView intTextView = (TextView) tableRow.findViewById(R.id.tv_attribute_int); - intTextView.setText(getFloorValueString(intVal, roundDown)); - - TextView conTextView = (TextView) tableRow.findViewById(R.id.tv_attribute_con); - conTextView.setText(getFloorValueString(conVal, roundDown)); - - TextView perTextView = (TextView) tableRow.findViewById(R.id.tv_attribute_per); - perTextView.setText(getFloorValueString(perVal, roundDown)); - - - if (isSummary) { - strTextView.setTypeface(null, Typeface.BOLD); - intTextView.setTypeface(null, Typeface.BOLD); - conTextView.setTypeface(null, Typeface.BOLD); - perTextView.setTypeface(null, Typeface.BOLD); - } else { - attributeStrSum += getFloorValue(strVal, roundDown); - attributeIntSum += getFloorValue(intVal, roundDown); - attributeConSum += getFloorValue(conVal, roundDown); - attributePerSum += getFloorValue(perVal, roundDown); - - attributeRows.add(tableRow); - tableRow.setVisibility(attributeDetailsHidden ? View.GONE : View.VISIBLE); - } - - attributesTableLayout.addView(tableRow); - - return tableRow; - } - - private void toggleAttributeDetails() { - attributeDetailsHidden = !attributeDetailsHidden; - - attributesCollapseIcon.setImageDrawable(ContextCompat.getDrawable(this, attributeDetailsHidden - ? R.drawable.ic_keyboard_arrow_right_black_24dp - : R.drawable.ic_keyboard_arrow_down_black_24dp)); - - for (TableRow row : attributeRows) { - row.setVisibility(attributeDetailsHidden ? View.GONE : View.VISIBLE); - } - } - - - // endregion - - // region Navigation - - @Override - public boolean onSupportNavigateUp() { - finish(); - return true; - } - - @Override - public void onBackPressed() { - finish(); - } - - // endregion - - // region BaseActivity-Overrides - - @Override - protected int getLayoutResId() { - return R.layout.activity_full_profile; - } - - @Override - protected void injectActivity(AppComponent component) { - component.inject(this); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - // Inflate the menu; this adds items to the action bar if it is present. - getMenuInflater().inflate(R.menu.menu_full_profile, menu); - return true; - } - - // endregion - -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/FullProfileActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/FullProfileActivity.kt new file mode 100644 index 000000000..e71be4d27 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/FullProfileActivity.kt @@ -0,0 +1,465 @@ +package com.habitrpg.android.habitica.ui.activities + +import android.content.Context +import android.content.Intent +import android.graphics.Typeface +import android.os.Bundle +import android.support.v4.content.ContextCompat +import android.support.v7.app.AlertDialog +import android.support.v7.widget.AppCompatImageView +import android.support.v7.widget.CardView +import android.support.v7.widget.GridLayoutManager +import android.support.v7.widget.RecyclerView +import android.view.* +import android.widget.* +import com.facebook.drawee.backends.pipeline.Fresco +import com.facebook.drawee.controller.BaseControllerListener +import com.facebook.drawee.view.SimpleDraweeView +import com.facebook.imagepipeline.image.ImageInfo +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.components.AppComponent +import com.habitrpg.android.habitica.data.ApiClient +import com.habitrpg.android.habitica.data.InventoryRepository +import com.habitrpg.android.habitica.data.SocialRepository +import com.habitrpg.android.habitica.helpers.RxErrorHandler +import com.habitrpg.android.habitica.helpers.UserStatComputer +import com.habitrpg.android.habitica.models.AchievementGroup +import com.habitrpg.android.habitica.models.AchievementResult +import com.habitrpg.android.habitica.models.inventory.Equipment +import com.habitrpg.android.habitica.models.members.Member +import com.habitrpg.android.habitica.models.user.Outfit +import com.habitrpg.android.habitica.models.user.Stats +import com.habitrpg.android.habitica.ui.AvatarView +import com.habitrpg.android.habitica.ui.AvatarWithBarsViewModel +import com.habitrpg.android.habitica.ui.adapter.social.AchievementAdapter +import com.habitrpg.android.habitica.ui.helpers.MarkdownParser +import com.habitrpg.android.habitica.ui.helpers.UiUtils +import com.habitrpg.android.habitica.ui.helpers.bindView +import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar +import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar.SnackbarDisplayType +import io.reactivex.Flowable +import io.reactivex.functions.Consumer +import io.realm.RealmResults +import net.pherth.android.emoji_library.EmojiEditText +import java.util.* +import javax.inject.Inject + +class FullProfileActivity : BaseActivity() { + @Inject + lateinit var inventoryRepository: InventoryRepository + @Inject + lateinit var apiClient: ApiClient + @Inject + lateinit var socialRepository: SocialRepository + + private val profileImage: SimpleDraweeView by bindView(R.id.profile_image) + private val blurbTextView: TextView by bindView(R.id.profile_blurb) + private val avatarView: AvatarView by bindView(R.id.avatarView) + private val copyUserIdButton: Button by bindView(R.id.copy_userid) + private val userIdText: TextView by bindView(R.id.userid) + private val attributesCardView: CardView by bindView(R.id.profile_attributes_card) + private val attributesTableLayout: TableLayout by bindView(R.id.attributes_table) + private val attributesCollapseIcon: AppCompatImageView by bindView(R.id.attributes_collapse_icon) + private val equipmentTableLayout: TableLayout by bindView(R.id.equipment_table) + private val costumeTableLayout: TableLayout by bindView(R.id.costume_table) + private val costumeCard: CardView by bindView(R.id.profile_costume_card) + private val avatar_with_bars: View by bindView(R.id.avatar_with_bars) + private val fullprofile_scrollview: ScrollView by bindView(R.id.fullprofile_scrollview) + private val petsFoundCount: TextView by bindView(R.id.profile_pets_found_count) + private val mountsTamedCount: TextView by bindView(R.id.profile_mounts_tamed_count) + private val achievementCard: CardView by bindView(R.id.profile_achievements_card) + private val achievementProgress: ProgressBar by bindView(R.id.avatar_achievements_progress) + private val achievementGroupList: RecyclerView by bindView(R.id.recyclerView) + + private var userId = "" + private var userName: String? = null + private var avatarWithBars = AvatarWithBarsViewModel(this, avatar_with_bars) + private var attributeStrSum = 0f + private var attributeIntSum = 0f + private var attributeConSum = 0f + private var attributePerSum = 0f + private var attributeDetailsHidden = true + private val attributeRows = ArrayList() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val intent = intent + val bundle = intent.extras + this.userId = bundle?.getString("userId") ?: "" + + setTitle(R.string.profile_loading_data) + + socialRepository.getMember(this.userId).subscribe(Consumer { this.updateView(it) }, RxErrorHandler.handleEmptyError()) + + avatarWithBars.valueBarLabelsToBlack() + + avatar_with_bars.setBackgroundColor(ContextCompat.getColor(this, R.color.transparent)) + + attributeRows.clear() + attributesCardView.setOnClickListener { toggleAttributeDetails() } + } + + override fun onDestroy() { + inventoryRepository.close() + super.onDestroy() + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + val id = item.itemId + + if (id == R.id.private_message) { + showSendMessageToUserDialog() + return true + } + + if (id == android.R.id.home) { + // app icon in action bar clicked; goto parent activity. + this.finish() + return true + } + + return super.onOptionsItemSelected(item) + } + + private fun showSendMessageToUserDialog() { + val factory = LayoutInflater.from(this) + val newMessageView = factory.inflate(R.layout.profile_new_message_dialog, null) + + val emojiEditText = newMessageView.findViewById(R.id.edit_new_message_text) + + val newMessageTitle = newMessageView.findViewById(R.id.new_message_title) + newMessageTitle.text = String.format(getString(R.string.profile_send_message_to), userName) + + val addMessageDialog = AlertDialog.Builder(this) + .setPositiveButton(android.R.string.ok) { _, _ -> + socialRepository.postPrivateMessage(userId, emojiEditText.text.toString()) + .subscribe(Consumer { + HabiticaSnackbar.showSnackbar(this@FullProfileActivity.fullprofile_scrollview.getChildAt(0) as ViewGroup, + String.format(getString(R.string.profile_message_sent_to), userName), SnackbarDisplayType.NORMAL) + }, RxErrorHandler.handleEmptyError()) + + UiUtils.dismissKeyboard(this) + } + .setNegativeButton(android.R.string.cancel) { _, _ -> UiUtils.dismissKeyboard(this) } + + .create() + + addMessageDialog.setView(newMessageView) + + addMessageDialog.show() + } + + private fun updateView(user: Member) { + val profile = user.profile + + userName = profile.name + + title = profile.name + + val imageUrl = profile.imageUrl + if (imageUrl == null || imageUrl.isEmpty()) { + profileImage.visibility = View.GONE + } else { + profileImage.controller = Fresco.newDraweeControllerBuilder() + .setUri(imageUrl) + .setControllerListener(object : BaseControllerListener() { + override fun onFailure(id: String?, throwable: Throwable?) { + profileImage.visibility = View.GONE + } + }) + .build() + } + + val blurbText = profile.blurb + if (blurbText != null && !blurbText.isEmpty()) { + blurbTextView.text = MarkdownParser.parseMarkdown(blurbText) + } + userIdText.text = userId + copyUserIdButton.visibility = View.VISIBLE + copyUserIdButton.setOnClickListener { view -> + val clipboard = view.context + .getSystemService(Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager + val clip = android.content.ClipData.newPlainText(userId, userId) + clipboard.primaryClip = clip + } + + + avatarView.setAvatar(user) + avatarWithBars.updateData(user) + + loadItemDataByOutfit(user.equipped).subscribe(Consumer { gear -> this.gotGear(gear, user) }, RxErrorHandler.handleEmptyError()) + + if (user.preferences.costume) { + loadItemDataByOutfit(user.costume).subscribe(Consumer> { this.gotCostume(it) }, RxErrorHandler.handleEmptyError()) + } else { + costumeCard.visibility = View.GONE + } + + //petsFoundCount.setText(String.valueOf(user.getPetsFoundCount())); + //mountsTamedCount.setText(String.valueOf(user.getMountsTamedCount())); + + // Load the members achievements now + socialRepository.getMemberAchievements(this.userId).subscribe(Consumer { this.fillAchievements(it) }, RxErrorHandler.handleEmptyError()) + } + + // endregion + + // region Attributes + + private fun fillAchievements(achievements: AchievementResult?) { + if (achievements == null) { + return + } + val items = ArrayList() + + fillAchievements(achievements.basic, items) + fillAchievements(achievements.seasonal, items) + fillAchievements(achievements.special, items) + + val adapter = AchievementAdapter() + adapter.setItemList(items) + + val layoutManager = GridLayoutManager(this, 3) + layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { + override fun getSpanSize(position: Int): Int { + return if (adapter.getItemViewType(position) == 0) { + layoutManager.spanCount + } else { + 1 + } + } + } + achievementGroupList.layoutManager = layoutManager + achievementGroupList.adapter = adapter + + stopAndHideProgress(achievementProgress) + } + + private fun fillAchievements(achievementGroup: AchievementGroup, targetList: MutableList) { + // Order by ID first + val achievementList = ArrayList(achievementGroup.achievements.values) + achievementList.sortWith(Comparator { achievement, t1 -> java.lang.Double.compare(achievement.index.toDouble(), t1.index.toDouble()) }) + + targetList.add(achievementGroup.label) + targetList.addAll(achievementList) + } + + private fun stopAndHideProgress(bar: ProgressBar) { + bar.isIndeterminate = false + bar.visibility = View.GONE + } + + private fun getFloorValueString(`val`: Float, roundDown: Boolean): String { + return if (roundDown) { + Math.floor(`val`.toDouble()).toString() + } else { + if (`val`.toDouble() == 0.0) { + "0" + } else { + `val`.toString() + } + } + } + + private fun getFloorValue(value: Float, roundDown: Boolean): Float { + return if (roundDown) { + Math.floor(value.toDouble()).toFloat() + } else { + value + } + } + + private fun addEquipmentRow(table: TableLayout, gearKey: String?, text: String, stats: String) { + val gearRow = layoutInflater.inflate(R.layout.profile_gear_tablerow, table, false) as TableRow + + val draweeView = gearRow.findViewById(R.id.gear_drawee) + + draweeView.controller = Fresco.newDraweeControllerBuilder() + .setUri(AvatarView.IMAGE_URI_ROOT + "shop_" + gearKey + ".png") + .setControllerListener(object : BaseControllerListener() { + override fun onFailure(id: String?, throwable: Throwable?) { + draweeView.visibility = View.GONE + } + }) + .build() + + val keyTextView = gearRow.findViewById(R.id.tableRowTextView1) + keyTextView.text = text + + val valueTextView = gearRow.findViewById(R.id.tableRowTextView2) + + if (!stats.isEmpty()) { + valueTextView.text = stats + } else { + valueTextView.visibility = View.GONE + } + + table.addView(gearRow) + + } + + private fun addLevelAttributes(user: Member) { + val byLevelStat = Math.min(user.stats.getLvl() / 2.0f, 50f) + + addAttributeRow(getString(R.string.profile_level), byLevelStat, byLevelStat, byLevelStat, byLevelStat, true, false) + } + + private fun loadItemDataByOutfit(outfit: Outfit?): Flowable> { + val outfitList = ArrayList() + if (outfit != null) { + outfitList.add(outfit.armor) + outfitList.add(outfit.back) + outfitList.add(outfit.body) + outfitList.add(outfit.eyeWear) + outfitList.add(outfit.head) + outfitList.add(outfit.headAccessory) + outfitList.add(outfit.shield) + outfitList.add(outfit.weapon) + } + return inventoryRepository.getItems(outfitList) + } + + private fun gotGear(equipmentList: List, user: Member) { + val userStatComputer = UserStatComputer() + val statsRows = userStatComputer.computeClassBonus(equipmentList, user) + + equipmentTableLayout.removeAllViews() + for (index in 1 until attributesTableLayout.childCount) { + attributesTableLayout.removeViewAt(index) + } + + addLevelAttributes(user) + + for (row in statsRows) { + if (row.javaClass == UserStatComputer.EquipmentRow::class.java) { + val equipmentRow = row as UserStatComputer.EquipmentRow + addEquipmentRow(equipmentTableLayout, equipmentRow.gearKey, equipmentRow.text, equipmentRow.stats) + } else if (row.javaClass == UserStatComputer.AttributeRow::class.java) { + val attributeRow2 = row as UserStatComputer.AttributeRow + addAttributeRow(getString(attributeRow2.labelId), attributeRow2.strVal, attributeRow2.intVal, attributeRow2.conVal, attributeRow2.perVal, attributeRow2.roundDown, attributeRow2.isSummary) + } + } + + addNormalAddBuffAttributes(user.stats) + } + + private fun gotCostume(obj: List) { + // fill costume table + costumeTableLayout.removeAllViews() + for (i in obj) { + addEquipmentRow(costumeTableLayout, i.key, i.text, "") + } + } + + private fun addNormalAddBuffAttributes(stats: Stats) { + val buffs = stats.getBuffs() + + addAttributeRow(getString(R.string.profile_allocated), stats.getStr().toFloat(), stats.get_int().toFloat(), stats.getCon().toFloat(), stats.getPer().toFloat(), true, false) + addAttributeRow(getString(R.string.profile_boosts), buffs.getStr(), buffs.get_int(), buffs.getCon(), buffs.getPer(), true, false) + + // Summary row + addAttributeRow("", attributeStrSum, attributeIntSum, attributeConSum, attributePerSum, false, true) + } + + private fun addAttributeRow(label: String, strVal: Float, intVal: Float, conVal: Float, perVal: Float, roundDown: Boolean, isSummary: Boolean) { + val tableRow = layoutInflater.inflate(R.layout.profile_attributetablerow, attributesTableLayout, false) as TableRow + val keyTextView = tableRow.findViewById(R.id.tv_attribute_type) + keyTextView.text = label + + val strTextView = tableRow.findViewById(R.id.tv_attribute_str) + strTextView.text = getFloorValueString(strVal, roundDown) + + val intTextView = tableRow.findViewById(R.id.tv_attribute_int) + intTextView.text = getFloorValueString(intVal, roundDown) + + val conTextView = tableRow.findViewById(R.id.tv_attribute_con) + conTextView.text = getFloorValueString(conVal, roundDown) + + val perTextView = tableRow.findViewById(R.id.tv_attribute_per) + perTextView.text = getFloorValueString(perVal, roundDown) + + + if (isSummary) { + strTextView.setTypeface(null, Typeface.BOLD) + intTextView.setTypeface(null, Typeface.BOLD) + conTextView.setTypeface(null, Typeface.BOLD) + perTextView.setTypeface(null, Typeface.BOLD) + } else { + attributeStrSum += getFloorValue(strVal, roundDown) + attributeIntSum += getFloorValue(intVal, roundDown) + attributeConSum += getFloorValue(conVal, roundDown) + attributePerSum += getFloorValue(perVal, roundDown) + + attributeRows.add(tableRow) + tableRow.visibility = if (attributeDetailsHidden) View.GONE else View.VISIBLE + } + + attributesTableLayout.addView(tableRow) + + } + + private fun toggleAttributeDetails() { + attributeDetailsHidden = !attributeDetailsHidden + + attributesCollapseIcon.setImageDrawable(ContextCompat.getDrawable(this, if (attributeDetailsHidden) + R.drawable.ic_keyboard_arrow_right_black_24dp + else + R.drawable.ic_keyboard_arrow_down_black_24dp)) + + for (row in attributeRows) { + row.visibility = if (attributeDetailsHidden) View.GONE else View.VISIBLE + } + } + + + // endregion + + // region Navigation + + override fun onSupportNavigateUp(): Boolean { + finish() + return true + } + + override fun onBackPressed() { + finish() + } + + // endregion + + // region BaseActivity-Overrides + + override fun getLayoutResId(): Int { + return R.layout.activity_full_profile + } + + override fun injectActivity(component: AppComponent?) { + component?.inject(this) + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + // Inflate the menu; this adds items to the action bar if it is present. + menuInflater.inflate(R.menu.menu_full_profile, menu) + return true + } + + companion object { + + fun open(context: Context, userId: String) { + if (userId == "system") { + return + } + val bundle = Bundle() + bundle.putString("userId", userId) + + val intent = Intent(context, FullProfileActivity::class.java) + intent.putExtras(bundle) + intent.flags = Intent.FLAG_ACTIVITY_REORDER_TO_FRONT + context.startActivity(intent) + } + } + + // endregion + +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GemPurchaseActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GemPurchaseActivity.java deleted file mode 100644 index 220757e44..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GemPurchaseActivity.java +++ /dev/null @@ -1,300 +0,0 @@ -package com.habitrpg.android.habitica.ui.activities; - - -import android.content.Intent; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.design.widget.TabLayout; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentPagerAdapter; -import android.support.v4.view.ViewPager; -import android.support.v7.widget.Toolbar; -import android.view.MenuItem; - -import com.habitrpg.android.habitica.HabiticaApplication; -import com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.components.AppComponent; -import com.habitrpg.android.habitica.helpers.PurchaseTypes; -import com.habitrpg.android.habitica.proxy.CrashlyticsProxy; -import com.habitrpg.android.habitica.ui.fragments.GemsPurchaseFragment; -import com.habitrpg.android.habitica.ui.fragments.SubscriptionFragment; -import com.playseeds.android.sdk.Seeds; -import com.playseeds.android.sdk.inappmessaging.InAppMessageListener; - -import org.solovyev.android.checkout.ActivityCheckout; -import org.solovyev.android.checkout.BillingRequests; -import org.solovyev.android.checkout.Checkout; -import org.solovyev.android.checkout.ProductTypes; -import org.solovyev.android.checkout.Purchase; -import org.solovyev.android.checkout.Purchases; -import org.solovyev.android.checkout.RequestListener; - -import java.util.ArrayList; -import java.util.List; - -import javax.inject.Inject; - -import butterknife.BindView; - -public class GemPurchaseActivity extends BaseActivity implements InAppMessageListener { - - @Inject - CrashlyticsProxy crashlyticsProxy; - @BindView(R.id.tab_layout) - TabLayout tabLayout; - @BindView(R.id.viewPager) - ViewPager viewPager; - List fragments = new ArrayList<>(); - private ActivityCheckout checkout; - private BillingRequests billingRequests; - - @Override - protected int getLayoutResId() { - return R.layout.activity_gem_purchase; - } - - @Override - protected void injectActivity(AppComponent component) { - component.inject(this); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - checkout.onActivityResult(requestCode, resultCode, data); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setupCheckout(); - - Seeds.sharedInstance() - .simpleInit(this, this, "https://dash.playseeds.com", getString(R.string.seeds_app_key)) - .setLoggingEnabled(true); - Seeds.sharedInstance().requestInAppMessage(getString(R.string.seeds_interstitial_gems)); - Seeds.sharedInstance().requestInAppMessage(getString(R.string.seeds_interstitial_sharing)); - - Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - - if (getSupportActionBar() != null) { - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setDisplayShowHomeEnabled(true); - getSupportActionBar().setTitle(R.string.gem_purchase_toolbartitle); - } - - viewPager.setCurrentItem(0); - - setViewPagerAdapter(); - - checkout.destroyPurchaseFlow(); - - checkout.createPurchaseFlow(new RequestListener() { - @Override - public void onSuccess(@NonNull Purchase purchase) { - if (PurchaseTypes.allGemTypes.contains(purchase.sku)) { - billingRequests.consume(purchase.token, new RequestListener() { - @Override - public void onSuccess(@NonNull Object o) { - //EventBus.getDefault().post(new BoughtGemsEvent(GEMS_TO_ADD)); - if (purchase.sku.equals(PurchaseTypes.Purchase84Gems)) { - GemPurchaseActivity.this.showSeedsPromo(getString(R.string.seeds_interstitial_sharing), "store"); - } - } - - @Override - public void onError(int i, @NonNull Exception e) { - crashlyticsProxy.fabricLogE("Purchase", "Consume", e); - } - }); - } - } - - @Override - public void onError(int i, @NonNull Exception e) { - crashlyticsProxy.fabricLogE("Purchase", "Error", e); - } - }); - - - checkout.whenReady(new Checkout.Listener() { - @Override - public void onReady(@NonNull final BillingRequests billingRequests) { - GemPurchaseActivity.this.billingRequests = billingRequests; - - for (CheckoutFragment fragment : fragments) { - fragment.setBillingRequests(billingRequests); - } - - checkIfPendingPurchases(); - } - - @Override - public void onReady(@NonNull BillingRequests billingRequests, @NonNull String s, boolean b) { - } - }); - } - - @Override - public void onDestroy() { - if (checkout != null) { - checkout.stop(); - } - super.onDestroy(); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == android.R.id.home) { - finish(); - } - - return super.onOptionsItemSelected(item); - } - - private void setupCheckout() { - checkout = Checkout.forActivity(this, HabiticaApplication.getInstance(this).getBilling()); - checkout.start(); - } - - public ActivityCheckout getActivityCheckout() { - return checkout; - } - - @Override - public void inAppMessageClicked(String messageId) { - for (CheckoutFragment fragment : fragments) { - if (fragment.getClass().isAssignableFrom(GemsPurchaseFragment.class)) { - ((GemsPurchaseFragment)fragment).purchaseGems(PurchaseTypes.Purchase84Gems); - } - } - } - - @Override - public void inAppMessageDismissed(String messageId) { - - } - - @Override - public void inAppMessageLoadSucceeded(String messageId) { - - } - - @Override - public void inAppMessageShown(String messageId, boolean succeeded) { - - } - - @Override - public void noInAppMessageFound(String messageId) { - - } - - @Override - public void inAppMessageClickedWithDynamicPrice(String messageId, Double price) { - - } - - public void showSeedsPromo(final String messageId, final String context) { - try { - runOnUiThread(() -> { - if (Seeds.sharedInstance().isInAppMessageLoaded(messageId)) { - Seeds.sharedInstance().showInAppMessage(messageId, context); - } else { - // Skip the interstitial showing this time and try to reload the interstitial - Seeds.sharedInstance().requestInAppMessage(messageId); - } - }); - } catch (Exception e) { - System.out.println("Exception: " + e); - } - } - - public void setViewPagerAdapter() { - android.support.v4.app.FragmentManager fragmentManager = getSupportFragmentManager(); - - viewPager.setAdapter(new FragmentPagerAdapter(fragmentManager) { - - @Override - public Fragment getItem(int position) { - CheckoutFragment fragment; - if (position == 0) { - fragment = new GemsPurchaseFragment(); - } else { - fragment = new SubscriptionFragment(); - } - if (fragments.size() > position) { - fragments.set(position, fragment); - } else { - fragments.add(fragment); - } - fragment.setListener(GemPurchaseActivity.this); - fragment.setupCheckout(); - if (billingRequests != null) { - fragment.setBillingRequests(billingRequests); - } - return (Fragment) fragment; - } - - @Override - public int getCount() { - return 2; - } - - @Override - public CharSequence getPageTitle(int position) { - switch (position) { - case 0: - return getString(R.string.gems); - case 1: - return getString(R.string.subscriptions); - } - return ""; - } - }); - - if (tabLayout != null && viewPager != null) { - tabLayout.setupWithViewPager(viewPager); - } - } - - private void checkIfPendingPurchases() { - billingRequests.getAllPurchases(ProductTypes.IN_APP, new RequestListener() { - @Override - public void onSuccess(@NonNull Purchases purchases) { - for (Purchase purchase : purchases.list) { - if (PurchaseTypes.allGemTypes.contains(purchase.sku)) { - billingRequests.consume(purchase.token, new RequestListener() { - @Override - public void onSuccess(@NonNull Object o) { - //EventBus.getDefault().post(new BoughtGemsEvent(GEMS_TO_ADD)); - } - - @Override - public void onError(int i, @NonNull Exception e) { - crashlyticsProxy.fabricLogE("Purchase", "Consume", e); - } - }); - } - } - } - - @Override - public void onError(int i, @NonNull Exception e) { - crashlyticsProxy.fabricLogE("Purchase", "getAllPurchases", e); - } - }); - } - - public interface CheckoutFragment { - - void setupCheckout(); - - void setListener(GemPurchaseActivity listener); - - void setBillingRequests(BillingRequests billingRequests); - } - -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GemPurchaseActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GemPurchaseActivity.kt new file mode 100644 index 000000000..a8bcbba23 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GemPurchaseActivity.kt @@ -0,0 +1,254 @@ +package com.habitrpg.android.habitica.ui.activities + + +import android.content.Intent +import android.os.Bundle +import android.support.design.widget.TabLayout +import android.support.v4.app.Fragment +import android.support.v4.app.FragmentPagerAdapter +import android.support.v4.view.ViewPager +import android.support.v7.widget.Toolbar +import android.view.MenuItem +import com.habitrpg.android.habitica.HabiticaApplication +import com.habitrpg.android.habitica.HabiticaBaseApplication +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.components.AppComponent +import com.habitrpg.android.habitica.extensions.notNull +import com.habitrpg.android.habitica.helpers.PurchaseTypes +import com.habitrpg.android.habitica.proxy.CrashlyticsProxy +import com.habitrpg.android.habitica.ui.fragments.GemsPurchaseFragment +import com.habitrpg.android.habitica.ui.fragments.SubscriptionFragment +import com.habitrpg.android.habitica.ui.helpers.bindView +import com.playseeds.android.sdk.Seeds +import com.playseeds.android.sdk.inappmessaging.InAppMessageListener +import org.solovyev.android.checkout.* +import java.util.* +import javax.inject.Inject + +class GemPurchaseActivity : BaseActivity(), InAppMessageListener { + + @Inject + lateinit var crashlyticsProxy: CrashlyticsProxy + + internal val tabLayout: TabLayout by bindView(R.id.tab_layout) + internal val viewPager: ViewPager by bindView(R.id.viewPager) + + internal var fragments: MutableList = ArrayList() + var activityCheckout: ActivityCheckout? = null + private set + private var billingRequests: BillingRequests? = null + + override fun getLayoutResId(): Int { + return R.layout.activity_gem_purchase + } + + override fun injectActivity(component: AppComponent?) { + component?.inject(this) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) { + super.onActivityResult(requestCode, resultCode, data) + activityCheckout?.onActivityResult(requestCode, resultCode, data) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setupCheckout() + + Seeds.sharedInstance() + .simpleInit(this, this, "https://dash.playseeds.com", getString(R.string.seeds_app_key)).isLoggingEnabled = true + Seeds.sharedInstance().requestInAppMessage(getString(R.string.seeds_interstitial_gems)) + Seeds.sharedInstance().requestInAppMessage(getString(R.string.seeds_interstitial_sharing)) + + val toolbar = findViewById(R.id.toolbar) + setSupportActionBar(toolbar) + + supportActionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.setDisplayShowHomeEnabled(true) + supportActionBar?.setTitle(R.string.gem_purchase_toolbartitle) + + viewPager.currentItem = 0 + + setViewPagerAdapter() + + activityCheckout?.destroyPurchaseFlow() + + activityCheckout?.createPurchaseFlow(object : RequestListener { + override fun onSuccess(purchase: Purchase) { + if (PurchaseTypes.allGemTypes.contains(purchase.sku)) { + billingRequests?.consume(purchase.token, object : RequestListener { + override fun onSuccess(o: Any) { + //EventBus.getDefault().post(new BoughtGemsEvent(GEMS_TO_ADD)); + if (purchase.sku == PurchaseTypes.Purchase84Gems) { + this@GemPurchaseActivity.showSeedsPromo(getString(R.string.seeds_interstitial_sharing), "store") + } + } + + override fun onError(i: Int, e: Exception) { + crashlyticsProxy.fabricLogE("Purchase", "Consume", e) + } + }) + } + } + + override fun onError(i: Int, e: Exception) { + crashlyticsProxy.fabricLogE("Purchase", "Error", e) + } + }) + + + activityCheckout?.whenReady(object : Checkout.Listener { + override fun onReady(billingRequests: BillingRequests) { + this@GemPurchaseActivity.billingRequests = billingRequests + + for (fragment in fragments) { + fragment.setBillingRequests(billingRequests) + } + + checkIfPendingPurchases() + } + + override fun onReady(billingRequests: BillingRequests, s: String, b: Boolean) {} + }) + } + + public override fun onDestroy() { + activityCheckout?.stop() + super.onDestroy() + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == android.R.id.home) { + finish() + } + + return super.onOptionsItemSelected(item) + } + + private fun setupCheckout() { + HabiticaBaseApplication.getInstance(this).billing.notNull { + activityCheckout = Checkout.forActivity(this, it) + activityCheckout?.start() + } + + } + + override fun inAppMessageClicked(messageId: String) { + for (fragment in fragments) { + if (fragment.javaClass.isAssignableFrom(GemsPurchaseFragment::class.java)) { + (fragment as GemsPurchaseFragment).purchaseGems(PurchaseTypes.Purchase84Gems) + } + } + } + + override fun inAppMessageDismissed(messageId: String) { + + } + + override fun inAppMessageLoadSucceeded(messageId: String) { + + } + + override fun inAppMessageShown(messageId: String, succeeded: Boolean) { + + } + + override fun noInAppMessageFound(messageId: String) { + + } + + override fun inAppMessageClickedWithDynamicPrice(messageId: String, price: Double?) { + + } + + fun showSeedsPromo(messageId: String, context: String) { + try { + runOnUiThread { + if (Seeds.sharedInstance().isInAppMessageLoaded(messageId)) { + Seeds.sharedInstance().showInAppMessage(messageId, context) + } else { + // Skip the interstitial showing this time and try to reload the interstitial + Seeds.sharedInstance().requestInAppMessage(messageId) + } + } + } catch (e: Exception) { + println("Exception: $e") + } + + } + + private fun setViewPagerAdapter() { + val fragmentManager = supportFragmentManager + + viewPager.adapter = object : FragmentPagerAdapter(fragmentManager) { + + override fun getItem(position: Int): Fragment { + val fragment: CheckoutFragment = if (position == 0) { + GemsPurchaseFragment() + } else { + SubscriptionFragment() + } + if (fragments.size > position) { + fragments[position] = fragment + } else { + fragments.add(fragment) + } + fragment.setListener(this@GemPurchaseActivity) + fragment.setupCheckout() + if (billingRequests != null) { + fragment.setBillingRequests(billingRequests) + } + return fragment as Fragment + } + + override fun getCount(): Int { + return 2 + } + + override fun getPageTitle(position: Int): CharSequence? { + when (position) { + 0 -> return getString(R.string.gems) + 1 -> return getString(R.string.subscriptions) + } + return "" + } + } + + tabLayout.setupWithViewPager(viewPager) + } + + private fun checkIfPendingPurchases() { + billingRequests?.getAllPurchases(ProductTypes.IN_APP, object : RequestListener { + override fun onSuccess(purchases: Purchases) { + for (purchase in purchases.list) { + if (PurchaseTypes.allGemTypes.contains(purchase.sku)) { + billingRequests?.consume(purchase.token, object : RequestListener { + override fun onSuccess(o: Any) { + //EventBus.getDefault().post(new BoughtGemsEvent(GEMS_TO_ADD)); + } + + override fun onError(i: Int, e: Exception) { + crashlyticsProxy.fabricLogE("Purchase", "Consume", e) + } + }) + } + } + } + + override fun onError(i: Int, e: Exception) { + crashlyticsProxy.fabricLogE("Purchase", "getAllPurchases", e) + } + }) + } + + interface CheckoutFragment { + + fun setupCheckout() + + fun setListener(listener: GemPurchaseActivity) + + fun setBillingRequests(billingRequests: BillingRequests?) + } + +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GroupFormActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GroupFormActivity.java deleted file mode 100644 index 9c0dcb502..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GroupFormActivity.java +++ /dev/null @@ -1,227 +0,0 @@ -package com.habitrpg.android.habitica.ui.activities; - -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.support.v4.content.ContextCompat; -import android.view.KeyEvent; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.inputmethod.InputMethodManager; -import android.widget.EditText; -import android.widget.ImageButton; -import android.widget.LinearLayout; -import android.widget.Spinner; - -import com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.components.AppComponent; -import com.habitrpg.android.habitica.ui.helpers.MarkdownParser; -import com.habitrpg.android.habitica.ui.helpers.UiUtils; - -import net.pherth.android.emoji_library.EmojiEditText; -import net.pherth.android.emoji_library.EmojiPopup; - -import butterknife.BindView; - -public class GroupFormActivity extends BaseActivity { - - public static final int GROUP_FORM_ACTIVITY = 11; - - String groupID; - String groupName; - String groupDescription; - String groupPrivacy; - String groupLeader; - - @BindView(R.id.group_name_edittext) - EditText groupNameEditText; - - @BindView(R.id.group_description_edittext) - EmojiEditText groupDescriptionEditText; - - @BindView(R.id.emoji_toggle_btn) - ImageButton emojiButton; - - @BindView(R.id.privacyWrapper) - LinearLayout privacyWrapper; - - @BindView(R.id.privacySpinner) - Spinner privacySpinner; - - EmojiPopup popup; - - @Override - protected int getLayoutResId() { - return R.layout.activity_group_form; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - Intent intent = getIntent(); - Bundle bundle = intent.getExtras(); - this.groupID = bundle.getString("groupID"); - this.groupName = bundle.getString("name"); - this.groupDescription = bundle.getString("description"); - this.groupPrivacy = bundle.getString("privacy"); - this.groupLeader = bundle.getString("leader"); - - // Emoji keyboard stuff - - popup = new EmojiPopup(emojiButton.getRootView(), this, ContextCompat.getColor(this, R.color.brand)); - - popup.setSizeForSoftKeyboard(); - popup.setOnDismissListener(() -> changeEmojiKeyboardIcon(false)); - popup.setOnSoftKeyboardOpenCloseListener(new EmojiPopup.OnSoftKeyboardOpenCloseListener() { - - @Override - public void onKeyboardOpen(int keyBoardHeight) { - - } - - @Override - public void onKeyboardClose() { - if (popup.isShowing()) - popup.dismiss(); - } - }); - - popup.setOnEmojiconClickedListener(emojicon -> { - EmojiEditText emojiEditText = null; - if (getCurrentFocus() == null || !isEmojiEditText(getCurrentFocus()) || emojicon == null) { - return; - } else { - emojiEditText = (EmojiEditText) getCurrentFocus(); - } - int start = emojiEditText.getSelectionStart(); - int end = emojiEditText.getSelectionEnd(); - if (start < 0) { - emojiEditText.append(emojicon.getEmoji()); - } else { - emojiEditText.getText().replace(Math.min(start, end), - Math.max(start, end), emojicon.getEmoji(), 0, - emojicon.getEmoji().length()); - } - }); - - popup.setOnEmojiconBackspaceClickedListener(v -> { - if (isEmojiEditText(getCurrentFocus())) { - KeyEvent event = new KeyEvent( - 0, 0, 0, KeyEvent.KEYCODE_DEL, 0, 0, 0, 0, KeyEvent.KEYCODE_ENDCALL); - getCurrentFocus().dispatchKeyEvent(event); - } - }); - - emojiButton.setOnClickListener(new emojiClickListener(groupDescriptionEditText)); - - if (this.groupID != null) { - this.fillForm(); - } - } - - - @Override - protected void injectActivity(AppComponent component) { - component.inject(this); - } - - private void fillForm() { - this.groupNameEditText.setText(this.groupName); - this.groupDescriptionEditText.setText(this.groupDescription); - this.privacyWrapper.setVisibility(View.GONE); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - // Inflate the menu; this adds items to the action bar if it is present. - getMenuInflater().inflate(R.menu.menu_save, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - // Handle action bar item clicks here. The action bar will - // automatically handle clicks on the Home/Up button, so long - // as you specify a parent activity in AndroidManifest.xml. - int id = item.getItemId(); - - //noinspection SimplifiableIfStatement - if (id == R.id.action_save_changes) { - finishActivitySuccessfuly(); - return true; - } - - return super.onOptionsItemSelected(item); - } - - private boolean isEmojiEditText(View view) { - return view instanceof EmojiEditText; - } - - private void changeEmojiKeyboardIcon(Boolean keyboardOpened) { - - if (keyboardOpened) { - emojiButton.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_keyboard_grey600_24dp)); - } else { - emojiButton.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_emoticon_grey600_24dp)); - } - } - - @Override - public boolean onSupportNavigateUp() { - finish(); - UiUtils.dismissKeyboard(this); - return true; - } - - @Override - public void onBackPressed() { - finish(); - UiUtils.dismissKeyboard(this); - } - - private void finishActivitySuccessfuly() { - Intent resultIntent = new Intent(); - Bundle bundle = new Bundle(); - bundle.putString("name", this.groupNameEditText.getText().toString()); - bundle.putString("description", MarkdownParser.INSTANCE.parseCompiled(this.groupDescriptionEditText.getText())); - bundle.putString("leader", this.groupLeader); - resultIntent.putExtras(bundle); - setResult(Activity.RESULT_OK, resultIntent); - finish(); - UiUtils.dismissKeyboard(this); - } - - private class emojiClickListener implements View.OnClickListener { - - EmojiEditText view; - - public emojiClickListener(EmojiEditText view) { - this.view = view; - } - - @Override - public void onClick(View v) { - if (!popup.isShowing()) { - - if (popup.isKeyBoardOpen()) { - popup.showAtBottom(); - changeEmojiKeyboardIcon(true); - } else { - view.setFocusableInTouchMode(true); - view.requestFocus(); - popup.showAtBottomPending(); - final InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - inputMethodManager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT); - changeEmojiKeyboardIcon(true); - } - } else { - popup.dismiss(); - changeEmojiKeyboardIcon(false); - } - } - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GroupFormActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GroupFormActivity.kt new file mode 100644 index 000000000..7e1ca663a --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/GroupFormActivity.kt @@ -0,0 +1,202 @@ +package com.habitrpg.android.habitica.ui.activities + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.support.v4.content.ContextCompat +import android.view.KeyEvent +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.view.inputmethod.InputMethodManager +import android.widget.EditText +import android.widget.ImageButton +import android.widget.LinearLayout +import android.widget.Spinner +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.components.AppComponent +import com.habitrpg.android.habitica.ui.helpers.MarkdownParser +import com.habitrpg.android.habitica.ui.helpers.UiUtils +import com.habitrpg.android.habitica.ui.helpers.bindView +import net.pherth.android.emoji_library.EmojiEditText +import net.pherth.android.emoji_library.EmojiPopup + +class GroupFormActivity : BaseActivity() { + + private var groupID: String? = null + private var groupName: String? = null + private var groupDescription: String? = null + private var groupPrivacy: String? = null + private var groupLeader: String? = null + + private val groupNameEditText: EditText by bindView(R.id.group_name_edittext) + private val groupDescriptionEditText: EmojiEditText by bindView(R.id.group_description_edittext) + internal val emojiButton: ImageButton by bindView(R.id.emoji_toggle_btn) + internal val privacyWrapper: LinearLayout by bindView(R.id.privacyWrapper) + internal val privacySpinner: Spinner by bindView(R.id.privacySpinner) + + private val popup: EmojiPopup by lazy { + EmojiPopup(emojiButton.rootView, this, ContextCompat.getColor(this, R.color.brand)) + } + + + override fun getLayoutResId(): Int { + return R.layout.activity_group_form + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val intent = intent + val bundle = intent.extras + this.groupID = bundle!!.getString("groupID") + this.groupName = bundle.getString("name") + this.groupDescription = bundle.getString("description") + this.groupPrivacy = bundle.getString("privacy") + this.groupLeader = bundle.getString("leader") + + // Emoji keyboard stuff + + popup + + popup.setSizeForSoftKeyboard() + popup.setOnDismissListener { changeEmojiKeyboardIcon(false) } + popup.setOnSoftKeyboardOpenCloseListener(object : EmojiPopup.OnSoftKeyboardOpenCloseListener { + + override fun onKeyboardOpen(keyBoardHeight: Int) { + } + + override fun onKeyboardClose() { + if (popup.isShowing) { + popup.dismiss() + } + } + }) + + popup.setOnEmojiconClickedListener { emojicon -> + if (currentFocus == null || !isEmojiEditText(currentFocus) || emojicon == null) { + return@setOnEmojiconClickedListener + } + val emojiEditText = currentFocus as EmojiEditText + val start = emojiEditText.selectionStart + val end = emojiEditText.selectionEnd + if (start < 0) { + emojiEditText.append(emojicon.emoji) + } else { + emojiEditText.text.replace(Math.min(start, end), + Math.max(start, end), emojicon.emoji, 0, + emojicon.emoji.length) + } + } + + popup.setOnEmojiconBackspaceClickedListener { v -> + if (isEmojiEditText(currentFocus)) { + val event = KeyEvent( + 0, 0, 0, KeyEvent.KEYCODE_DEL, 0, 0, 0, 0, KeyEvent.KEYCODE_ENDCALL) + currentFocus!!.dispatchKeyEvent(event) + } + } + + emojiButton.setOnClickListener(EmojiClickListener(groupDescriptionEditText)) + + if (this.groupID != null) { + this.fillForm() + } + } + + + override fun injectActivity(component: AppComponent?) { + component?.inject(this) + } + + private fun fillForm() { + this.groupNameEditText.setText(this.groupName) + this.groupDescriptionEditText.setText(this.groupDescription) + this.privacyWrapper.visibility = View.GONE + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + // Inflate the menu; this adds items to the action bar if it is present. + menuInflater.inflate(R.menu.menu_save, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + val id = item.itemId + + + if (id == R.id.action_save_changes) { + finishActivitySuccessfuly() + return true + } + + return super.onOptionsItemSelected(item) + } + + private fun isEmojiEditText(view: View?): Boolean { + return view is EmojiEditText + } + + private fun changeEmojiKeyboardIcon(keyboardOpened: Boolean) { + + if (keyboardOpened) { + emojiButton.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_keyboard_grey600_24dp)) + } else { + emojiButton.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_emoticon_grey600_24dp)) + } + } + + override fun onSupportNavigateUp(): Boolean { + finish() + UiUtils.dismissKeyboard(this) + return true + } + + override fun onBackPressed() { + finish() + UiUtils.dismissKeyboard(this) + } + + private fun finishActivitySuccessfuly() { + val resultIntent = Intent() + val bundle = Bundle() + bundle.putString("name", this.groupNameEditText.text.toString()) + bundle.putString("description", MarkdownParser.parseCompiled(this.groupDescriptionEditText.text)) + bundle.putString("leader", this.groupLeader) + resultIntent.putExtras(bundle) + setResult(Activity.RESULT_OK, resultIntent) + finish() + UiUtils.dismissKeyboard(this) + } + + private inner class EmojiClickListener(internal var view: EmojiEditText) : View.OnClickListener { + + override fun onClick(v: View) { + if (!popup.isShowing) { + if (popup.isKeyBoardOpen == true) { + popup.showAtBottom() + changeEmojiKeyboardIcon(true) + } else { + view.isFocusableInTouchMode = true + view.requestFocus() + popup.showAtBottomPending() + val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + inputMethodManager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT) + changeEmojiKeyboardIcon(true) + } + } else { + popup.dismiss() + changeEmojiKeyboardIcon(false) + } + } + } + + companion object { + + const val GROUP_FORM_ACTIVITY = 11 + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/HabitButtonWidgetActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/HabitButtonWidgetActivity.java deleted file mode 100644 index 932080c68..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/HabitButtonWidgetActivity.java +++ /dev/null @@ -1,101 +0,0 @@ -package com.habitrpg.android.habitica.ui.activities; - -import android.appwidget.AppWidgetManager; -import android.content.Intent; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.support.v7.preference.PreferenceManager; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; - -import com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.components.AppComponent; -import com.habitrpg.android.habitica.data.TaskRepository; -import com.habitrpg.android.habitica.helpers.RxErrorHandler; -import com.habitrpg.android.habitica.models.tasks.Task; -import com.habitrpg.android.habitica.modules.AppModule; -import com.habitrpg.android.habitica.ui.adapter.SkillTasksRecyclerViewAdapter; -import com.habitrpg.android.habitica.widget.HabitButtonWidgetProvider; - -import javax.inject.Inject; -import javax.inject.Named; - -import butterknife.BindView; - -public class HabitButtonWidgetActivity extends BaseActivity { - - @Inject - TaskRepository taskRepository; - @Inject - @Named(AppModule.NAMED_USER_ID) - String userId; - - @BindView(R.id.recyclerView) - RecyclerView recyclerView; - private int widgetId; - private SkillTasksRecyclerViewAdapter adapter; - - @Override - protected int getLayoutResId() { - return R.layout.widget_configure_habit_button; - } - - @Override - protected void injectActivity(AppComponent component) { - component.inject(this); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - Intent intent = getIntent(); - Bundle extras = intent.getExtras(); - if (extras != null) { - widgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); - } - - // If this activity was started with an intent without an app widget ID, - // finish with an error. - if (widgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { - finish(); - } - - LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager(); - - if (layoutManager == null) { - layoutManager = new LinearLayoutManager(this); - - recyclerView.setLayoutManager(layoutManager); - } - - adapter = new SkillTasksRecyclerViewAdapter(null, true); - compositeSubscription.add(adapter.getTaskSelectionEvents().subscribe(task -> taskSelected(task.getId()), RxErrorHandler.handleEmptyError())); - recyclerView.setAdapter(adapter); - - taskRepository.getTasks(Task.TYPE_HABIT, userId).firstElement().subscribe(adapter::updateData, RxErrorHandler.handleEmptyError()); - } - - public void taskSelected(String taskId) { - finishWithSelection(taskId); - } - - private void finishWithSelection(String selectedTaskId) { - storeSelectedTaskId(selectedTaskId); - - Intent resultValue = new Intent(); - resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId); - setResult(RESULT_OK, resultValue); - finish(); - - Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE, null, this, HabitButtonWidgetProvider.class); - intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[]{widgetId}); - sendBroadcast(intent); - } - - private void storeSelectedTaskId(String selectedTaskId) { - SharedPreferences.Editor preferences = PreferenceManager.getDefaultSharedPreferences(this).edit(); - preferences.putString("habit_button_widget_" + widgetId, selectedTaskId); - preferences.apply(); - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/HabitButtonWidgetActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/HabitButtonWidgetActivity.kt new file mode 100644 index 000000000..6dce245b5 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/HabitButtonWidgetActivity.kt @@ -0,0 +1,95 @@ +package com.habitrpg.android.habitica.ui.activities + +import android.app.Activity +import android.appwidget.AppWidgetManager +import android.content.Intent +import android.os.Bundle +import android.support.v7.preference.PreferenceManager +import android.support.v7.widget.LinearLayoutManager +import android.support.v7.widget.RecyclerView +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.components.AppComponent +import com.habitrpg.android.habitica.data.TaskRepository +import com.habitrpg.android.habitica.extensions.notNull +import com.habitrpg.android.habitica.helpers.RxErrorHandler +import com.habitrpg.android.habitica.models.tasks.Task +import com.habitrpg.android.habitica.modules.AppModule +import com.habitrpg.android.habitica.ui.adapter.SkillTasksRecyclerViewAdapter +import com.habitrpg.android.habitica.ui.helpers.bindView +import com.habitrpg.android.habitica.widget.HabitButtonWidgetProvider +import io.reactivex.functions.Consumer +import javax.inject.Inject +import javax.inject.Named + +class HabitButtonWidgetActivity : BaseActivity() { + + @Inject + lateinit var taskRepository: TaskRepository + @field:[Inject Named(AppModule.NAMED_USER_ID)] + lateinit var userId: String + + internal val recyclerView: RecyclerView by bindView(R.id.recyclerView) + private var widgetId: Int = 0 + private var adapter: SkillTasksRecyclerViewAdapter? = null + + override fun getLayoutResId(): Int { + return R.layout.widget_configure_habit_button + } + + override fun injectActivity(component: AppComponent?) { + component?.inject(this) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val intent = intent + val extras = intent.extras + if (extras != null) { + widgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID) + } + + // If this activity was started with an intent without an app widget ID, + // finish with an error. + if (widgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { + finish() + } + + var layoutManager: LinearLayoutManager? = recyclerView.layoutManager as LinearLayoutManager? + + if (layoutManager == null) { + layoutManager = LinearLayoutManager(this) + + recyclerView.layoutManager = layoutManager + } + + adapter = SkillTasksRecyclerViewAdapter(null, true) + adapter?.getTaskSelectionEvents()?.subscribe(Consumer { task -> taskSelected(task.id) }, RxErrorHandler.handleEmptyError()).notNull { compositeSubscription?.add(it) } + recyclerView.adapter = adapter + + taskRepository.getTasks(Task.TYPE_HABIT, userId).firstElement().subscribe(Consumer { adapter?.updateData(it) }, RxErrorHandler.handleEmptyError()) + } + + private fun taskSelected(taskId: String?) { + finishWithSelection(taskId) + } + + private fun finishWithSelection(selectedTaskId: String?) { + storeSelectedTaskId(selectedTaskId) + + val resultValue = Intent() + resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId) + setResult(Activity.RESULT_OK, resultValue) + finish() + + val intent = Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE, null, this, HabitButtonWidgetProvider::class.java) + intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, intArrayOf(widgetId)) + sendBroadcast(intent) + } + + private fun storeSelectedTaskId(selectedTaskId: String?) { + val preferences = PreferenceManager.getDefaultSharedPreferences(this).edit() + preferences.putString("habit_button_widget_$widgetId", selectedTaskId) + preferences.apply() + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/IntroActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/IntroActivity.java deleted file mode 100644 index 5f12480ba..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/IntroActivity.java +++ /dev/null @@ -1,167 +0,0 @@ -package com.habitrpg.android.habitica.ui.activities; - -import android.content.Intent; -import android.os.Build; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentPagerAdapter; -import android.support.v4.content.ContextCompat; -import android.support.v4.content.res.ResourcesCompat; -import android.support.v4.view.ViewPager; -import android.view.View; -import android.view.Window; -import android.view.WindowManager; -import android.widget.Button; - -import com.habitrpg.android.habitica.HabiticaBaseApplication; -import com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.components.AppComponent; -import com.habitrpg.android.habitica.data.ApiClient; -import com.habitrpg.android.habitica.helpers.RxErrorHandler; -import com.habitrpg.android.habitica.ui.fragments.setup.IntroFragment; -import com.viewpagerindicator.IconPageIndicator; -import com.viewpagerindicator.IconPagerAdapter; - -import javax.inject.Inject; - -import butterknife.BindView; - -public class IntroActivity extends BaseActivity implements View.OnClickListener, ViewPager.OnPageChangeListener { - - @Inject - public ApiClient apiClient; - @BindView(R.id.viewPager) - ViewPager pager; - @BindView(R.id.view_pager_indicator) - IconPageIndicator indicator; - @BindView(R.id.skipButton) - Button skipButton; - @BindView(R.id.finishButton) - Button finishButton; - - @Override - protected int getLayoutResId() { - return R.layout.activity_intro; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - HabiticaBaseApplication.getComponent().inject(this); - - setupIntro(); - indicator.setViewPager(pager); - - this.skipButton.setOnClickListener(this); - this.finishButton.setOnClickListener(this); - - apiClient.getContent() - .subscribe(contentResult -> {}, RxErrorHandler.handleEmptyError()); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - Window window = getWindow(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - window.setStatusBarColor(ContextCompat.getColor(this, R.color.black_20_alpha)); - } - getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); - } - - } - - @Override - protected void injectActivity(AppComponent component) { - component.inject(this); - } - - private void setupIntro() { - android.support.v4.app.FragmentManager fragmentManager = getSupportFragmentManager(); - - pager.setAdapter(new PagerAdapter(fragmentManager)); - - pager.addOnPageChangeListener(this); - } - - @Override - public void onClick(View v) { - finishIntro(); - } - - private void finishIntro() { - Intent intent = new Intent(this, LoginActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); - - this.startActivity(intent); - overridePendingTransition(0, R.anim.activity_fade_out); - finish(); - } - - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - } - - @Override - public void onPageSelected(int position) { - if (position == 2) { - this.finishButton.setVisibility(View.VISIBLE); - } else { - this.finishButton.setVisibility(View.GONE); - } - } - - @Override - public void onPageScrollStateChanged(int state) { - - } - - private class PagerAdapter extends FragmentPagerAdapter implements IconPagerAdapter { - public PagerAdapter(FragmentManager fm) { - super(fm); - } - - @Override - public Fragment getItem(int position) { - IntroFragment fragment = new IntroFragment(); - - switch (position) { - case 0: { - fragment.setImage(ResourcesCompat.getDrawable(getResources(), R.drawable.intro_1, null)); - fragment.setSubtitle(getString(R.string.intro_1_subtitle)); - fragment.setTitleImage(ResourcesCompat.getDrawable(getResources(), R.drawable.intro_1_title, null)); - fragment.setDescription(getString(R.string.intro_1_description, getString(R.string.habitica_user_count))); - fragment.setBackgroundColor(ContextCompat.getColor(IntroActivity.this, R.color.brand_300)); - break; - } - case 1: { - fragment.setImage(ResourcesCompat.getDrawable(getResources(), R.drawable.intro_2, null)); - fragment.setSubtitle(getString(R.string.intro_2_subtitle)); - fragment.setTitle(getString(R.string.intro_2_title)); - fragment.setDescription(getString(R.string.intro_2_description)); - fragment.setBackgroundColor(ContextCompat.getColor(IntroActivity.this, R.color.blue_10)); - break; - } - case 2: { - fragment.setImage(ResourcesCompat.getDrawable(getResources(), R.drawable.intro_3, null)); - fragment.setSubtitle(getString(R.string.intro_3_subtitle)); - fragment.setTitle(getString(R.string.intro_3_title)); - fragment.setDescription(getString(R.string.intro_3_description)); - fragment.setBackgroundColor(ContextCompat.getColor(IntroActivity.this, R.color.red_100)); - break; - } - } - - return fragment; - } - - @Override - public int getIconResId(int index) { - return R.drawable.indicator_diamond; - } - - @Override - public int getCount() { - return 3; - } - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/IntroActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/IntroActivity.kt new file mode 100644 index 000000000..86b5711f3 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/IntroActivity.kt @@ -0,0 +1,142 @@ +package com.habitrpg.android.habitica.ui.activities + +import android.content.Intent +import android.os.Build +import android.os.Bundle +import android.support.v4.app.Fragment +import android.support.v4.app.FragmentManager +import android.support.v4.app.FragmentPagerAdapter +import android.support.v4.content.ContextCompat +import android.support.v4.content.res.ResourcesCompat +import android.support.v4.view.ViewPager +import android.view.View +import android.view.WindowManager +import android.widget.Button +import com.habitrpg.android.habitica.HabiticaBaseApplication +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.components.AppComponent +import com.habitrpg.android.habitica.data.ContentRepository +import com.habitrpg.android.habitica.data.InventoryRepository +import com.habitrpg.android.habitica.helpers.RxErrorHandler +import com.habitrpg.android.habitica.ui.fragments.setup.IntroFragment +import com.habitrpg.android.habitica.ui.helpers.bindView +import com.viewpagerindicator.IconPageIndicator +import com.viewpagerindicator.IconPagerAdapter +import io.reactivex.functions.Consumer +import javax.inject.Inject + +class IntroActivity : BaseActivity(), View.OnClickListener, ViewPager.OnPageChangeListener { + + @Inject + lateinit var contentRepository: InventoryRepository + + private val pager: ViewPager by bindView(R.id.viewPager) + private val indicator: IconPageIndicator by bindView(R.id.view_pager_indicator) + private val skipButton: Button by bindView(R.id.skipButton) + private val finishButton: Button by bindView(R.id.finishButton) + + override fun getLayoutResId(): Int { + return R.layout.activity_intro + } + + public override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setupIntro() + indicator.setViewPager(pager) + + this.skipButton.setOnClickListener(this) + this.finishButton.setOnClickListener(this) + + contentRepository.retrieveContent().subscribe(Consumer { }, RxErrorHandler.handleEmptyError()) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + val window = window + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + window.statusBarColor = ContextCompat.getColor(this, R.color.black_20_alpha) + } + getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) + } + + } + + override fun injectActivity(component: AppComponent?) { + component?.inject(this) + } + + private fun setupIntro() { + val fragmentManager = supportFragmentManager + + pager.adapter = PagerAdapter(fragmentManager) + + pager.addOnPageChangeListener(this) + } + + override fun onClick(v: View) { + finishIntro() + } + + private fun finishIntro() { + val intent = Intent(this, LoginActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) + + this.startActivity(intent) + overridePendingTransition(0, R.anim.activity_fade_out) + finish() + } + + override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {} + + override fun onPageSelected(position: Int) { + if (position == 2) { + this.finishButton.visibility = View.VISIBLE + } else { + this.finishButton.visibility = View.GONE + } + } + + override fun onPageScrollStateChanged(state: Int) { + + } + + private inner class PagerAdapter(fm: FragmentManager) : FragmentPagerAdapter(fm), IconPagerAdapter { + + override fun getItem(position: Int): Fragment { + val fragment = IntroFragment() + + when (position) { + 0 -> { + fragment.setImage(ResourcesCompat.getDrawable(resources, R.drawable.intro_1, null)) + fragment.setSubtitle(getString(R.string.intro_1_subtitle)) + fragment.setTitleImage(ResourcesCompat.getDrawable(resources, R.drawable.intro_1_title, null)) + fragment.setDescription(getString(R.string.intro_1_description, getString(R.string.habitica_user_count))) + fragment.setBackgroundColor(ContextCompat.getColor(this@IntroActivity, R.color.brand_300)) + } + 1 -> { + fragment.setImage(ResourcesCompat.getDrawable(resources, R.drawable.intro_2, null)) + fragment.setSubtitle(getString(R.string.intro_2_subtitle)) + fragment.setTitle(getString(R.string.intro_2_title)) + fragment.setDescription(getString(R.string.intro_2_description)) + fragment.setBackgroundColor(ContextCompat.getColor(this@IntroActivity, R.color.blue_10)) + } + 2 -> { + fragment.setImage(ResourcesCompat.getDrawable(resources, R.drawable.intro_3, null)) + fragment.setSubtitle(getString(R.string.intro_3_subtitle)) + fragment.setTitle(getString(R.string.intro_3_title)) + fragment.setDescription(getString(R.string.intro_3_description)) + fragment.setBackgroundColor(ContextCompat.getColor(this@IntroActivity, R.color.red_100)) + } + } + + return fragment + } + + override fun getIconResId(index: Int): Int { + return R.drawable.indicator_diamond + } + + override fun getCount(): Int { + return 3 + } + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.java deleted file mode 100644 index 9f721e69b..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.java +++ /dev/null @@ -1,677 +0,0 @@ -package com.habitrpg.android.habitica.ui.activities; - -import android.accounts.AccountManager; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; -import android.app.Dialog; -import android.content.ActivityNotFoundException; -import android.content.Intent; -import android.content.SharedPreferences; -import android.graphics.Color; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.support.design.widget.Snackbar; -import android.support.v4.content.ContextCompat; -import android.support.v7.app.AlertDialog; -import android.support.v7.preference.PreferenceManager; -import android.text.InputType; -import android.text.SpannableString; -import android.text.style.UnderlineSpan; -import android.util.Log; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.view.Window; -import android.view.WindowManager; -import android.view.inputmethod.EditorInfo; -import android.widget.Button; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.ProgressBar; -import android.widget.ScrollView; - -import com.facebook.AccessToken; -import com.facebook.CallbackManager; -import com.facebook.FacebookCallback; -import com.facebook.FacebookException; -import com.facebook.FacebookSdk; -import com.facebook.login.LoginManager; -import com.facebook.login.LoginResult; -import com.google.android.gms.auth.GoogleAuthException; -import com.google.android.gms.auth.GoogleAuthUtil; -import com.google.android.gms.auth.GooglePlayServicesAvailabilityException; -import com.google.android.gms.auth.UserRecoverableAuthException; -import com.google.android.gms.common.AccountPicker; -import com.google.android.gms.common.ConnectionResult; -import com.google.android.gms.common.GoogleApiAvailability; -import com.google.android.gms.common.GooglePlayServicesUtil; -import com.google.android.gms.common.Scopes; -import com.habitrpg.android.habitica.BuildConfig; -import com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.api.HostConfig; -import com.habitrpg.android.habitica.components.AppComponent; -import com.habitrpg.android.habitica.data.ApiClient; -import com.habitrpg.android.habitica.data.UserRepository; -import com.habitrpg.android.habitica.helpers.AmplitudeManager; -import com.habitrpg.android.habitica.helpers.RxErrorHandler; -import com.habitrpg.android.habitica.models.auth.UserAuthResponse; -import com.habitrpg.android.habitica.prefs.scanner.IntentIntegrator; -import com.habitrpg.android.habitica.prefs.scanner.IntentResult; -import com.habitrpg.android.habitica.ui.helpers.UiUtils; -import com.habitrpg.android.habitica.ui.views.login.LockableScrollView; -import com.habitrpg.android.habitica.ui.views.login.LoginBackgroundView; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import javax.inject.Inject; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import io.reactivex.Flowable; -import io.reactivex.exceptions.Exceptions; -import io.reactivex.functions.Consumer; -import io.reactivex.schedulers.Schedulers; - -import static com.habitrpg.android.habitica.R.id.password; - -/** - * @author Mickael Goubin - */ -public class LoginActivity extends BaseActivity - implements Consumer { - static final int REQUEST_CODE_PICK_ACCOUNT = 1000; - private final static String TAG_ADDRESS = "address"; - private final static String TAG_USERID = "user"; - private final static String TAG_APIKEY = "key"; - private static final int REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR = 1001; - private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 9000; - - @Inject - public ApiClient apiClient; - @Inject - public SharedPreferences sharedPrefs; - @Inject - public HostConfig hostConfig; - @Inject - UserRepository userRepository; - public String mTmpUserToken; - public String mTmpApiToken; - public Boolean isRegistering; - Boolean isShowingForm = false; - - @BindView(R.id.background_container) - LockableScrollView backgroundContainer; - @BindView(R.id.background_view) - LoginBackgroundView backgroundView; - @BindView(R.id.new_game_button) - Button newGameButton; - @BindView(R.id.show_login_button) - Button showLoginButton; - @BindView(R.id.login_scrollview) - ScrollView scrollView; - @BindView(R.id.login_linear_layout) - LinearLayout formWrapper; - @BindView(R.id.back_button) - Button backButton; - @BindView(R.id.logo_view) - ImageView logoView; - - @BindView(R.id.login_btn) - Button mLoginNormalBtn; - @BindView(R.id.PB_AsyncTask) - ProgressBar mProgressBar; - @BindView(R.id.username) - EditText mUsernameET; - @BindView(password) - EditText mPasswordET; - @BindView(R.id.email) - EditText mEmail; - @BindView(R.id.confirm_password) - EditText mConfirmPassword; - @BindView(R.id.forgot_password) - Button forgotPasswordButton; - private CallbackManager callbackManager; - private String googleEmail; - private LoginManager loginManager; - - @Override - protected int getLayoutResId() { - getWindow().requestFeature(Window.FEATURE_ACTION_BAR); - return R.layout.activity_login; - } - - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (getSupportActionBar() != null) { - getSupportActionBar().hide(); - } - //Set default values to avoid null-responses when requesting unedited settings - PreferenceManager.setDefaultValues(this, R.xml.preferences_fragment, false); - - ButterKnife.bind(this); - - setupFacebookLogin(); - - mLoginNormalBtn.setOnClickListener(mLoginNormalClick); - - SpannableString content = new SpannableString(forgotPasswordButton.getText()); - content.setSpan(new UnderlineSpan(), 0, content.length(), 0); - forgotPasswordButton.setText(content); - - callbackManager = CallbackManager.Factory.create(); - - - this.isRegistering = true; - - Map additionalData = new HashMap<>(); - additionalData.put("page", this.getClass().getSimpleName()); - AmplitudeManager.sendEvent("navigate", AmplitudeManager.EVENT_CATEGORY_NAVIGATION, AmplitudeManager.EVENT_HITTYPE_PAGEVIEW, additionalData); - - backgroundContainer.post(() -> backgroundContainer.scrollTo(0, backgroundContainer.getBottom())); - backgroundContainer.setScrollingEnabled(false); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - Window window = getWindow(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - window.setStatusBarColor(ContextCompat.getColor(this, R.color.black_20_alpha)); - } - getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); - } - } - - private void setupFacebookLogin() { - callbackManager = CallbackManager.Factory.create(); - loginManager = LoginManager.getInstance(); - loginManager.registerCallback(callbackManager, - new FacebookCallback() { - @Override - public void onSuccess(LoginResult loginResult) { - Log.e("Login", "SUCCESS"); - AccessToken accessToken = AccessToken.getCurrentAccessToken(); - apiClient.connectSocial("facebook", accessToken.getUserId(), accessToken.getToken()) - .subscribe(LoginActivity.this, throwable -> hideProgress()); - } - - @Override - public void onCancel() { - Log.e("Login", "CANCEL"); - } - - @Override - public void onError(FacebookException exception) { - exception.printStackTrace(); - } - }); - - } - - @Override - public void onBackPressed() { - if (isShowingForm) { - hideForm(); - } else { - super.onBackPressed(); - } - } - - @Override - protected void injectActivity(AppComponent component) { - component.inject(this); - } - - private void resetLayout() { - if (this.isRegistering) { - if (this.mEmail.getVisibility() == View.GONE) { - show(this.mEmail); - } - if (this.mConfirmPassword.getVisibility() == View.GONE) { - show(this.mConfirmPassword); - } - } else { - if (this.mEmail.getVisibility() == View.VISIBLE) { - hide(this.mEmail); - } - if (this.mConfirmPassword.getVisibility() == View.VISIBLE) { - hide(this.mConfirmPassword); - } - } - } - - private View.OnClickListener mLoginNormalClick = new View.OnClickListener() { - @Override - public void onClick(View v) { - mProgressBar.setVisibility(View.VISIBLE); - if (isRegistering) { - String username, email, password, cpassword; - username = String.valueOf(mUsernameET.getText()).trim(); - email = String.valueOf(mEmail.getText()).trim(); - password = String.valueOf(mPasswordET.getText()); - cpassword = String.valueOf(mConfirmPassword.getText()); - if (username.length() == 0 || password.length() == 0 || email.length() == 0 || cpassword.length() == 0) { - showValidationError(R.string.login_validation_error_fieldsmissing); - return; - } - apiClient.registerUser(username, email, password, cpassword) - .subscribe(LoginActivity.this, throwable -> hideProgress()); - } else { - String username, password; - username = String.valueOf(mUsernameET.getText()).trim(); - password = String.valueOf(mPasswordET.getText()); - if (username.length() == 0 || password.length() == 0) { - showValidationError(R.string.login_validation_error_fieldsmissing); - return; - } - apiClient.connectUser(username, password) - .subscribe(LoginActivity.this, throwable -> hideProgress()); - } - } - }; - - private View.OnClickListener mForgotPWClick = v -> { - String url = BuildConfig.BASE_URL; - Intent i = new Intent(Intent.ACTION_VIEW); - i.setData(Uri.parse(url)); - startActivity(i); - }; - - - public static void show(final View v) { - v.setVisibility(View.VISIBLE); - } - - public static void hide(final View v) { - v.setVisibility(View.GONE); - } - - private void startMainActivity() { - Intent intent = new Intent(LoginActivity.this, MainActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); - finish(); - } - - private void startSetupActivity() { - Intent intent = new Intent(LoginActivity.this, SetupActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); - finish(); - } - - private void toggleRegistering() { - this.isRegistering = !this.isRegistering; - this.setRegistering(); - } - - private void setRegistering() { - if (this.isRegistering) { - this.mLoginNormalBtn.setText(getString(R.string.register_btn)); - mUsernameET.setHint(R.string.username); - mPasswordET.setImeOptions(EditorInfo.IME_ACTION_NEXT); - } else { - this.mLoginNormalBtn.setText(getString(R.string.login_btn)); - mUsernameET.setHint(R.string.email_username); - mPasswordET.setImeOptions(EditorInfo.IME_ACTION_DONE); - } - this.resetLayout(); - } - - public void onActivityResult(int requestCode, int resultCode, Intent intent) { - super.onActivityResult(requestCode, resultCode, intent); - callbackManager.onActivityResult(requestCode, resultCode, intent); - IntentResult scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent); - if (scanResult != null) { - try { - Log.d("scanresult", scanResult.getContents()); - this.parse(scanResult.getContents()); - } catch (Exception e) { - Log.e("scanresult", "Could not parse scanResult", e); - } - } - - if (requestCode == REQUEST_CODE_PICK_ACCOUNT) { - if (resultCode == RESULT_OK) { - googleEmail = intent.getStringExtra(AccountManager.KEY_ACCOUNT_NAME); - handleGoogleLoginResult(); - } - } - if (requestCode == REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR) { - handleGoogleLoginResult(); - } - - if (requestCode == FacebookSdk.getCallbackRequestCodeOffset()) { - //This is necessary because the regular login callback is not called for some reason - AccessToken accessToken = AccessToken.getCurrentAccessToken(); - if (accessToken != null && accessToken.getToken() != null) { - apiClient.connectSocial("facebook", accessToken.getUserId(), accessToken.getToken()) - .subscribe(LoginActivity.this, throwable -> hideProgress()); - } - } - } - - private void parse(String contents) { - String adr, user, key; - try { - JSONObject obj; - - obj = new JSONObject(contents); - adr = obj.getString(TAG_ADDRESS); - user = obj.getString(TAG_USERID); - key = obj.getString(TAG_APIKEY); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - SharedPreferences.Editor editor = prefs.edit(); - boolean ans = editor.putString(getString(R.string.SP_address), adr) - .putString(getString(R.string.SP_APIToken), key) - .putString(getString(R.string.SP_userID), user) - .commit(); - if (!ans) { - throw new Exception("PB_string_commit"); - } - startMainActivity(); - } catch (JSONException e) { - showSnackbar(getString(R.string.ERR_pb_barcode)); - e.printStackTrace(); - } catch (Exception e) { - if ("PB_string_commit".equals(e.getMessage())) { - showSnackbar(getString(R.string.ERR_pb_barcode)); - } - } - } - - private void showSnackbar(String content) { - Snackbar snackbar = Snackbar - .make(this.findViewById(R.id.login_linear_layout), content, Snackbar.LENGTH_LONG); - - View snackbarView = snackbar.getView(); - snackbarView.setBackgroundColor(Color.RED);//change Snackbar's background color; - snackbar.show(); // Don’t forget to show! - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_toggleRegistering: - toggleRegistering(); - break; - } - return super.onOptionsItemSelected(item); - } - - private void saveTokens(String api, String user) throws Exception { - this.apiClient.updateAuthenticationCredentials(user, api); - SharedPreferences.Editor editor = sharedPrefs.edit(); - boolean ans = editor.putString(getString(R.string.SP_APIToken), api) - .putString(getString(R.string.SP_userID), user) - .commit(); - if (!ans) { - throw new Exception("PB_string_commit"); - } - } - - private void hideProgress() { - runOnUiThread(() -> { - if (mProgressBar != null) { - mProgressBar.setVisibility(View.GONE); - } - }); - } - - private void showValidationError(int resourceMessageString) { - mProgressBar.setVisibility(View.GONE); - new android.support.v7.app.AlertDialog.Builder(this) - .setTitle(R.string.login_validation_error_title) - .setMessage(resourceMessageString) - .setNeutralButton(android.R.string.ok, (dialog, which) -> { - }) - .setIcon(R.drawable.ic_warning_black) - .show(); - } - - @Override - public void accept(UserAuthResponse userAuthResponse) { - try { - saveTokens(userAuthResponse.getToken(), userAuthResponse.getId()); - } catch (Exception e) { - e.printStackTrace(); - } - - userRepository.retrieveUser(true) - .subscribe(user -> { - if (userAuthResponse.getNewUser()) { - this.startSetupActivity(); - } else { - AmplitudeManager.sendEvent("login", AmplitudeManager.EVENT_CATEGORY_BEHAVIOUR, AmplitudeManager.EVENT_HITTYPE_EVENT); - this.startMainActivity(); - } - }, RxErrorHandler.handleEmptyError()); - } - - @OnClick(R.id.fb_login_button) - public void handleFacebookLogin() { - loginManager.logInWithReadPermissions(this, Collections.singletonList("user_friends")); - } - - @OnClick(R.id.google_login_button) - public void handleGoogleLogin() { - if (!checkPlayServices()) { - return; - } - String[] accountTypes = new String[]{"com.google"}; - Intent intent = AccountPicker.newChooseAccountIntent(null, null, - accountTypes, false, null, null, null, null); - try { - startActivityForResult(intent, REQUEST_CODE_PICK_ACCOUNT); - } catch (ActivityNotFoundException e) { - Dialog dialog = new AlertDialog.Builder(this) - .setTitle(R.string.authentication_error_title) - .setMessage(R.string.google_services_missing) - .setNegativeButton(R.string.close, (dialogInterface, i) -> dialogInterface.dismiss()) - .create(); - dialog.show(); - } - } - - private void handleGoogleLoginResult() { - String scopesString = Scopes.PROFILE + " " + Scopes.EMAIL; - String scopes = "oauth2:" + scopesString; - Flowable.defer(() -> { - try { - return Flowable.just(GoogleAuthUtil.getToken(LoginActivity.this, googleEmail, scopes)); - } catch (IOException | GoogleAuthException e) { - throw Exceptions.propagate(e); - } - }) - .subscribeOn(Schedulers.io()) - .flatMap(token -> apiClient.connectSocial("google", googleEmail, token)) - .subscribe(LoginActivity.this, throwable -> { - throwable.printStackTrace(); - hideProgress(); - if (throwable.getCause() != null && GoogleAuthException.class.isAssignableFrom(throwable.getCause().getClass())) { - handleGoogleAuthException((GoogleAuthException) throwable.getCause()); - } - }); - } - - private void handleGoogleAuthException(final Exception e) { - if (e instanceof GooglePlayServicesAvailabilityException) { - // The Google Play services APK is old, disabled, or not present. - // Show a dialog created by Google Play services that allows - // the user to update the APK - int statusCode = ((GooglePlayServicesAvailabilityException) e) - .getConnectionStatusCode(); - GoogleApiAvailability googleAPI = GoogleApiAvailability.getInstance(); - Dialog dialog = GooglePlayServicesUtil.getErrorDialog(statusCode, - LoginActivity.this, - REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR); - dialog.show(); - } else if (e instanceof UserRecoverableAuthException) { - // Unable to authenticate, such as when the user has not yet granted - // the app access to the account, but the user can fix this. - // Forward the user to an activity in Google Play services. - Intent intent = ((UserRecoverableAuthException) e).getIntent(); - startActivityForResult(intent, REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR); - } - } - - private boolean checkPlayServices() { - GoogleApiAvailability googleAPI = GoogleApiAvailability.getInstance(); - int result = googleAPI.isGooglePlayServicesAvailable(this); - if(result != ConnectionResult.SUCCESS) { - if(googleAPI.isUserResolvableError(result)) { - googleAPI.getErrorDialog(this, result, PLAY_SERVICES_RESOLUTION_REQUEST).show(); - } - return false; - } - - return true; - } - - @OnClick(R.id.new_game_button) - void newGameButtonClicked() { - isRegistering = true; - showForm(); - setRegistering(); - } - - @OnClick(R.id.show_login_button) - void showLoginButtonClicked() { - isRegistering = false; - showForm(); - setRegistering(); - } - - @OnClick(R.id.back_button) - void backButtonClicked() { - if (isShowingForm) { - hideForm(); - } - } - - - private void showForm() { - isShowingForm = true; - ValueAnimator panAnimation = ObjectAnimator.ofInt(backgroundContainer, "scrollY", 0).setDuration(1000); - ValueAnimator newGameAlphaAnimation = ObjectAnimator.ofFloat(newGameButton, View.ALPHA, 0); - ValueAnimator showLoginAlphaAnimation = ObjectAnimator.ofFloat(showLoginButton, View.ALPHA, 0); - ValueAnimator scaleLogoAnimation = ValueAnimator.ofInt(logoView.getMeasuredHeight(), (int)(logoView.getMeasuredHeight()*0.75)); - scaleLogoAnimation.addUpdateListener(valueAnimator -> { - int val = (Integer) valueAnimator.getAnimatedValue(); - ViewGroup.LayoutParams layoutParams = logoView.getLayoutParams(); - layoutParams.height = val; - logoView.setLayoutParams(layoutParams); - }); - if (isRegistering) { - newGameAlphaAnimation.setStartDelay(600); - newGameAlphaAnimation.setDuration(400); - showLoginAlphaAnimation.setDuration(400); - newGameAlphaAnimation.addListener(new AnimatorListenerAdapter() { - public void onAnimationEnd(Animator animation) { - newGameButton.setVisibility(View.GONE); - showLoginButton.setVisibility(View.GONE); - scrollView.setVisibility(View.VISIBLE); - scrollView.setAlpha(1); - } - }); - } else { - showLoginAlphaAnimation.setStartDelay(600); - showLoginAlphaAnimation.setDuration(400); - newGameAlphaAnimation.setDuration(400); - showLoginAlphaAnimation.addListener(new AnimatorListenerAdapter() { - public void onAnimationEnd(Animator animation) { - newGameButton.setVisibility(View.GONE); - showLoginButton.setVisibility(View.GONE); - scrollView.setVisibility(View.VISIBLE); - scrollView.setAlpha(1); - } - }); - } - ValueAnimator backAlphaAnimation = ObjectAnimator.ofFloat(backButton, View.ALPHA, 1).setDuration(800); - AnimatorSet showAnimation = new AnimatorSet(); - showAnimation.playTogether(panAnimation, newGameAlphaAnimation, showLoginAlphaAnimation, scaleLogoAnimation); - showAnimation.play(backAlphaAnimation).after(panAnimation); - for (int i = 0; i < formWrapper.getChildCount(); i++) { - View view = formWrapper.getChildAt(i); - view.setAlpha(0); - ValueAnimator animator = ObjectAnimator.ofFloat(view, View.ALPHA, 1).setDuration(400); - animator.setStartDelay(100 * i); - showAnimation.play(animator).after(panAnimation); - } - - showAnimation.start(); - } - - private void hideForm() { - isShowingForm = false; - ValueAnimator panAnimation = ObjectAnimator.ofInt(backgroundContainer, "scrollY", backgroundContainer.getBottom()).setDuration(1000); - ValueAnimator newGameAlphaAnimation = ObjectAnimator.ofFloat(newGameButton, View.ALPHA, 1).setDuration(700); - ValueAnimator showLoginAlphaAnimation = ObjectAnimator.ofFloat(showLoginButton, View.ALPHA, 1).setDuration(700); - ValueAnimator scaleLogoAnimation = ValueAnimator.ofInt(logoView.getMeasuredHeight(), (int)(logoView.getMeasuredHeight()*1.333333)); - scaleLogoAnimation.addUpdateListener(valueAnimator -> { - int val = (Integer) valueAnimator.getAnimatedValue(); - ViewGroup.LayoutParams layoutParams = logoView.getLayoutParams(); - layoutParams.height = val; - logoView.setLayoutParams(layoutParams); - }); - showLoginAlphaAnimation.setStartDelay(300); - ValueAnimator scrollViewAlphaAnimation = ObjectAnimator.ofFloat(scrollView, View.ALPHA, 0).setDuration(800); - scrollViewAlphaAnimation.addListener(new AnimatorListenerAdapter() { - public void onAnimationEnd(Animator animation) { - newGameButton.setVisibility(View.VISIBLE); - showLoginButton.setVisibility(View.VISIBLE); - scrollView.setVisibility(View.INVISIBLE); - } - }); - ValueAnimator backAlphaAnimation = ObjectAnimator.ofFloat(backButton, View.ALPHA, 0).setDuration(800); - AnimatorSet showAnimation = new AnimatorSet(); - showAnimation.playTogether(panAnimation, scrollViewAlphaAnimation, backAlphaAnimation, scaleLogoAnimation); - showAnimation.play(newGameAlphaAnimation).after(scrollViewAlphaAnimation); - showAnimation.play(showLoginAlphaAnimation).after(scrollViewAlphaAnimation); - showAnimation.start(); - UiUtils.dismissKeyboard(this); - } - - @OnClick(R.id.forgot_password) - public void onForgotPasswordClicked() { - final EditText input = new EditText(this); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - input.setAutofillHints(EditText.AUTOFILL_HINT_EMAIL_ADDRESS); - } - input.setInputType(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS); - LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.MATCH_PARENT); - input.setLayoutParams(lp); - AlertDialog.Builder alertDialog = new AlertDialog.Builder(this) - .setTitle(R.string.forgot_password_title) - .setMessage(R.string.forgot_password_description) - .setView(input) - .setPositiveButton(R.string.send, (dialog, which) -> { - dialog.dismiss(); - userRepository.sendPasswordResetEmail(input.getText().toString()).subscribe(aVoid -> { - showPasswordEmailConfirmation(); - }, RxErrorHandler.handleEmptyError()); - }).setNegativeButton(R.string.action_cancel, (dialog, which) -> { - dialog.dismiss(); - }); - - alertDialog.show(); - } - - private void showPasswordEmailConfirmation() { - new AlertDialog.Builder(this) - .setMessage(R.string.forgot_password_confirmation) - .setPositiveButton(R.string.ok, (dialog, which) -> dialog.dismiss()) - .show(); - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.kt new file mode 100644 index 000000000..899e98bd5 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.kt @@ -0,0 +1,608 @@ +package com.habitrpg.android.habitica.ui.activities + +import android.accounts.AccountManager +import android.animation.* +import android.app.Activity +import android.content.ActivityNotFoundException +import android.content.Intent +import android.content.SharedPreferences +import android.graphics.Color +import android.os.Build +import android.os.Bundle +import android.support.design.widget.Snackbar +import android.support.v4.content.ContextCompat +import android.support.v7.app.AlertDialog +import android.support.v7.preference.PreferenceManager +import android.text.InputType +import android.text.SpannableString +import android.text.style.UnderlineSpan +import android.util.Log +import android.view.MenuItem +import android.view.View +import android.view.Window +import android.view.WindowManager +import android.view.inputmethod.EditorInfo +import android.widget.* +import com.facebook.* +import com.facebook.login.LoginManager +import com.facebook.login.LoginResult +import com.google.android.gms.auth.GoogleAuthException +import com.google.android.gms.auth.GoogleAuthUtil +import com.google.android.gms.auth.GooglePlayServicesAvailabilityException +import com.google.android.gms.auth.UserRecoverableAuthException +import com.google.android.gms.common.* +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.api.HostConfig +import com.habitrpg.android.habitica.components.AppComponent +import com.habitrpg.android.habitica.data.ApiClient +import com.habitrpg.android.habitica.data.UserRepository +import com.habitrpg.android.habitica.extensions.notNull +import com.habitrpg.android.habitica.helpers.AmplitudeManager +import com.habitrpg.android.habitica.helpers.RxErrorHandler +import com.habitrpg.android.habitica.models.auth.UserAuthResponse +import com.habitrpg.android.habitica.prefs.scanner.IntentIntegrator +import com.habitrpg.android.habitica.ui.helpers.UiUtils +import com.habitrpg.android.habitica.ui.helpers.bindView +import com.habitrpg.android.habitica.ui.views.login.LockableScrollView +import com.habitrpg.android.habitica.ui.views.login.LoginBackgroundView +import io.reactivex.Flowable +import io.reactivex.exceptions.Exceptions +import io.reactivex.functions.Consumer +import io.reactivex.schedulers.Schedulers +import org.json.JSONException +import org.json.JSONObject +import java.io.IOException +import java.util.* +import javax.inject.Inject + +/** + * @author Mickael Goubin + */ +class LoginActivity : BaseActivity(), Consumer { + + @Inject + lateinit var apiClient: ApiClient + @Inject + lateinit var sharedPrefs: SharedPreferences + @Inject + lateinit var hostConfig: HostConfig + @Inject + internal lateinit var userRepository: UserRepository + + private var isRegistering: Boolean = false + private var isShowingForm: Boolean = false + + private val backgroundContainer: LockableScrollView by bindView(R.id.background_container) + internal val backgroundView: LoginBackgroundView by bindView(R.id.background_view) + internal val newGameButton: Button by bindView(R.id.new_game_button) + internal val showLoginButton: Button by bindView(R.id.show_login_button) + internal val scrollView: ScrollView by bindView(R.id.login_scrollview) + private val formWrapper: LinearLayout by bindView(R.id.login_linear_layout) + private val backButton: Button by bindView(R.id.back_button) + private val logoView: ImageView by bindView(R.id.logo_view) + private val mLoginNormalBtn: Button by bindView(R.id.login_btn) + private val mProgressBar: ProgressBar by bindView(R.id.PB_AsyncTask) + private val mUsernameET: EditText by bindView(R.id.username) + private val mPasswordET: EditText by bindView(R.id.password) + private val mEmail: EditText by bindView(R.id.email) + private val mConfirmPassword: EditText by bindView(R.id.confirm_password) + private val forgotPasswordButton: Button by bindView(R.id.forgot_password) + private val facebookLoginButton: Button by bindView(R.id.fb_login_button) + private val googleLoginButton: Button by bindView(R.id.google_login_button) + + private var callbackManager = CallbackManager.Factory.create() + private var googleEmail: String? = null + private var loginManager = LoginManager.getInstance() + + private val mLoginNormalClick = View.OnClickListener { + mProgressBar.visibility = View.VISIBLE + if (isRegistering) { + val username: String = mUsernameET.text.toString().trim { it <= ' ' } + val email: String = mEmail.text.toString().trim { it <= ' ' } + val password: String = mPasswordET.text.toString() + val confirmPassword: String = mConfirmPassword.text.toString() + if (username.isEmpty() || password.isEmpty() || email.isEmpty() || confirmPassword.isEmpty()) { + showValidationError(R.string.login_validation_error_fieldsmissing) + return@OnClickListener + } + apiClient.registerUser(username, email, password, confirmPassword) + .subscribe(this@LoginActivity, Consumer { hideProgress() }) + } else { + val username: String = mUsernameET.text.toString().trim { it <= ' ' } + val password: String = mPasswordET.text.toString() + if (username.isEmpty() || password.isEmpty()) { + showValidationError(R.string.login_validation_error_fieldsmissing) + return@OnClickListener + } + apiClient.connectUser(username, password) + .subscribe(this@LoginActivity, Consumer { hideProgress() }) + } + } + + override fun getLayoutResId(): Int { + window.requestFeature(Window.FEATURE_ACTION_BAR) + return R.layout.activity_login + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + supportActionBar?.hide() + //Set default values to avoid null-responses when requesting unedited settings + PreferenceManager.setDefaultValues(this, R.xml.preferences_fragment, false) + + setupFacebookLogin() + + mLoginNormalBtn.setOnClickListener(mLoginNormalClick) + + val content = SpannableString(forgotPasswordButton.text) + content.setSpan(UnderlineSpan(), 0, content.length, 0) + forgotPasswordButton.text = content + + this.isRegistering = true + + val additionalData = HashMap() + additionalData["page"] = this.javaClass.simpleName + AmplitudeManager.sendEvent("navigate", AmplitudeManager.EVENT_CATEGORY_NAVIGATION, AmplitudeManager.EVENT_HITTYPE_PAGEVIEW, additionalData) + + backgroundContainer.post { backgroundContainer.scrollTo(0, backgroundContainer.bottom) } + backgroundContainer.setScrollingEnabled(false) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + val window = window + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + window.statusBarColor = ContextCompat.getColor(this, R.color.black_20_alpha) + } + getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) + } + + newGameButton.setOnClickListener { newGameButtonClicked() } + showLoginButton.setOnClickListener { showLoginButtonClicked() } + backButton.setOnClickListener { backButtonClicked() } + forgotPasswordButton.setOnClickListener { onForgotPasswordClicked() } + facebookLoginButton.setOnClickListener { handleFacebookLogin() } + googleLoginButton.setOnClickListener { handleGoogleLogin() } + } + + private fun setupFacebookLogin() { + callbackManager = CallbackManager.Factory.create() + loginManager.registerCallback(callbackManager, + object : FacebookCallback { + override fun onSuccess(loginResult: LoginResult) { + Log.e("Login", "SUCCESS") + val accessToken = AccessToken.getCurrentAccessToken() + apiClient.connectSocial("facebook", accessToken.userId, accessToken.token) + .subscribe(this@LoginActivity, Consumer { hideProgress() }) + } + + override fun onCancel() { + Log.e("Login", "CANCEL") + } + + override fun onError(exception: FacebookException) { + exception.printStackTrace() + } + }) + + } + + override fun onBackPressed() { + if (isShowingForm) { + hideForm() + } else { + super.onBackPressed() + } + } + + override fun injectActivity(component: AppComponent?) { + component?.inject(this) + } + + private fun resetLayout() { + if (this.isRegistering) { + if (this.mEmail.visibility == View.GONE) { + show(this.mEmail) + } + if (this.mConfirmPassword.visibility == View.GONE) { + show(this.mConfirmPassword) + } + } else { + if (this.mEmail.visibility == View.VISIBLE) { + hide(this.mEmail) + } + if (this.mConfirmPassword.visibility == View.VISIBLE) { + hide(this.mConfirmPassword) + } + } + } + + private fun startMainActivity() { + val intent = Intent(this@LoginActivity, MainActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK) + startActivity(intent) + finish() + } + + private fun startSetupActivity() { + val intent = Intent(this@LoginActivity, SetupActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK) + startActivity(intent) + finish() + } + + private fun toggleRegistering() { + this.isRegistering = (!this.isRegistering) + this.setRegistering() + } + + private fun setRegistering() { + if (this.isRegistering) { + this.mLoginNormalBtn.text = getString(R.string.register_btn) + mUsernameET.setHint(R.string.username) + mPasswordET.imeOptions = EditorInfo.IME_ACTION_NEXT + } else { + this.mLoginNormalBtn.text = getString(R.string.login_btn) + mUsernameET.setHint(R.string.email_username) + mPasswordET.imeOptions = EditorInfo.IME_ACTION_DONE + } + this.resetLayout() + } + + public override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent) { + super.onActivityResult(requestCode, resultCode, intent) + callbackManager.onActivityResult(requestCode, resultCode, intent) + val scanResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, intent) + if (scanResult != null) { + try { + Log.d("scanresult", scanResult.contents) + this.parse(scanResult.contents) + } catch (e: Exception) { + Log.e("scanresult", "Could not parse scanResult", e) + } + + } + + if (requestCode == REQUEST_CODE_PICK_ACCOUNT) { + if (resultCode == Activity.RESULT_OK) { + googleEmail = intent.getStringExtra(AccountManager.KEY_ACCOUNT_NAME) + handleGoogleLoginResult() + } + } + if (requestCode == REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR) { + handleGoogleLoginResult() + } + + if (requestCode == FacebookSdk.getCallbackRequestCodeOffset()) { + //This is necessary because the regular login callback is not called for some reason + val accessToken = AccessToken.getCurrentAccessToken() + if (accessToken != null && accessToken.token != null) { + apiClient.connectSocial("facebook", accessToken.userId, accessToken.token) + .subscribe(this@LoginActivity, Consumer { hideProgress() }) + } + } + } + + private fun parse(contents: String) { + val adr: String + val user: String + val key: String + try { + val obj = JSONObject(contents) + + adr = obj.getString(TAG_ADDRESS) + user = obj.getString(TAG_USERID) + key = obj.getString(TAG_APIKEY) + val prefs = PreferenceManager.getDefaultSharedPreferences(this) + val editor = prefs.edit() + val ans = editor.putString(getString(R.string.SP_address), adr) + .putString(getString(R.string.SP_APIToken), key) + .putString(getString(R.string.SP_userID), user) + .commit() + if (!ans) { + throw Exception("PB_string_commit") + } + startMainActivity() + } catch (e: JSONException) { + showSnackbar(getString(R.string.ERR_pb_barcode)) + e.printStackTrace() + } catch (e: Exception) { + if ("PB_string_commit" == e.message) { + showSnackbar(getString(R.string.ERR_pb_barcode)) + } + } + + } + + private fun showSnackbar(content: String) { + val snackbar = Snackbar + .make(this.findViewById(R.id.login_linear_layout), content, Snackbar.LENGTH_LONG) + + val snackbarView = snackbar.view + snackbarView.setBackgroundColor(Color.RED)//change Snackbar's background color; + snackbar.show() // Don’t forget to show! + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.action_toggleRegistering -> toggleRegistering() + } + return super.onOptionsItemSelected(item) + } + + @Throws(Exception::class) + private fun saveTokens(api: String, user: String) { + this.apiClient.updateAuthenticationCredentials(user, api) + val editor = sharedPrefs.edit() + val ans = editor.putString(getString(R.string.SP_APIToken), api) + .putString(getString(R.string.SP_userID), user) + .commit() + if (!ans) { + throw Exception("PB_string_commit") + } + } + + private fun hideProgress() { + runOnUiThread { + mProgressBar.visibility = View.GONE + } + } + + private fun showValidationError(resourceMessageString: Int) { + mProgressBar.visibility = View.GONE + android.support.v7.app.AlertDialog.Builder(this) + .setTitle(R.string.login_validation_error_title) + .setMessage(resourceMessageString) + .setNeutralButton(android.R.string.ok) { _, _ -> } + .setIcon(R.drawable.ic_warning_black) + .show() + } + + override fun accept(userAuthResponse: UserAuthResponse) { + try { + saveTokens(userAuthResponse.token, userAuthResponse.id) + } catch (e: Exception) { + e.printStackTrace() + } + + userRepository.retrieveUser(true) + .subscribe(Consumer { + if (userAuthResponse.newUser) { + this.startSetupActivity() + } else { + AmplitudeManager.sendEvent("login", AmplitudeManager.EVENT_CATEGORY_BEHAVIOUR, AmplitudeManager.EVENT_HITTYPE_EVENT) + this.startMainActivity() + } + }, RxErrorHandler.handleEmptyError()) + } + + fun handleFacebookLogin() { + loginManager.logInWithReadPermissions(this, listOf("user_friends")) + } + + fun handleGoogleLogin() { + if (!checkPlayServices()) { + return + } + val accountTypes = arrayOf("com.google") + val intent = AccountPicker.newChooseAccountIntent(null, null, + accountTypes, false, null, null, null, null) + try { + startActivityForResult(intent, REQUEST_CODE_PICK_ACCOUNT) + } catch (e: ActivityNotFoundException) { + val dialog = AlertDialog.Builder(this) + .setTitle(R.string.authentication_error_title) + .setMessage(R.string.google_services_missing) + .setNegativeButton(R.string.close) { dialogInterface, _ -> dialogInterface.dismiss() } + .create() + dialog.show() + } + + } + + private fun handleGoogleLoginResult() { + val scopesString = Scopes.PROFILE + " " + Scopes.EMAIL + val scopes = "oauth2:$scopesString" + Flowable.defer { + try { + return@defer Flowable.just(GoogleAuthUtil.getToken(this, googleEmail, scopes)) + } catch (e: IOException) { + throw Exceptions.propagate(e) + } catch (e: GoogleAuthException) { + throw Exceptions.propagate(e) + } + } + .subscribeOn(Schedulers.io()) + .flatMap { token -> apiClient.connectSocial("google", googleEmail ?: "", token) } + .subscribe(this@LoginActivity, Consumer { throwable -> + throwable.printStackTrace() + hideProgress() + throwable.cause.notNull { + if (GoogleAuthException::class.java.isAssignableFrom(it.javaClass)) { + handleGoogleAuthException(throwable.cause as GoogleAuthException) + } + } + + }) + } + + private fun handleGoogleAuthException(e: Exception) { + if (e is GooglePlayServicesAvailabilityException) { + // The Google Play services APK is old, disabled, or not present. + // Show a dialog created by Google Play services that allows + // the user to update the APK + val statusCode = e + .connectionStatusCode + GoogleApiAvailability.getInstance() + val dialog = GooglePlayServicesUtil.getErrorDialog(statusCode, + this@LoginActivity, + REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR) + dialog.show() + } else if (e is UserRecoverableAuthException) { + // Unable to authenticate, such as when the user has not yet granted + // the app access to the account, but the user can fix this. + // Forward the user to an activity in Google Play services. + val intent = e.intent + startActivityForResult(intent, REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR) + } + } + + private fun checkPlayServices(): Boolean { + val googleAPI = GoogleApiAvailability.getInstance() + val result = googleAPI.isGooglePlayServicesAvailable(this) + if (result != ConnectionResult.SUCCESS) { + if (googleAPI.isUserResolvableError(result)) { + googleAPI.getErrorDialog(this, result, PLAY_SERVICES_RESOLUTION_REQUEST).show() + } + return false + } + + return true + } + + private fun newGameButtonClicked() { + isRegistering = true + showForm() + setRegistering() + } + + private fun showLoginButtonClicked() { + isRegistering = false + showForm() + setRegistering() + } + + private fun backButtonClicked() { + if (isShowingForm) { + hideForm() + } + } + + + private fun showForm() { + isShowingForm = true + val panAnimation = ObjectAnimator.ofInt(backgroundContainer, "scrollY", 0).setDuration(1000) + val newGameAlphaAnimation = ObjectAnimator.ofFloat(newGameButton, View.ALPHA, 0.toFloat()) + val showLoginAlphaAnimation = ObjectAnimator.ofFloat(showLoginButton, View.ALPHA, 0.toFloat()) + val scaleLogoAnimation = ValueAnimator.ofInt(logoView.measuredHeight, (logoView.measuredHeight * 0.75).toInt()) + scaleLogoAnimation.addUpdateListener { valueAnimator -> + val `val` = valueAnimator.animatedValue as Int + val layoutParams = logoView.layoutParams + layoutParams.height = `val` + logoView.layoutParams = layoutParams + } + if (isRegistering) { + newGameAlphaAnimation.startDelay = 600 + newGameAlphaAnimation.duration = 400 + showLoginAlphaAnimation.duration = 400 + newGameAlphaAnimation.addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + newGameButton.visibility = View.GONE + showLoginButton.visibility = View.GONE + scrollView.visibility = View.VISIBLE + scrollView.alpha = 1f + } + }) + } else { + showLoginAlphaAnimation.startDelay = 600 + showLoginAlphaAnimation.duration = 400 + newGameAlphaAnimation.duration = 400 + showLoginAlphaAnimation.addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + newGameButton.visibility = View.GONE + showLoginButton.visibility = View.GONE + scrollView.visibility = View.VISIBLE + scrollView.alpha = 1f + } + }) + } + val backAlphaAnimation = ObjectAnimator.ofFloat(backButton, View.ALPHA, 1.toFloat()).setDuration(800) + val showAnimation = AnimatorSet() + showAnimation.playTogether(panAnimation, newGameAlphaAnimation, showLoginAlphaAnimation, scaleLogoAnimation) + showAnimation.play(backAlphaAnimation).after(panAnimation) + for (i in 0 until formWrapper.childCount) { + val view = formWrapper.getChildAt(i) + view.alpha = 0f + val animator = ObjectAnimator.ofFloat(view, View.ALPHA, 1.toFloat()).setDuration(400) + animator.startDelay = (100 * i).toLong() + showAnimation.play(animator).after(panAnimation) + } + + showAnimation.start() + } + + private fun hideForm() { + isShowingForm = false + val panAnimation = ObjectAnimator.ofInt(backgroundContainer, "scrollY", backgroundContainer.bottom).setDuration(1000) + val newGameAlphaAnimation = ObjectAnimator.ofFloat(newGameButton, View.ALPHA, 1.toFloat()).setDuration(700) + val showLoginAlphaAnimation = ObjectAnimator.ofFloat(showLoginButton, View.ALPHA, 1.toFloat()).setDuration(700) + val scaleLogoAnimation = ValueAnimator.ofInt(logoView.measuredHeight, (logoView.measuredHeight * 1.333333).toInt()) + scaleLogoAnimation.addUpdateListener { valueAnimator -> + val value = valueAnimator.animatedValue as Int + val layoutParams = logoView.layoutParams + layoutParams.height = value + logoView.layoutParams = layoutParams + } + showLoginAlphaAnimation.startDelay = 300 + val scrollViewAlphaAnimation = ObjectAnimator.ofFloat(scrollView, View.ALPHA, 0.toFloat()).setDuration(800) + scrollViewAlphaAnimation.addListener(object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + newGameButton.visibility = View.VISIBLE + showLoginButton.visibility = View.VISIBLE + scrollView.visibility = View.INVISIBLE + } + }) + val backAlphaAnimation = ObjectAnimator.ofFloat(backButton, View.ALPHA, 0.toFloat()).setDuration(800) + val showAnimation = AnimatorSet() + showAnimation.playTogether(panAnimation, scrollViewAlphaAnimation, backAlphaAnimation, scaleLogoAnimation) + showAnimation.play(newGameAlphaAnimation).after(scrollViewAlphaAnimation) + showAnimation.play(showLoginAlphaAnimation).after(scrollViewAlphaAnimation) + showAnimation.start() + UiUtils.dismissKeyboard(this) + } + + fun onForgotPasswordClicked() { + val input = EditText(this) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + input.setAutofillHints(EditText.AUTOFILL_HINT_EMAIL_ADDRESS) + } + input.inputType = InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS + val lp = LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT) + input.layoutParams = lp + val alertDialog = AlertDialog.Builder(this) + .setTitle(R.string.forgot_password_title) + .setMessage(R.string.forgot_password_description) + .setView(input) + .setPositiveButton(R.string.send) { dialog, _ -> + dialog.dismiss() + userRepository.sendPasswordResetEmail(input.text.toString()).subscribe(Consumer { showPasswordEmailConfirmation() }, RxErrorHandler.handleEmptyError()) + }.setNegativeButton(R.string.action_cancel) { dialog, _ -> dialog.dismiss() } + + alertDialog.show() + } + + private fun showPasswordEmailConfirmation() { + AlertDialog.Builder(this) + .setMessage(R.string.forgot_password_confirmation) + .setPositiveButton(R.string.ok) { dialog, _ -> dialog.dismiss() } + .show() + } + + companion object { + internal const val REQUEST_CODE_PICK_ACCOUNT = 1000 + private const val TAG_ADDRESS = "address" + private const val TAG_USERID = "user" + private const val TAG_APIKEY = "key" + private const val REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR = 1001 + private const val PLAY_SERVICES_RESOLUTION_REQUEST = 9000 + + + fun show(v: View) { + v.visibility = View.VISIBLE + } + + fun hide(v: View) { + v.visibility = View.GONE + } + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.java deleted file mode 100644 index 0744f5f03..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.java +++ /dev/null @@ -1,1039 +0,0 @@ -package com.habitrpg.android.habitica.ui.activities; - -import android.annotation.SuppressLint; -import android.appwidget.AppWidgetManager; -import android.content.ComponentName; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.res.Configuration; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.drawable.BitmapDrawable; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.support.annotation.Nullable; -import android.support.design.widget.AppBarLayout; -import android.support.design.widget.CollapsingToolbarLayout; -import android.support.design.widget.TabLayout; -import android.support.v4.app.FragmentTransaction; -import android.support.v4.content.ContextCompat; -import android.support.v4.content.FileProvider; -import android.support.v4.widget.DrawerLayout; -import android.support.v7.app.ActionBarDrawerToggle; -import android.support.v7.app.AlertDialog; -import android.support.v7.widget.Toolbar; -import android.util.TypedValue; -import android.view.Gravity; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.view.Window; -import android.view.WindowManager; -import android.widget.FrameLayout; -import android.widget.TextView; - -import com.facebook.drawee.view.SimpleDraweeView; -import com.habitrpg.android.habitica.HabiticaApplication; -import com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.api.HostConfig; -import com.habitrpg.android.habitica.api.MaintenanceApiService; -import com.habitrpg.android.habitica.components.AppComponent; -import com.habitrpg.android.habitica.data.ApiClient; -import com.habitrpg.android.habitica.data.InventoryRepository; -import com.habitrpg.android.habitica.data.TagRepository; -import com.habitrpg.android.habitica.data.TaskRepository; -import com.habitrpg.android.habitica.data.UserRepository; -import com.habitrpg.android.habitica.events.DisplayFragmentEvent; -import com.habitrpg.android.habitica.events.DisplayTutorialEvent; -import com.habitrpg.android.habitica.events.HabitScoreEvent; -import com.habitrpg.android.habitica.events.OpenMysteryItemEvent; -import com.habitrpg.android.habitica.events.SelectClassEvent; -import com.habitrpg.android.habitica.events.ShareEvent; -import com.habitrpg.android.habitica.events.ShowCheckinDialog; -import com.habitrpg.android.habitica.events.ShowSnackbarEvent; -import com.habitrpg.android.habitica.events.commands.BuyRewardCommand; -import com.habitrpg.android.habitica.events.commands.ChecklistCheckedCommand; -import com.habitrpg.android.habitica.events.commands.FeedCommand; -import com.habitrpg.android.habitica.events.commands.HatchingCommand; -import com.habitrpg.android.habitica.events.commands.OpenGemPurchaseFragmentCommand; -import com.habitrpg.android.habitica.events.commands.OpenMenuItemCommand; -import com.habitrpg.android.habitica.events.commands.TaskCheckedCommand; -import com.habitrpg.android.habitica.helpers.AmplitudeManager; -import com.habitrpg.android.habitica.helpers.LanguageHelper; -import com.habitrpg.android.habitica.helpers.RemoteConfigManager; -import com.habitrpg.android.habitica.helpers.RxErrorHandler; -import com.habitrpg.android.habitica.helpers.SoundManager; -import com.habitrpg.android.habitica.helpers.TaskAlarmManager; -import com.habitrpg.android.habitica.helpers.notifications.PushNotificationManager; -import com.habitrpg.android.habitica.interactors.BuyRewardUseCase; -import com.habitrpg.android.habitica.interactors.CheckClassSelectionUseCase; -import com.habitrpg.android.habitica.interactors.ChecklistCheckUseCase; -import com.habitrpg.android.habitica.interactors.DailyCheckUseCase; -import com.habitrpg.android.habitica.interactors.DisplayItemDropUseCase; -import com.habitrpg.android.habitica.interactors.HabitScoreUseCase; -import com.habitrpg.android.habitica.interactors.NotifyUserUseCase; -import com.habitrpg.android.habitica.interactors.TodoCheckUseCase; -import com.habitrpg.android.habitica.models.TutorialStep; -import com.habitrpg.android.habitica.models.inventory.Pet; -import com.habitrpg.android.habitica.models.responses.MaintenanceResponse; -import com.habitrpg.android.habitica.models.responses.TaskScoringResult; -import com.habitrpg.android.habitica.models.tasks.Task; -import com.habitrpg.android.habitica.models.user.Preferences; -import com.habitrpg.android.habitica.models.user.SpecialItems; -import com.habitrpg.android.habitica.models.user.User; -import com.habitrpg.android.habitica.proxy.CrashlyticsProxy; -import com.habitrpg.android.habitica.ui.AvatarView; -import com.habitrpg.android.habitica.ui.AvatarWithBarsViewModel; -import com.habitrpg.android.habitica.ui.TutorialView; -import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment; -import com.habitrpg.android.habitica.ui.fragments.NavigationDrawerFragment; -import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils; -import com.habitrpg.android.habitica.ui.helpers.KeyboardUtil; -import com.habitrpg.android.habitica.ui.menu.HabiticaDrawerItem; -import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper; -import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar; -import com.habitrpg.android.habitica.ui.views.ValueBar; -import com.habitrpg.android.habitica.ui.views.yesterdailies.YesterdailyDialog; -import com.habitrpg.android.habitica.userpicture.BitmapUtils; -import com.habitrpg.android.habitica.widget.AvatarStatsWidgetProvider; -import com.habitrpg.android.habitica.widget.DailiesWidgetProvider; -import com.habitrpg.android.habitica.widget.HabitButtonWidgetProvider; -import com.habitrpg.android.habitica.widget.TodoListWidgetProvider; -import com.roughike.bottombar.BottomBar; - -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; - -import java.io.File; -import java.lang.ref.WeakReference; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -import javax.inject.Inject; - -import butterknife.BindView; -import io.reactivex.Completable; -import io.reactivex.android.schedulers.AndroidSchedulers; - -import static android.os.Build.VERSION.SDK_INT; -import static com.habitrpg.android.habitica.ui.views.HabiticaSnackbar.SnackbarDisplayType; - -public class MainActivity extends BaseActivity implements TutorialView.OnTutorialReaction { - - public static final int SELECT_CLASS_RESULT = 11; - public static final int GEM_PURCHASE_REQUEST = 111; - @Inject - public ApiClient apiClient; - - @Inject - public SoundManager soundManager; - - @Inject - public MaintenanceApiService maintenanceService; - public User user; - @BindView(R.id.floating_menu_wrapper) - public ViewGroup floatingMenuWrapper; - @BindView(R.id.bottom_navigation) - BottomBar bottomNavigation; - @Inject - protected HostConfig hostConfig; - @Inject - protected SharedPreferences sharedPreferences; - @Inject - CrashlyticsProxy crashlyticsProxy; - @BindView(R.id.appbar) - AppBarLayout appBar; - @BindView(R.id.toolbar) - Toolbar toolbar; - @BindView(R.id.toolbar_accessory_container) - FrameLayout toolbarAccessoryContainer; - @BindView(R.id.toolbar_title) - TextView toolbarTitleTextView; - @BindView(R.id.collapsing_toolbar) - CollapsingToolbarLayout collapsingToolbar; - @BindView(R.id.detail_tabs) - TabLayout detail_tabs; - @BindView(R.id.avatar_with_bars) - public - View avatar_with_bars; - @BindView(R.id.overlayFrameLayout) - ViewGroup overlayLayout; - - @Inject - PushNotificationManager pushNotificationManager; - // region UseCases - - @Inject - HabitScoreUseCase habitScoreUseCase; - - @Inject - DailyCheckUseCase dailyCheckUseCase; - - @Inject - TodoCheckUseCase todoCheckUseCase; - - @Inject - BuyRewardUseCase buyRewardUseCase; - - @Inject - ChecklistCheckUseCase checklistCheckUseCase; - - @Inject - CheckClassSelectionUseCase checkClassSelectionUseCase; - - @Inject - DisplayItemDropUseCase displayItemDropUseCase; - - @Inject - NotifyUserUseCase notifyUserUseCase; - - @Inject - TaskRepository taskRepository; - @Inject - UserRepository userRepository; - @Inject - TagRepository tagRepository; - @Inject - InventoryRepository inventoryRepository; - - @Inject - TaskAlarmManager taskAlarmManager; - @Inject - RemoteConfigManager remoteConfigManager; - - // endregion - - @Nullable - private WeakReference activeFragment; - private AvatarWithBarsViewModel avatarInHeader; - private AlertDialog faintDialog; - private AvatarView sideAvatarView; - private TutorialView activeTutorialView; - private NavigationDrawerFragment drawerFragment; - private ActionBarDrawerToggle drawerToggle; - private KeyboardUtil keyboardUtil; - - - @Override - protected int getLayoutResId() { - return R.layout.activity_main; - } - - @SuppressLint("ObsoleteSdkInt") - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - LanguageHelper languageHelper = new LanguageHelper(sharedPreferences.getString("language", "en")); - Locale.setDefault(languageHelper.getLocale()); - Configuration configuration = new Configuration(); - if (SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) { - configuration.locale = languageHelper.getLocale(); - } else { - configuration.setLocale(languageHelper.getLocale()); - } - getResources().updateConfiguration(configuration, - getResources().getDisplayMetrics()); - - - if (!HabiticaApplication.checkUserAuthentication(this, hostConfig)) { - return; - } - - setupToolbar(toolbar); - - avatarInHeader = new AvatarWithBarsViewModel(this, avatar_with_bars); - sideAvatarView = new AvatarView(this, true, false, false); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - Window window = getWindow(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - window.setStatusBarColor(ContextCompat.getColor(this, R.color.black_10_alpha)); - } - getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); - toolbar.setPadding(0, getStatusBarHeight(), 0, 0); - float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, getResources().getDisplayMetrics()); - avatar_with_bars.setPadding((int)px, getStatusBarHeight(), (int)px, 0); - } - - userRepository.getUser(hostConfig.getUser()) - .subscribe(newUser -> { - MainActivity.this.user = newUser; - MainActivity.this.setUserData(); - }, RxErrorHandler.handleEmptyError()); - - DrawerLayout drawerLayout = findViewById(R.id.drawer_layout); - - drawerFragment = (NavigationDrawerFragment) getSupportFragmentManager().findFragmentById(R.id.navigation_drawer); - drawerFragment.setUp(R.id.navigation_drawer, drawerLayout); - drawerFragment.setSelection(NavigationDrawerFragment.SIDEBAR_TASKS, true); - - drawerToggle = new ActionBarDrawerToggle( - this, /* host Activity */ - findViewById(R.id.drawer_layout), /* DrawerLayout object */ - R.string.navigation_drawer_open, /* "open drawer" description */ - R.string.navigation_drawer_close /* "close drawer" description */ - ) { - - /** Called when a drawer has settled in a completely closed state. */ - public void onDrawerClosed(View view) { - super.onDrawerClosed(view); - } - - /** Called when a drawer has settled in a completely open state. */ - public void onDrawerOpened(View drawerView) { - super.onDrawerOpened(drawerView); - } - }; - - // Set the drawer toggle as the DrawerListener - drawerLayout.addDrawerListener(drawerToggle); - - if (getSupportActionBar() != null) { - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setHomeButtonEnabled(true); - } - - this.keyboardUtil = new KeyboardUtil(this, this.findViewById(android.R.id.content)); - this.keyboardUtil.enable(); - } - - @Override - protected void onPostCreate(Bundle savedInstanceState) { - super.onPostCreate(savedInstanceState); - // Sync the toggle state after onRestoreInstanceState has occurred. - if (drawerToggle != null) { - drawerToggle.syncState(); - } - } - - @Override - public void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - if (drawerToggle != null) { - drawerToggle.onConfigurationChanged(newConfig); - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (drawerToggle.onOptionsItemSelected(item)) { - return true; - } - return super.onOptionsItemSelected(item); - } - - public int getStatusBarHeight() { - int result = 0; - int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android"); - if (resourceId > 0) { - result = getResources().getDimensionPixelSize(resourceId); - } - return result; - } - - @Override - protected void injectActivity(AppComponent component) { - component.inject(this); - } - - @Override - protected void onResume() { - super.onResume(); - - retrieveUser(); - this.checkMaintenance(); - - if (this.sharedPreferences.getLong("lastReminderSchedule", 0) < new Date().getTime() - 86400000) { - try { - taskAlarmManager.scheduleAllSavedAlarms(); - } catch (Exception e) { - crashlyticsProxy.logException(e); - } - } - - //after the activity has been stopped and is thereafter resumed, - //a state can arise in which the active fragment no longer has a - //reference to the tabLayout (and all its adapters are null). - //Recreate the fragment as a result. - if (activeFragment != null && activeFragment.get() != null && activeFragment.get().tabLayout == null) { - activeFragment = null; - if (drawerFragment != null) { - String selection = NavigationDrawerFragment.SIDEBAR_TASKS; - try { - selection = this.sharedPreferences.getString("lastActivePosition", NavigationDrawerFragment.SIDEBAR_TASKS); - } catch (java.lang.RuntimeException ignored) {} - drawerFragment.setSelection(selection, true); - } - } - } - - @Override - protected void onPause() { - updateWidgets(); - super.onPause(); - } - - private void updateWidgets() { - updateWidget(AvatarStatsWidgetProvider.class); - updateWidget(TodoListWidgetProvider.class); - updateWidget(DailiesWidgetProvider.class); - updateWidget(HabitButtonWidgetProvider.class); - } - - private void updateWidget(Class widgetClass) { - Intent intent = new Intent(this, widgetClass); - intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE); - int ids[] = AppWidgetManager.getInstance(getApplication()).getAppWidgetIds(new ComponentName(getApplication(), widgetClass)); - intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids); - sendBroadcast(intent); - } - - @SuppressLint("ObsoleteSdkInt") - public void displayFragment(BaseMainFragment fragment) { - if (this.activeFragment != null && activeFragment.get() != null && fragment.getClass() == this.activeFragment.get().getClass()) { - return; - } - if (SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && this.isDestroyed()) { - return; - } - this.activeFragment = new WeakReference<>(fragment); - fragment.setArguments(getIntent().getExtras()); - fragment.setUser(user); - fragment.setActivity(this); - fragment.setTabLayout(detail_tabs); - fragment.setToolbarAccessoryContainer(toolbarAccessoryContainer); - fragment.setCollapsingToolbar(collapsingToolbar); - fragment.setBottomNavigation(bottomNavigation); - fragment.setFloatingMenuWrapper(floatingMenuWrapper); - - - if (getSupportFragmentManager().getFragments() == null) { - getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, fragment).commitAllowingStateLoss(); - } else { - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out, android.R.anim.fade_in, android.R.anim.fade_out); - transaction.replace(R.id.fragment_container, fragment); - if (fragment.addToBackStack()) { - transaction.addToBackStack(null); - } - transaction.commitAllowingStateLoss(); - } - } - - protected void setUserData() { - if (user != null) { - - Preferences preferences = user.getPreferences(); - - if (preferences != null) { - if (preferences.getLanguage() != null) { - apiClient.setLanguageCode(preferences.getLanguage()); - } - if (preferences.getSound() != null) { - soundManager.setSoundTheme(preferences.getSound()); - } - } - runOnUiThread(() -> { - updateHeader(); - updateSidebar(); - if (activeFragment != null && activeFragment.get() != null) { - activeFragment.get().updateUserData(user); - } else { - if (drawerFragment != null) { - drawerFragment.setSelection(NavigationDrawerFragment.SIDEBAR_TASKS, true); - } - } - }); - - displayDeathDialogIfNeeded(); - YesterdailyDialog.Companion.showDialogIfNeeded(this, user.getId(), userRepository, taskRepository); - - displayNewInboxMessagesBadge(); - } - } - - private void displayNewInboxMessagesBadge() { - /*int numberOfUnreadPms = this.user.getInbox().getNewMessages(); - IDrawerItem newInboxItem; - - if (numberOfUnreadPms <= 0) { - newInboxItem = new PrimaryDrawerItem() - .withName(this.getString(R.string.sidebar_inbox)) - .withIdentifier(MainDrawerBuilder.INSTANCE.getSIDEBAR_INBOX()); - } else { - String numberOfUnreadPmsLabel = String.valueOf(numberOfUnreadPms); - BadgeStyle badgeStyle = new BadgeStyle() - .withTextColor(Color.WHITE) - .withColorRes(R.color.md_red_700); - - newInboxItem = new PrimaryDrawerItem() - .withName(this.getString(R.string.sidebar_inbox)) - .withIdentifier(MainDrawerBuilder.INSTANCE.getSIDEBAR_INBOX()) - .withBadge(numberOfUnreadPmsLabel) - .withBadgeStyle(badgeStyle); - } - if (this.drawerFragment != null) { - this.drawer.updateItemAtPosition(newInboxItem, this.drawer.getPosition(MainDrawerBuilder.INSTANCE.getSIDEBAR_INBOX())); - }*/ - } - - private void updateHeader() { - if (avatarInHeader != null) { - avatarInHeader.updateData(user); - } - if (activeFragment != null) { - setTranslatedFragmentTitle(activeFragment.get()); - } - } - - public void updateSidebar() { - if (drawerFragment != null) { - drawerFragment.setUsername(user.getProfile().getName()); - } - - if (user.getPreferences() == null || user.getFlags() == null) { - return; - } - - SpecialItems specialItems = user.getItems().getSpecial(); - Boolean hasSpecialItems = false; - if (specialItems != null) { - hasSpecialItems = specialItems.hasSpecialItems(); - } - if (drawerFragment != null) { - HabiticaDrawerItem item = drawerFragment.getItemWithIdentifier(NavigationDrawerFragment.SIDEBAR_SKILLS); - if (item != null) { - if (!user.hasClass() && !hasSpecialItems) { - item.setVisible(false); - } else { - if (user.getStats().getLvl() < HabiticaSnackbar.MIN_LEVEL_FOR_SKILLS && !hasSpecialItems) { - item.setAdditionalInfo(getString(R.string.unlock_lvl_11)); - } else { - item.setAdditionalInfo(null); - } - item.setVisible(true); - } - drawerFragment.updateItem(item); - } - HabiticaDrawerItem statsItem = drawerFragment.getItemWithIdentifier(NavigationDrawerFragment.SIDEBAR_STATS); - if (statsItem != null) { - if (user.getStats() != null && user.getStats().lvl >= 0 && user.getStats().points > 0) { - statsItem.setAdditionalInfo(user.getStats().points.toString()); - } else { - statsItem.setAdditionalInfo(null); - } - drawerFragment.updateItem(statsItem); - } - } - } - - public void setActiveFragment(@Nullable BaseMainFragment fragment) { - this.activeFragment = new WeakReference<>(fragment); - setTranslatedFragmentTitle(fragment); - if (this.drawerFragment != null && this.activeFragment != null && activeFragment.get() != null) { - this.drawerFragment.setSelection(this.activeFragment.get().fragmentSidebarIdentifier, false); - } - } - - private void setTranslatedFragmentTitle(@Nullable BaseMainFragment fragment) { - if (getSupportActionBar() == null) { - return; - } - if (fragment != null && fragment.customTitle() != null) { - toolbarTitleTextView.setText(fragment.customTitle()); - } else if (user != null && user.getProfile() != null) { - toolbarTitleTextView.setText(user.getProfile().getName()); - } - } - - public void onBackPressed() { - if (this.activeTutorialView != null) { - this.removeActiveTutorialView(); - } - if (drawerFragment != null && drawerFragment.isDrawerOpen()) { - drawerFragment.closeDrawer(); - } else { - try { - super.onBackPressed(); - } catch (Exception ignored) {} - if (this.activeFragment != null && activeFragment.get() != null) { - this.activeFragment.get().updateUserData(user); - } - } - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - if (resultCode == SELECT_CLASS_RESULT) { - retrieveUser(); - } else if (requestCode == GEM_PURCHASE_REQUEST) { - retrieveUser(); - } - super.onActivityResult(requestCode, resultCode, data); - } - - // region Events - - @Override - public void onDestroy() { - userRepository.close(); - tagRepository.close(); - inventoryRepository.close(); - if (keyboardUtil != null) { - keyboardUtil.disable(); - } - super.onDestroy(); - } - - @Subscribe - public void onEvent(OpenMenuItemCommand event) { - if (drawerFragment != null) { - drawerFragment.setSelection(event.identifier, true); - } - } - - @Subscribe - public void onEvent(final BuyRewardCommand event) { - final String rewardKey = event.Reward.getId(); - - if (user.getStats().getGp() < event.Reward.getValue()) { - HabiticaSnackbar.Companion.showSnackbar(floatingMenuWrapper, getString(R.string.no_gold), SnackbarDisplayType.FAILURE); - return; - } - - if ("potion".equals(rewardKey)) { - int currentHp = user.getStats().getHp().intValue(); - int maxHp = user.getStats().getMaxHealth(); - - if (currentHp == maxHp) { - HabiticaSnackbar.Companion.showSnackbar(floatingMenuWrapper, getString(R.string.no_potion), SnackbarDisplayType.FAILURE_BLUE); - return; - } - } - - if (event.Reward.getSpecialTag() != null && event.Reward.getSpecialTag().equals("item")) { - inventoryRepository.buyItem(user, event.Reward.getId(), event.Reward.getValue()) - .subscribe(buyResponse -> { - String snackbarMessage = getString(R.string.successful_purchase, event.Reward.getText()); - if (event.Reward.getId().equals("armoire")) { - if (buyResponse.armoire.get("type").equals("gear")) { - snackbarMessage = getApplicationContext().getString(R.string.armoireEquipment, buyResponse.armoire.get("dropText")); - } else if (buyResponse.armoire.get("type").equals("food")) { - snackbarMessage = getApplicationContext().getString(R.string.armoireFood, buyResponse.armoire.get("dropArticle"), buyResponse.armoire.get("dropText")); - } else { - snackbarMessage = getApplicationContext().getString(R.string.armoireExp); - } - soundManager.loadAndPlayAudio(SoundManager.SoundItemDrop); - } - HabiticaSnackbar.Companion.showSnackbar(floatingMenuWrapper, null, snackbarMessage, new BitmapDrawable(getResources(), HabiticaIconsHelper.imageOfGold()), ContextCompat.getColor(this, R.color.yellow_10), "-"+ event.Reward.getValue(), SnackbarDisplayType.NORMAL); - }, RxErrorHandler.handleEmptyError()); - } else { - buyRewardUseCase.observable(new BuyRewardUseCase.RequestValues(user, event.Reward)) - .subscribe(res -> HabiticaSnackbar.Companion.showSnackbar(floatingMenuWrapper, null, getString(R.string.notification_purchase_reward), - new BitmapDrawable(getResources(), HabiticaIconsHelper.imageOfGold()), - ContextCompat.getColor(this, R.color.yellow_10), - "-"+ ((int) event.Reward.getValue()), - SnackbarDisplayType.DROP), RxErrorHandler.handleEmptyError()); - } - } - - @Subscribe - public void openMysteryItem(OpenMysteryItemEvent event) { - inventoryRepository.openMysteryItem(user) - .flatMap(mysteryItem -> userRepository.retrieveUser(false)) - .subscribe(mysteryItem -> {}, RxErrorHandler.handleEmptyError()); - } - - @Subscribe - public void openGemPurchaseFragment(@Nullable OpenGemPurchaseFragmentCommand cmd) { - if (drawerFragment != null) { - drawerFragment.setSelection(NavigationDrawerFragment.SIDEBAR_PURCHASE, true); - } - } - - @Subscribe - public void onEvent(DisplayTutorialEvent tutorialEvent) { - if (tutorialEvent.tutorialText != null) { - this.displayTutorialStep(tutorialEvent.step, tutorialEvent.tutorialText, tutorialEvent.canBeDeferred); - } else { - this.displayTutorialStep(tutorialEvent.step, tutorialEvent.tutorialTexts, tutorialEvent.canBeDeferred); - } - } - - @Subscribe - public void onEvent(DisplayFragmentEvent event) { - this.displayFragment(event.fragment); - } - - @Subscribe - public void onEvent(final HatchingCommand event) { - if (event.usingEgg == null || event.usingHatchingPotion == null) { - return; - } - this.inventoryRepository.hatchPet(event.usingEgg, event.usingHatchingPotion) - .subscribe(items -> { - FrameLayout petWrapper = (FrameLayout) View.inflate(this, R.layout.pet_imageview, null); - SimpleDraweeView petImageView = (SimpleDraweeView) petWrapper.findViewById(R.id.pet_imageview); - - DataBindingUtils.INSTANCE.loadImage(petImageView, "Pet-" + event.usingEgg.getKey() + "-" + event.usingHatchingPotion.getKey()); - String potionName = event.usingHatchingPotion.getText(); - String eggName = event.usingEgg.getText(); - AlertDialog dialog = new AlertDialog.Builder(MainActivity.this) - .setTitle(getString(R.string.hatched_pet_title, potionName, eggName)) - .setView(petWrapper) - .setPositiveButton(R.string.close, (hatchingDialog, which) -> hatchingDialog.dismiss()) - .setNeutralButton(R.string.share, (hatchingDialog, which) -> { - ShareEvent event1 = new ShareEvent(); - event1.sharedMessage = getString(R.string.share_hatched, potionName, eggName) + " https://habitica.com/social/hatch-pet"; - Bitmap sharedImage = Bitmap.createBitmap(140, 140, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(sharedImage); - canvas.drawColor(ContextCompat.getColor(this, R.color.brand_300)); - petImageView.getDrawable().draw(canvas); - event1.shareImage = sharedImage; - EventBus.getDefault().post(event1); - hatchingDialog.dismiss(); - }) - .create(); - dialog.show(); - }, RxErrorHandler.handleEmptyError()); - } - - @Subscribe - public void onEvent(FeedCommand event) { - if (event.usingFood == null || event.usingPet == null) { - return; - } - final Pet pet = event.usingPet; - this.inventoryRepository.feedPet(event.usingPet, event.usingFood) - .subscribe(feedResponse -> { - HabiticaSnackbar.Companion.showSnackbar(floatingMenuWrapper, getString(R.string.notification_pet_fed, pet.getColorText(), pet.getAnimalText()), SnackbarDisplayType.NORMAL); - if (feedResponse.value == -1) { - FrameLayout mountWrapper = (FrameLayout) View.inflate(this, R.layout.pet_imageview, null); - SimpleDraweeView mountImageView = (SimpleDraweeView) mountWrapper.findViewById(R.id.pet_imageview); - - DataBindingUtils.INSTANCE.loadImage(mountImageView, "Mount_Icon_" + event.usingPet.getKey()); - String colorName = event.usingPet.getColorText(); - String animalName = event.usingPet.getAnimalText(); - AlertDialog dialog = new AlertDialog.Builder(MainActivity.this) - .setTitle(getString(R.string.evolved_pet_title, colorName, animalName)) - .setView(mountWrapper) - .setPositiveButton(R.string.close, (hatchingDialog, which) -> hatchingDialog.dismiss()) - .setNeutralButton(R.string.share, (hatchingDialog, which) -> { - ShareEvent event1 = new ShareEvent(); - event1.sharedMessage = getString(R.string.share_raised, colorName, animalName) + " https://habitica.com/social/raise-pet"; - Bitmap sharedImage = Bitmap.createBitmap(99, 99, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(sharedImage); - canvas.drawColor(ContextCompat.getColor(this, R.color.brand_300)); - mountImageView.getDrawable().draw(canvas); - event1.shareImage = sharedImage; - EventBus.getDefault().post(event1); - hatchingDialog.dismiss(); - }) - .create(); - dialog.show(); - } - }, RxErrorHandler.handleEmptyError()); - } - - // endregion - - public void displayTaskScoringResponse(TaskScoringResult data) { - if (user != null && data != null) { - notifyUserUseCase.observable(new NotifyUserUseCase.RequestValues(this, floatingMenuWrapper, - user, data.getExperienceDelta(), data.getHealthDelta(), data.getGoldDelta(), data.getManaDelta(), data.getQuestDamage(), data.getHasLeveledUp())) - .subscribe(aVoid -> {}, RxErrorHandler.handleEmptyError()); - } - - displayItemDropUseCase.observable(new DisplayItemDropUseCase.RequestValues(data, this, floatingMenuWrapper)) - .subscribe(aVoid -> {}, RxErrorHandler.handleEmptyError()); - } - - - private void displayDeathDialogIfNeeded() { - - if (user.getStats().getHp() == null || user.getStats().getHp() > 0) { - return; - } - - if (this.faintDialog == null && !this.isFinishing()) { - - View customView = View.inflate(this, R.layout.dialog_faint, null); - if (customView != null) { - ValueBar hpBarView = (ValueBar) customView.findViewById(R.id.hpBar); - - hpBarView.setLightBackground(true); - hpBarView.setIcon(HabiticaIconsHelper.imageOfHeartLightBg()); - - AvatarView dialogAvatarView = (AvatarView) customView.findViewById(R.id.avatarView); - dialogAvatarView.setAvatar(user); - } - - this.faintDialog = new AlertDialog.Builder(this) - .setTitle(R.string.faint_header) - .setView(customView) - .setPositiveButton(R.string.faint_button, (dialog, which) -> { - faintDialog = null; - userRepository.revive(user).subscribe(user1 -> {}, RxErrorHandler.handleEmptyError()); - }) - .create(); - - soundManager.loadAndPlayAudio(SoundManager.SoundDeath); - this.faintDialog.show(); - } - } - - - @Override - public boolean onKeyUp(int keyCode, KeyEvent event) { - if (keyCode == KeyEvent.KEYCODE_MENU && drawerFragment != null) { - drawerFragment.openDrawer(); - return true; - } - - return super.onKeyUp(keyCode, event); - } - - public ViewGroup getFloatingMenuWrapper() { - return floatingMenuWrapper; - } - - protected void retrieveUser() { - if (this.userRepository != null && hostConfig.hasAuthentication()) { - this.userRepository.retrieveUser(true) - .doOnNext(user1 -> { - pushNotificationManager.setUser(user1); - pushNotificationManager.addPushDeviceUsingStoredToken(); - }) - .flatMap(user1 -> userRepository.retrieveInboxMessages()) - .flatMap(user1 -> inventoryRepository.retrieveContent(false)) - .flatMap(contentResult -> inventoryRepository.retrieveWorldState()) - .subscribe(user1 -> {}, RxErrorHandler.handleEmptyError()); - } - } - - @Subscribe - public void displayClassSelectionActivity(SelectClassEvent event) { - checkClassSelectionUseCase.observable(new CheckClassSelectionUseCase.RequestValues(user, event, this)) - .subscribe(aVoid -> {}, RxErrorHandler.handleEmptyError()); - } - - private void displayTutorialStep(TutorialStep step, String text, boolean canBeDeferred) { - TutorialView view = new TutorialView(this, step, this); - this.activeTutorialView = view; - view.setTutorialText(text); - view.setOnReaction(this); - view.setCanBeDeferred(canBeDeferred); - this.overlayLayout.addView(view); - - Map additionalData = new HashMap<>(); - additionalData.put("eventLabel", step.getIdentifier() + "-android"); - additionalData.put("eventValue", step.getIdentifier()); - additionalData.put("complete", false); - AmplitudeManager.sendEvent("tutorial", AmplitudeManager.EVENT_CATEGORY_BEHAVIOUR, AmplitudeManager.EVENT_HITTYPE_EVENT, additionalData); - } - - private void displayTutorialStep(TutorialStep step, List texts, boolean canBeDeferred) { - TutorialView view = new TutorialView(this, step, this); - this.activeTutorialView = view; - view.setTutorialTexts(texts); - view.setOnReaction(this); - view.setCanBeDeferred(canBeDeferred); - this.overlayLayout.addView(view); - - Map additionalData = new HashMap<>(); - additionalData.put("eventLabel", step.getIdentifier() + "-android"); - additionalData.put("eventValue", step.getIdentifier()); - additionalData.put("complete", false); - AmplitudeManager.sendEvent("tutorial", AmplitudeManager.EVENT_CATEGORY_BEHAVIOUR, AmplitudeManager.EVENT_HITTYPE_EVENT, additionalData); - } - - @Override - public void onTutorialCompleted(TutorialStep step) { - String path = "flags.tutorial." + step.getTutorialGroup() + "." + step.getIdentifier(); - Map updateData = new HashMap<>(); - updateData.put(path, true); - userRepository.updateUser(user, updateData) - .subscribe(user1 -> {}, RxErrorHandler.handleEmptyError()); - this.overlayLayout.removeView(this.activeTutorialView); - this.removeActiveTutorialView(); - - Map additionalData = new HashMap<>(); - additionalData.put("eventLabel", step.getIdentifier() + "-android"); - additionalData.put("eventValue", step.getIdentifier()); - additionalData.put("complete", true); - AmplitudeManager.sendEvent("tutorial", AmplitudeManager.EVENT_CATEGORY_BEHAVIOUR, AmplitudeManager.EVENT_HITTYPE_EVENT, additionalData); - } - - @Override - public void onTutorialDeferred(TutorialStep step) { - taskRepository.executeTransaction(realm -> step.setDisplayedOn(new Date())); - - this.removeActiveTutorialView(); - } - - private void removeActiveTutorialView() { - if (this.activeTutorialView != null) { - this.overlayLayout.removeView(this.activeTutorialView); - this.activeTutorialView = null; - } - } - - public String getUserID() { - if (this.user != null) { - return user.getId(); - } else { - return ""; - } - } - - @Subscribe - public void shareEvent(ShareEvent event) { - Intent sharingIntent = new Intent(Intent.ACTION_SEND); - sharingIntent.setType("*/*"); - sharingIntent.putExtra(Intent.EXTRA_TEXT, event.sharedMessage); - File f = BitmapUtils.saveToShareableFile(getFilesDir() + "/shared_images", "share.png", event.shareImage); - Uri fileUri = FileProvider.getUriForFile(this, getString(R.string.content_provider), f); - sharingIntent.putExtra(Intent.EXTRA_STREAM, fileUri); - List resInfoList = this.getPackageManager().queryIntentActivities(sharingIntent, PackageManager.MATCH_DEFAULT_ONLY); - for (ResolveInfo resolveInfo : resInfoList) { - String packageName = resolveInfo.activityInfo.packageName; - this.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); - } - startActivity(Intent.createChooser(sharingIntent, getString(R.string.share_using))); - } - - @Subscribe - public void onEvent(TaskCheckedCommand event) { - switch (event.Task.getType()) { - case Task.TYPE_DAILY: { - dailyCheckUseCase.observable(new DailyCheckUseCase.RequestValues(user, event.Task, !event.Task.getCompleted())) - .subscribe(this::displayTaskScoringResponse, RxErrorHandler.handleEmptyError()); - } - break; - case Task.TYPE_TODO: { - todoCheckUseCase.observable(new TodoCheckUseCase.RequestValues(user, event.Task, !event.Task.getCompleted())) - .subscribe(this::displayTaskScoringResponse, RxErrorHandler.handleEmptyError()); - } - break; - } - } - - @Subscribe - public void onEvent(ChecklistCheckedCommand event) { - checklistCheckUseCase.observable(new ChecklistCheckUseCase.RequestValues(event.task.getId(), event.item.getId())).subscribe(task -> {}, error -> {}); - } - - @Subscribe - public void onEvent(HabitScoreEvent event) { - habitScoreUseCase.observable(new HabitScoreUseCase.RequestValues(user, event.habit, event.Up)) - .subscribe(this::displayTaskScoringResponse, RxErrorHandler.handleEmptyError()); - } - - private void checkMaintenance() { - this.maintenanceService.getMaintenanceStatus() - .compose(apiClient.configureApiCallObserver()) - .subscribe(maintenanceResponse -> { - if (maintenanceResponse == null) { - return; - } - if (maintenanceResponse.activeMaintenance) { - Intent intent = createMaintenanceIntent(maintenanceResponse, false); - startActivity(intent); - } else { - if (maintenanceResponse.minBuild != null) { - PackageInfo packageInfo = null; - try { - packageInfo = getPackageManager().getPackageInfo(getPackageName(), 0); - if (packageInfo.versionCode < maintenanceResponse.minBuild) { - Intent intent = createMaintenanceIntent(maintenanceResponse, true); - startActivity(intent); - } - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - } - } - } - }, RxErrorHandler.handleEmptyError()); - } - - private Intent createMaintenanceIntent(MaintenanceResponse maintenanceResponse, Boolean isDeprecationNotice) { - Intent intent = new Intent(this, MaintenanceActivity.class); - Bundle data = new Bundle(); - data.putString("title", maintenanceResponse.title); - data.putString("imageUrl", maintenanceResponse.imageUrl); - data.putString("description", maintenanceResponse.description); - data.putBoolean("deprecationNotice", isDeprecationNotice); - intent.putExtras(data); - return intent; - } - - @Subscribe - public void showSnackBarEvent(ShowSnackbarEvent event) { - HabiticaSnackbar.Companion.showSnackbar(floatingMenuWrapper, event.leftImage, event.title, event.text, event.specialView, event.rightIcon, event.rightTextColor, event.rightText, event.type); - } - - public boolean isAppBarExpanded() { - return (appBar.getHeight() - appBar.getBottom()) == 0; - } - - @Subscribe - public void showCheckinDialog(ShowCheckinDialog event) { - String title = event.notification.data.message; - - LayoutInflater factory = LayoutInflater.from(this); - final View view = factory.inflate(R.layout.dialog_login_incentive, null); - - SimpleDraweeView imageView = (SimpleDraweeView) view.findViewById(R.id.imageView); - String imageKey = event.notification.data.rewardKey.get(0); - DataBindingUtils.INSTANCE.loadImage(imageView, imageKey); - - String youEarnedMessage = this.getString(R.string.checkInRewardEarned, event.notification.data.rewardText); - - TextView titleTextView = new TextView(this); - titleTextView.setBackgroundResource(R.color.blue_100); - titleTextView.setTextColor(ContextCompat.getColor(this, R.color.white)); - float density = this.getResources().getDisplayMetrics().density; - int paddingDp = (int) (16 * density); - titleTextView.setPadding(paddingDp, paddingDp, paddingDp, paddingDp); - titleTextView.setTextSize(18); - titleTextView.setGravity(Gravity.CENTER_HORIZONTAL); - titleTextView.setText(title); - - TextView youEarnedTexView = (TextView) view.findViewById(R.id.you_earned_message); - youEarnedTexView.setText(youEarnedMessage); - - TextView nextUnlockTextView = (TextView) view.findViewById(R.id.next_unlock_message); - nextUnlockTextView.setText(event.nextUnlockText); - - AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.AlertDialogTheme) - .setView(view) - .setCustomTitle(titleTextView) - .setPositiveButton(R.string.start_day, (dialog, which) -> { - if (apiClient != null) { - // @TODO: This should be handled somewhere else? MAybe we notifiy via event - apiClient.readNotification(event.notification.getId()) - .subscribe(next -> {}, RxErrorHandler.handleEmptyError()); - } - }) - .setMessage(""); - - Completable.complete() - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(() -> { - final AlertDialog dialog = builder.create(); - dialog.show(); - }, RxErrorHandler.handleEmptyError()); - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt new file mode 100644 index 000000000..926b1aca0 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt @@ -0,0 +1,906 @@ +package com.habitrpg.android.habitica.ui.activities + +import android.annotation.SuppressLint +import android.appwidget.AppWidgetManager +import android.content.ComponentName +import android.content.Intent +import android.content.SharedPreferences +import android.content.pm.PackageManager +import android.content.res.Configuration +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.drawable.BitmapDrawable +import android.os.Build +import android.os.Build.VERSION.SDK_INT +import android.os.Bundle +import android.preference.PreferenceManager +import android.support.design.widget.AppBarLayout +import android.support.design.widget.CollapsingToolbarLayout +import android.support.design.widget.TabLayout +import android.support.v4.content.ContextCompat +import android.support.v4.content.FileProvider +import android.support.v4.widget.DrawerLayout +import android.support.v7.app.ActionBarDrawerToggle +import android.support.v7.app.AlertDialog +import android.support.v7.widget.Toolbar +import android.util.TypedValue +import android.view.* +import android.widget.FrameLayout +import android.widget.TextView +import com.facebook.drawee.view.SimpleDraweeView +import com.habitrpg.android.habitica.HabiticaApplication +import com.habitrpg.android.habitica.HabiticaBaseApplication +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.api.HostConfig +import com.habitrpg.android.habitica.api.MaintenanceApiService +import com.habitrpg.android.habitica.components.AppComponent +import com.habitrpg.android.habitica.data.* +import com.habitrpg.android.habitica.events.* +import com.habitrpg.android.habitica.events.commands.* +import com.habitrpg.android.habitica.extensions.notNull +import com.habitrpg.android.habitica.helpers.* +import com.habitrpg.android.habitica.helpers.notifications.PushNotificationManager +import com.habitrpg.android.habitica.interactors.* +import com.habitrpg.android.habitica.models.TutorialStep +import com.habitrpg.android.habitica.models.responses.MaintenanceResponse +import com.habitrpg.android.habitica.models.responses.TaskScoringResult +import com.habitrpg.android.habitica.models.tasks.Task +import com.habitrpg.android.habitica.models.user.User +import com.habitrpg.android.habitica.proxy.CrashlyticsProxy +import com.habitrpg.android.habitica.ui.AvatarView +import com.habitrpg.android.habitica.ui.AvatarWithBarsViewModel +import com.habitrpg.android.habitica.ui.TutorialView +import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment +import com.habitrpg.android.habitica.ui.fragments.NavigationDrawerFragment +import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils +import com.habitrpg.android.habitica.ui.helpers.KeyboardUtil +import com.habitrpg.android.habitica.ui.helpers.bindView +import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper +import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar +import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar.SnackbarDisplayType +import com.habitrpg.android.habitica.ui.views.ValueBar +import com.habitrpg.android.habitica.ui.views.yesterdailies.YesterdailyDialog +import com.habitrpg.android.habitica.userpicture.BitmapUtils +import com.habitrpg.android.habitica.widget.AvatarStatsWidgetProvider +import com.habitrpg.android.habitica.widget.DailiesWidgetProvider +import com.habitrpg.android.habitica.widget.HabitButtonWidgetProvider +import com.habitrpg.android.habitica.widget.TodoListWidgetProvider +import com.roughike.bottombar.BottomBar +import io.reactivex.Completable +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.functions.Action +import io.reactivex.functions.Consumer +import io.realm.Realm +import org.greenrobot.eventbus.EventBus +import org.greenrobot.eventbus.Subscribe +import java.lang.ref.WeakReference +import java.util.* +import javax.inject.Inject + +open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction { + @Inject + internal lateinit var apiClient: ApiClient + @Inject + internal lateinit var soundManager: SoundManager + @Inject + internal lateinit var maintenanceService: MaintenanceApiService + @Inject + internal lateinit var hostConfig: HostConfig + @Inject + internal lateinit var sharedPreferences: SharedPreferences + @Inject + internal lateinit var crashlyticsProxy: CrashlyticsProxy + @Inject + internal lateinit var pushNotificationManager: PushNotificationManager + @Inject + internal lateinit var habitScoreUseCase: HabitScoreUseCase + @Inject + internal lateinit var dailyCheckUseCase: DailyCheckUseCase + @Inject + internal lateinit var todoCheckUseCase: TodoCheckUseCase + @Inject + internal lateinit var buyRewardUseCase: BuyRewardUseCase + @Inject + internal lateinit var checklistCheckUseCase: ChecklistCheckUseCase + @Inject + internal lateinit var checkClassSelectionUseCase: CheckClassSelectionUseCase + @Inject + internal lateinit var displayItemDropUseCase: DisplayItemDropUseCase + @Inject + internal lateinit var notifyUserUseCase: NotifyUserUseCase + @Inject + internal lateinit var taskRepository: TaskRepository + @Inject + internal lateinit var userRepository: UserRepository + @Inject + internal lateinit var tagRepository: TagRepository + @Inject + internal lateinit var inventoryRepository: InventoryRepository + @Inject + internal lateinit var taskAlarmManager: TaskAlarmManager + @Inject + internal lateinit var remoteConfigManager: RemoteConfigManager + + val floatingMenuWrapper: ViewGroup by bindView(R.id.floating_menu_wrapper) + private val bottomNavigation: BottomBar by bindView(R.id.bottom_navigation) + + private val appBar: AppBarLayout by bindView(R.id.appbar) + private val toolbar: Toolbar by bindView(R.id.toolbar) + private val toolbarAccessoryContainer: FrameLayout by bindView(R.id.toolbar_accessory_container) + private val toolbarTitleTextView: TextView by bindView(R.id.toolbar_title) + private val collapsingToolbar: CollapsingToolbarLayout by bindView(R.id.collapsing_toolbar) + private val detailTabs: TabLayout by bindView(R.id.detail_tabs) + val avatarWithBars: View by bindView(R.id.avatar_with_bars) + private val overlayLayout: ViewGroup by bindView(R.id.overlayFrameLayout) + + var user: User? = null + + private var activeFragment: WeakReference? = null + private var avatarInHeader: AvatarWithBarsViewModel? = null + private var faintDialog: AlertDialog? = null + private var sideAvatarView: AvatarView? = null + private var activeTutorialView: TutorialView? = null + private var drawerFragment: NavigationDrawerFragment? = null + private var drawerToggle: ActionBarDrawerToggle? = null + private var keyboardUtil: KeyboardUtil? = null + + private val statusBarHeight: Int + get() { + var result = 0 + val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android") + if (resourceId > 0) { + result = resources.getDimensionPixelSize(resourceId) + } + return result + } + + val userID: String + get() = user?.id ?: "" + + val isAppBarExpanded: Boolean + get() = appBar.height - appBar.bottom == 0 + + + override fun getLayoutResId(): Int { + return R.layout.activity_main + } + + @SuppressLint("ObsoleteSdkInt") + public override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) + val languageHelper = LanguageHelper(sharedPreferences.getString("language", "en")) + Locale.setDefault(languageHelper.locale) + val configuration = Configuration() + if (SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) { + configuration.locale = languageHelper.locale + } else { + configuration.setLocale(languageHelper.locale) + } + resources.updateConfiguration(configuration, + resources.displayMetrics) + + + if (!HabiticaBaseApplication.checkUserAuthentication(this, hostConfig)) { + return + } + + setupToolbar(toolbar) + + avatarInHeader = AvatarWithBarsViewModel(this, avatarWithBars) + sideAvatarView = AvatarView(this, true, false, false) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + val window = window + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + window.statusBarColor = ContextCompat.getColor(this, R.color.black_10_alpha) + } + getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) + toolbar.setPadding(0, statusBarHeight, 0, 0) + val px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16f, resources.displayMetrics) + avatarWithBars.setPadding(px.toInt(), statusBarHeight, px.toInt(), 0) + } + + userRepository.getUser(hostConfig.user) + .subscribe(Consumer { newUser -> + this@MainActivity.user = newUser + this@MainActivity.setUserData() + }, RxErrorHandler.handleEmptyError()) + + val drawerLayout = findViewById(R.id.drawer_layout) + + drawerFragment = supportFragmentManager.findFragmentById(R.id.navigation_drawer) as NavigationDrawerFragment + + drawerFragment?.setUp(R.id.navigation_drawer, drawerLayout) + drawerFragment?.setSelection(NavigationDrawerFragment.SIDEBAR_TASKS, true) + + drawerToggle = object : ActionBarDrawerToggle( + this, /* host Activity */ + findViewById(R.id.drawer_layout), /* DrawerLayout object */ + R.string.navigation_drawer_open, /* "open drawer" description */ + R.string.navigation_drawer_close /* "close drawer" description */ + ) { + + } + + // Set the drawer toggle as the DrawerListener + drawerToggle.notNull { drawerLayout.addDrawerListener(it) } + + supportActionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.setHomeButtonEnabled(true) + + keyboardUtil = KeyboardUtil(this, this.findViewById(android.R.id.content)) + this.keyboardUtil?.enable() + } + + override fun onPostCreate(savedInstanceState: Bundle?) { + super.onPostCreate(savedInstanceState) + // Sync the toggle state after onRestoreInstanceState has occurred. + drawerToggle?.syncState() + } + + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + drawerToggle?.onConfigurationChanged(newConfig) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return if (drawerToggle?.onOptionsItemSelected(item) == true) { + true + } else super.onOptionsItemSelected(item) + } + + override fun injectActivity(component: AppComponent?) { + component?.inject(this) + } + + override fun onResume() { + super.onResume() + + retrieveUser() + this.checkMaintenance() + + if (this.sharedPreferences.getLong("lastReminderSchedule", 0) < Date().time - 86400000) { + try { + taskAlarmManager.scheduleAllSavedAlarms() + } catch (e: Exception) { + crashlyticsProxy.logException(e) + } + + } + + //after the activity has been stopped and is thereafter resumed, + //a state can arise in which the active fragment no longer has a + //reference to the tabLayout (and all its adapters are null). + //Recreate the fragment as a result. + if (activeFragment?.get()?.tabLayout == null) { + activeFragment = null + var selection: String? = NavigationDrawerFragment.SIDEBAR_TASKS + try { + selection = this.sharedPreferences.getString("lastActivePosition", NavigationDrawerFragment.SIDEBAR_TASKS) + } catch (ignored: java.lang.RuntimeException) { + } + + drawerFragment?.setSelection(selection, true) + } + } + + override fun onPause() { + updateWidgets() + super.onPause() + } + + private fun updateWidgets() { + updateWidget(AvatarStatsWidgetProvider::class.java) + updateWidget(TodoListWidgetProvider::class.java) + updateWidget(DailiesWidgetProvider::class.java) + updateWidget(HabitButtonWidgetProvider::class.java) + } + + private fun updateWidget(widgetClass: Class<*>) { + val intent = Intent(this, widgetClass) + intent.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE + val ids = AppWidgetManager.getInstance(application).getAppWidgetIds(ComponentName(application, widgetClass)) + intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids) + sendBroadcast(intent) + } + + @SuppressLint("ObsoleteSdkInt") + fun displayFragment(fragment: BaseMainFragment) { + if (fragment.javaClass == this.activeFragment?.get()?.javaClass) { + return + } + if (SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && this.isDestroyed) { + return + } + this.activeFragment = WeakReference(fragment) + fragment.arguments = intent.extras + fragment.user = user + fragment.activity = this + fragment.tabLayout = detailTabs + fragment.toolbarAccessoryContainer = toolbarAccessoryContainer + fragment.collapsingToolbar = collapsingToolbar + fragment.bottomNavigation = bottomNavigation + fragment.floatingMenuWrapper = floatingMenuWrapper + + + if (supportFragmentManager.fragments == null) { + supportFragmentManager.beginTransaction().add(R.id.fragment_container, fragment).commitAllowingStateLoss() + } else { + val transaction = supportFragmentManager.beginTransaction() + transaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out, android.R.anim.fade_in, android.R.anim.fade_out) + transaction.replace(R.id.fragment_container, fragment) + if (fragment.addToBackStack()) { + transaction.addToBackStack(null) + } + transaction.commitAllowingStateLoss() + } + } + + private fun setUserData() { + if (user != null) { + + val preferences = user?.preferences + + preferences?.language.notNull { apiClient.setLanguageCode(it) } + preferences?.language.notNull { soundManager.soundTheme = it } + runOnUiThread { + updateHeader() + updateSidebar() + if (activeFragment != null && activeFragment?.get() != null) { + activeFragment?.get()?.updateUserData(user) + } else { + drawerFragment?.setSelection(NavigationDrawerFragment.SIDEBAR_TASKS, true) + } + } + + displayDeathDialogIfNeeded() + YesterdailyDialog.showDialogIfNeeded(this, user?.id, userRepository, taskRepository) + + displayNewInboxMessagesBadge() + } + } + + private fun displayNewInboxMessagesBadge() { + /*int numberOfUnreadPms = this.user.getInbox().getNewMessages(); + IDrawerItem newInboxItem; + + if (numberOfUnreadPms <= 0) { + newInboxItem = new PrimaryDrawerItem() + .withName(this.getString(R.string.sidebar_inbox)) + .withIdentifier(MainDrawerBuilder.INSTANCE.getSIDEBAR_INBOX()); + } else { + String numberOfUnreadPmsLabel = String.valueOf(numberOfUnreadPms); + BadgeStyle badgeStyle = new BadgeStyle() + .withTextColor(Color.WHITE) + .withColorRes(R.color.md_red_700); + + newInboxItem = new PrimaryDrawerItem() + .withName(this.getString(R.string.sidebar_inbox)) + .withIdentifier(MainDrawerBuilder.INSTANCE.getSIDEBAR_INBOX()) + .withBadge(numberOfUnreadPmsLabel) + .withBadgeStyle(badgeStyle); + } + if (this.drawerFragment != null) { + this.drawer.updateItemAtPosition(newInboxItem, this.drawer.getPosition(MainDrawerBuilder.INSTANCE.getSIDEBAR_INBOX())); + }*/ + } + + private fun updateHeader() { + user.notNull { avatarInHeader?.updateData(it) } + if (activeFragment != null) { + setTranslatedFragmentTitle(activeFragment?.get()) + } + } + + private fun updateSidebar() { + drawerFragment?.setUsername(user?.profile?.name) + + if (user?.preferences == null || user?.flags == null) { + return + } + + val specialItems = user?.items?.special + var hasSpecialItems = false + if (specialItems != null) { + hasSpecialItems = specialItems.hasSpecialItems() + } + val item = drawerFragment?.getItemWithIdentifier(NavigationDrawerFragment.SIDEBAR_SKILLS) + if (item != null) { + if (user?.hasClass() == false && (!hasSpecialItems)) { + item.isVisible = false + } else { + if (user?.stats?.getLvl() ?: 0 < HabiticaSnackbar.MIN_LEVEL_FOR_SKILLS && (!hasSpecialItems)) { + item.additionalInfo = getString(R.string.unlock_lvl_11) + } else { + item.additionalInfo = null + } + item.isVisible = true + } + drawerFragment?.updateItem(item) + } + val statsItem = drawerFragment?.getItemWithIdentifier(NavigationDrawerFragment.SIDEBAR_STATS) + if (statsItem != null) { + if (user?.stats?.lvl ?: 0 >= 0 && user?.stats?.points ?: 0 > 0) { + statsItem.additionalInfo = user?.stats?.points.toString() + } else { + statsItem.additionalInfo = null + } + drawerFragment?.updateItem(statsItem) + } + } + + fun setActiveFragment(fragment: BaseMainFragment?) { + this.activeFragment = WeakReference(fragment) + setTranslatedFragmentTitle(fragment) + if (activeFragment?.get() != null) { + this.drawerFragment?.setSelection(this.activeFragment?.get()?.fragmentSidebarIdentifier, false) + } + } + + private fun setTranslatedFragmentTitle(fragment: BaseMainFragment?) { + if (supportActionBar == null) { + return + } + if (fragment?.customTitle() != null) { + toolbarTitleTextView.text = fragment.customTitle() + } else if (user?.profile != null) { + toolbarTitleTextView.text = user?.profile?.name + } + } + + override fun onBackPressed() { + if (this.activeTutorialView != null) { + this.removeActiveTutorialView() + } + if (drawerFragment?.isDrawerOpen == true) { + drawerFragment?.closeDrawer() + } else { + try { + super.onBackPressed() + } catch (ignored: Exception) { + } + this.activeFragment?.get()?.updateUserData(user) + } + } + + public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) { + if (resultCode == SELECT_CLASS_RESULT) { + retrieveUser() + } else if (requestCode == GEM_PURCHASE_REQUEST) { + retrieveUser() + } + super.onActivityResult(requestCode, resultCode, data) + } + + // region Events + + public override fun onDestroy() { + userRepository.close() + tagRepository.close() + inventoryRepository.close() + keyboardUtil?.disable() + super.onDestroy() + } + + @Subscribe + fun onEvent(event: OpenMenuItemCommand) { + drawerFragment?.setSelection(event.identifier, true) + } + + @Subscribe + fun onEvent(event: BuyRewardCommand) { + val rewardKey = event.Reward.id + + if (user?.stats?.getGp() ?: 0.toDouble() < event.Reward.value) { + HabiticaSnackbar.showSnackbar(floatingMenuWrapper, getString(R.string.no_gold), SnackbarDisplayType.FAILURE) + return + } + + if ("potion" == rewardKey) { + val currentHp = user?.stats?.getHp()?.toInt() + val maxHp = user?.stats?.maxHealth + + if (currentHp == maxHp) { + HabiticaSnackbar.showSnackbar(floatingMenuWrapper, getString(R.string.no_potion), SnackbarDisplayType.FAILURE_BLUE) + return + } + } + + if (event.Reward.specialTag != null && event.Reward.specialTag == "item") { + inventoryRepository.buyItem(user, event.Reward.id, event.Reward.value) + .subscribe(Consumer { buyResponse -> + var snackbarMessage = getString(R.string.successful_purchase, event.Reward.text) + if (event.Reward.id == "armoire") { + snackbarMessage = when { + buyResponse.armoire["type"] == "gear" -> applicationContext.getString(R.string.armoireEquipment, buyResponse.armoire["dropText"]) + buyResponse.armoire["type"] == "food" -> applicationContext.getString(R.string.armoireFood, buyResponse.armoire["dropArticle"], buyResponse.armoire["dropText"]) + else -> applicationContext.getString(R.string.armoireExp) + } + soundManager.loadAndPlayAudio(SoundManager.SoundItemDrop) + } + HabiticaSnackbar.showSnackbar(floatingMenuWrapper, null, snackbarMessage, BitmapDrawable(resources, HabiticaIconsHelper.imageOfGold()), ContextCompat.getColor(this, R.color.yellow_10), "-" + event.Reward.value, SnackbarDisplayType.NORMAL) + }, RxErrorHandler.handleEmptyError()) + } else { + buyRewardUseCase.observable(BuyRewardUseCase.RequestValues(user, event.Reward)) + .subscribe(Consumer { + HabiticaSnackbar.showSnackbar(floatingMenuWrapper, null, getString(R.string.notification_purchase_reward), + BitmapDrawable(resources, HabiticaIconsHelper.imageOfGold()), + ContextCompat.getColor(this, R.color.yellow_10), + "-" + event.Reward.value.toInt(), + SnackbarDisplayType.DROP) + }, RxErrorHandler.handleEmptyError()) + } + } + + @Subscribe + fun openMysteryItem(event: OpenMysteryItemEvent) { + inventoryRepository.openMysteryItem(user) + .flatMap { userRepository.retrieveUser(false) } + .subscribe(Consumer { }, RxErrorHandler.handleEmptyError()) + } + + @Subscribe + fun openGemPurchaseFragment(cmd: OpenGemPurchaseFragmentCommand?) { + drawerFragment?.setSelection(NavigationDrawerFragment.SIDEBAR_PURCHASE, true) + } + + @Subscribe + fun onEvent(tutorialEvent: DisplayTutorialEvent) { + if (tutorialEvent.tutorialText != null) { + this.displayTutorialStep(tutorialEvent.step, tutorialEvent.tutorialText, tutorialEvent.canBeDeferred) + } else { + this.displayTutorialStep(tutorialEvent.step, tutorialEvent.tutorialTexts, tutorialEvent.canBeDeferred) + } + } + + @Subscribe + fun onEvent(event: DisplayFragmentEvent) { + this.displayFragment(event.fragment) + } + + @Subscribe + fun onEvent(event: HatchingCommand) { + if (event.usingEgg == null || event.usingHatchingPotion == null) { + return + } + this.inventoryRepository.hatchPet(event.usingEgg, event.usingHatchingPotion) + .subscribe(Consumer { + val petWrapper = View.inflate(this, R.layout.pet_imageview, null) as FrameLayout + val petImageView = petWrapper.findViewById(R.id.pet_imageview) as SimpleDraweeView + + DataBindingUtils.loadImage(petImageView, "Pet-" + event.usingEgg.key + "-" + event.usingHatchingPotion.key) + val potionName = event.usingHatchingPotion.text + val eggName = event.usingEgg.text + val dialog = AlertDialog.Builder(this@MainActivity) + .setTitle(getString(R.string.hatched_pet_title, potionName, eggName)) + .setView(petWrapper) + .setPositiveButton(R.string.close) { hatchingDialog, _ -> hatchingDialog.dismiss() } + .setNeutralButton(R.string.share) { hatchingDialog, _ -> + val event1 = ShareEvent() + event1.sharedMessage = getString(R.string.share_hatched, potionName, eggName) + " https://habitica.com/social/hatch-pet" + val sharedImage = Bitmap.createBitmap(140, 140, Bitmap.Config.ARGB_8888) + val canvas = Canvas(sharedImage) + canvas.drawColor(ContextCompat.getColor(this, R.color.brand_300)) + petImageView.drawable.draw(canvas) + event1.shareImage = sharedImage + EventBus.getDefault().post(event1) + hatchingDialog.dismiss() + } + .create() + dialog.show() + }, RxErrorHandler.handleEmptyError()) + } + + @Subscribe + fun onEvent(event: FeedCommand) { + if (event.usingFood == null || event.usingPet == null) { + return + } + val pet = event.usingPet + this.inventoryRepository.feedPet(event.usingPet, event.usingFood) + .subscribe(Consumer { feedResponse -> + HabiticaSnackbar.showSnackbar(floatingMenuWrapper, getString(R.string.notification_pet_fed, pet.colorText, pet.animalText), SnackbarDisplayType.NORMAL) + if (feedResponse.value == -1) { + val mountWrapper = View.inflate(this, R.layout.pet_imageview, null) as FrameLayout + val mountImageView = mountWrapper.findViewById(R.id.pet_imageview) as SimpleDraweeView + + DataBindingUtils.loadImage(mountImageView, "Mount_Icon_" + event.usingPet.key) + val colorName = event.usingPet.colorText + val animalName = event.usingPet.animalText + val dialog = AlertDialog.Builder(this@MainActivity) + .setTitle(getString(R.string.evolved_pet_title, colorName, animalName)) + .setView(mountWrapper) + .setPositiveButton(R.string.close) { hatchingDialog, _ -> hatchingDialog.dismiss() } + .setNeutralButton(R.string.share) { hatchingDialog, _ -> + val event1 = ShareEvent() + event1.sharedMessage = getString(R.string.share_raised, colorName, animalName) + " https://habitica.com/social/raise-pet" + val sharedImage = Bitmap.createBitmap(99, 99, Bitmap.Config.ARGB_8888) + val canvas = Canvas(sharedImage) + canvas.drawColor(ContextCompat.getColor(this, R.color.brand_300)) + mountImageView.drawable.draw(canvas) + event1.shareImage = sharedImage + EventBus.getDefault().post(event1) + hatchingDialog.dismiss() + } + .create() + dialog.show() + } + }, RxErrorHandler.handleEmptyError()) + } + + // endregion + + private fun displayTaskScoringResponse(data: TaskScoringResult?) { + if (user != null && data != null) { + notifyUserUseCase.observable(NotifyUserUseCase.RequestValues(this, floatingMenuWrapper, + user, data.experienceDelta, data.healthDelta, data.goldDelta, data.manaDelta, data.questDamage, data.hasLeveledUp)) + .subscribe(Consumer { }, RxErrorHandler.handleEmptyError()) + } + + displayItemDropUseCase.observable(DisplayItemDropUseCase.RequestValues(data, this, floatingMenuWrapper)) + .subscribe(Consumer { }, RxErrorHandler.handleEmptyError()) + } + + + private fun displayDeathDialogIfNeeded() { + + if (user?.stats?.getHp() == null || user?.stats?.getHp() ?: 0.toDouble() > 0) { + return + } + + if (this.faintDialog == null && !this.isFinishing) { + + val customView = View.inflate(this, R.layout.dialog_faint, null) + if (customView != null) { + val hpBarView = customView.findViewById(R.id.hpBar) as ValueBar + + hpBarView.setLightBackground(true) + hpBarView.setIcon(HabiticaIconsHelper.imageOfHeartLightBg()) + + val dialogAvatarView = customView.findViewById(R.id.avatarView) as AvatarView + user.notNull { dialogAvatarView.setAvatar(it) } + } + + this.faintDialog = AlertDialog.Builder(this) + .setTitle(R.string.faint_header) + .setView(customView) + .setPositiveButton(R.string.faint_button) { _, _ -> + faintDialog = null + user.notNull { + userRepository.revive(it).subscribe(Consumer { }, RxErrorHandler.handleEmptyError()) + } + } + .create() + + soundManager.loadAndPlayAudio(SoundManager.SoundDeath) + this.faintDialog?.show() + } + } + + + override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean { + if (keyCode == KeyEvent.KEYCODE_MENU) { + drawerFragment?.openDrawer() + return true + } + + return super.onKeyUp(keyCode, event) + } + + protected fun retrieveUser() { + if (hostConfig.hasAuthentication()) { + this.userRepository.retrieveUser(true) + .doOnNext { user1 -> + pushNotificationManager.setUser(user1) + pushNotificationManager.addPushDeviceUsingStoredToken() + } + .flatMap { userRepository.retrieveInboxMessages() } + .flatMap { inventoryRepository.retrieveContent(false) } + .flatMap { inventoryRepository.retrieveWorldState() } + .subscribe(Consumer { }, RxErrorHandler.handleEmptyError()) + } + } + + @Subscribe + fun displayClassSelectionActivity(event: SelectClassEvent) { + checkClassSelectionUseCase.observable(CheckClassSelectionUseCase.RequestValues(user, event, this)) + .subscribe(Consumer { }, RxErrorHandler.handleEmptyError()) + } + + private fun displayTutorialStep(step: TutorialStep, text: String, canBeDeferred: Boolean) { + val view = TutorialView(this, step, this) + this.activeTutorialView = view + view.setTutorialText(text) + view.onReaction = this + view.setCanBeDeferred(canBeDeferred) + this.overlayLayout.addView(view) + + val additionalData = HashMap() + additionalData["eventLabel"] = step.identifier + "-android" + additionalData["eventValue"] = step.identifier ?: "" + additionalData["complete"] = false + AmplitudeManager.sendEvent("tutorial", AmplitudeManager.EVENT_CATEGORY_BEHAVIOUR, AmplitudeManager.EVENT_HITTYPE_EVENT, additionalData) + } + + private fun displayTutorialStep(step: TutorialStep, texts: List, canBeDeferred: Boolean) { + val view = TutorialView(this, step, this) + this.activeTutorialView = view + view.setTutorialTexts(texts) + view.onReaction = this + view.setCanBeDeferred(canBeDeferred) + this.overlayLayout.addView(view) + + val additionalData = HashMap() + additionalData["eventLabel"] = step.identifier + "-android" + additionalData["eventValue"] = step.identifier ?: "" + additionalData["complete"] = false + AmplitudeManager.sendEvent("tutorial", AmplitudeManager.EVENT_CATEGORY_BEHAVIOUR, AmplitudeManager.EVENT_HITTYPE_EVENT, additionalData) + } + + override fun onTutorialCompleted(step: TutorialStep) { + val path = "flags.tutorial." + step.tutorialGroup + "." + step.identifier + val updateData = HashMap() + updateData[path] = true + userRepository.updateUser(user, updateData) + .subscribe(Consumer { }, RxErrorHandler.handleEmptyError()) + this.overlayLayout.removeView(this.activeTutorialView) + this.removeActiveTutorialView() + + val additionalData = HashMap() + additionalData["eventLabel"] = step.identifier + "-android" + additionalData["eventValue"] = step.identifier ?: "" + additionalData["complete"] = true + AmplitudeManager.sendEvent("tutorial", AmplitudeManager.EVENT_CATEGORY_BEHAVIOUR, AmplitudeManager.EVENT_HITTYPE_EVENT, additionalData) + } + + override fun onTutorialDeferred(step: TutorialStep) { + taskRepository.executeTransaction(Realm.Transaction { step.displayedOn = Date() }) + + this.removeActiveTutorialView() + } + + private fun removeActiveTutorialView() { + if (this.activeTutorialView != null) { + this.overlayLayout.removeView(this.activeTutorialView) + this.activeTutorialView = null + } + } + + @Subscribe + fun shareEvent(event: ShareEvent) { + val sharingIntent = Intent(Intent.ACTION_SEND) + sharingIntent.type = "*/*" + sharingIntent.putExtra(Intent.EXTRA_TEXT, event.sharedMessage) + val f = BitmapUtils.saveToShareableFile(filesDir.toString() + "/shared_images", "share.png", event.shareImage) + val fileUri = FileProvider.getUriForFile(this, getString(R.string.content_provider), f) + sharingIntent.putExtra(Intent.EXTRA_STREAM, fileUri) + val resInfoList = this.packageManager.queryIntentActivities(sharingIntent, PackageManager.MATCH_DEFAULT_ONLY) + for (resolveInfo in resInfoList) { + val packageName = resolveInfo.activityInfo.packageName + this.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + startActivity(Intent.createChooser(sharingIntent, getString(R.string.share_using))) + } + + @Subscribe + fun onEvent(event: TaskCheckedCommand) { + when (event.Task.type) { + Task.TYPE_DAILY -> { + dailyCheckUseCase.observable(DailyCheckUseCase.RequestValues(user, event.Task, !event.Task.completed)) + .subscribe(Consumer { this.displayTaskScoringResponse(it) }, RxErrorHandler.handleEmptyError()) + } + Task.TYPE_TODO -> { + todoCheckUseCase.observable(TodoCheckUseCase.RequestValues(user, event.Task, !event.Task.completed)) + .subscribe(Consumer { this.displayTaskScoringResponse(it) }, RxErrorHandler.handleEmptyError()) + } + } + } + + @Subscribe + fun onEvent(event: ChecklistCheckedCommand) { + checklistCheckUseCase.observable(ChecklistCheckUseCase.RequestValues(event.task.id, event.item.id)).subscribe(Consumer { }, RxErrorHandler.handleEmptyError()) + } + + @Subscribe + fun onEvent(event: HabitScoreEvent) { + habitScoreUseCase.observable(HabitScoreUseCase.RequestValues(user, event.habit, event.Up)) + .subscribe(Consumer { this.displayTaskScoringResponse(it) }, RxErrorHandler.handleEmptyError()) + } + + private fun checkMaintenance() { + this.maintenanceService.maintenanceStatus + .compose(apiClient.configureApiCallObserver()) + .subscribe(Consumer { maintenanceResponse -> + if (maintenanceResponse == null) { + return@Consumer + } + if (maintenanceResponse.activeMaintenance) { + val intent = createMaintenanceIntent(maintenanceResponse, false) + startActivity(intent) + } else { + if (maintenanceResponse.minBuild != null) { + try { + val packageInfo = packageManager.getPackageInfo(packageName, 0) + if (packageInfo.versionCode < maintenanceResponse.minBuild) { + val intent = createMaintenanceIntent(maintenanceResponse, true) + startActivity(intent) + } + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + } + + } + } + }, RxErrorHandler.handleEmptyError()) + } + + private fun createMaintenanceIntent(maintenanceResponse: MaintenanceResponse, isDeprecationNotice: Boolean): Intent { + val intent = Intent(this, MaintenanceActivity::class.java) + val data = Bundle() + data.putString("title", maintenanceResponse.title) + data.putString("imageUrl", maintenanceResponse.imageUrl) + data.putString("description", maintenanceResponse.description) + data.putBoolean("deprecationNotice", isDeprecationNotice) + intent.putExtras(data) + return intent + } + + @Subscribe + fun showSnackBarEvent(event: ShowSnackbarEvent) { + HabiticaSnackbar.showSnackbar(floatingMenuWrapper, event.leftImage, event.title, event.text, event.specialView, event.rightIcon, event.rightTextColor, event.rightText, event.type) + } + + @Subscribe + fun showCheckinDialog(event: ShowCheckinDialog) { + val title = event.notification.data.message + + val factory = LayoutInflater.from(this) + val view = factory.inflate(R.layout.dialog_login_incentive, null) + + val imageView = view.findViewById(R.id.imageView) as SimpleDraweeView + val imageKey = event.notification.data.rewardKey[0] + DataBindingUtils.loadImage(imageView, imageKey) + + val youEarnedMessage = this.getString(R.string.checkInRewardEarned, event.notification.data.rewardText) + + val titleTextView = TextView(this) + titleTextView.setBackgroundResource(R.color.blue_100) + titleTextView.setTextColor(ContextCompat.getColor(this, R.color.white)) + val density = this.resources.displayMetrics.density + val paddingDp = (16 * density).toInt() + titleTextView.setPadding(paddingDp, paddingDp, paddingDp, paddingDp) + titleTextView.textSize = 18f + titleTextView.gravity = Gravity.CENTER_HORIZONTAL + titleTextView.text = title + + val youEarnedTexView = view.findViewById(R.id.you_earned_message) as TextView + youEarnedTexView.text = youEarnedMessage + + val nextUnlockTextView = view.findViewById(R.id.next_unlock_message) as TextView + nextUnlockTextView.text = event.nextUnlockText + + val builder = AlertDialog.Builder(this, R.style.AlertDialogTheme) + .setView(view) + .setCustomTitle(titleTextView) + .setPositiveButton(R.string.start_day) { _, _ -> + apiClient.readNotification(event.notification.id) + .subscribe(Consumer { }, RxErrorHandler.handleEmptyError()) + } + .setMessage("") + + Completable.complete() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(Action { + val dialog = builder.create() + dialog.show() + }, RxErrorHandler.handleEmptyError()) + } + + companion object { + + const val SELECT_CLASS_RESULT = 11 + const val GEM_PURCHASE_REQUEST = 111 + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MaintenanceActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MaintenanceActivity.java deleted file mode 100644 index e472f2448..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MaintenanceActivity.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.habitrpg.android.habitica.ui.activities; - -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.text.method.LinkMovementMethod; -import android.view.View; -import android.widget.Button; -import android.widget.TextView; - -import com.facebook.drawee.view.SimpleDraweeView; -import com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.api.MaintenanceApiService; -import com.habitrpg.android.habitica.components.AppComponent; -import com.habitrpg.android.habitica.data.ApiClient; -import com.habitrpg.android.habitica.helpers.RxErrorHandler; -import com.habitrpg.android.habitica.ui.helpers.MarkdownParser; - -import net.pherth.android.emoji_library.EmojiTextView; - -import javax.inject.Inject; - -import butterknife.BindView; -import butterknife.OnClick; - -public class MaintenanceActivity extends BaseActivity { - - @Inject - public MaintenanceApiService maintenanceService; - - @Inject - public ApiClient apiClient; - - @BindView(R.id.titleTextView) - TextView titleTextView; - @BindView(R.id.imageView) - SimpleDraweeView imageView; - @BindView(R.id.descriptionTextView) - EmojiTextView descriptionTextView; - @BindView(R.id.playStoreButton) - Button playStoreButton; - private Boolean isDeprecationNotice; - - @Override - protected int getLayoutResId() { - return R.layout.activity_maintenance; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - Bundle data = getIntent().getExtras(); - - this.titleTextView.setText(data.getString("title")); - - imageView.setImageURI(Uri.parse(data.getString("imageUrl"))); - this.descriptionTextView.setText(MarkdownParser.INSTANCE.parseMarkdown(data.getString("description"))); - this.descriptionTextView.setMovementMethod(LinkMovementMethod.getInstance()); - - isDeprecationNotice = data.getBoolean("deprecationNotice"); - if (isDeprecationNotice) { - this.playStoreButton.setVisibility(View.VISIBLE); - } else { - this.playStoreButton.setVisibility(View.GONE); - } - } - - @Override - protected void injectActivity(AppComponent component) { - component.inject(this); - } - - @Override - protected void onResume() { - super.onResume(); - if (!isDeprecationNotice) { - this.maintenanceService.getMaintenanceStatus() - .compose(apiClient.configureApiCallObserver()) - .subscribe(maintenanceResponse -> { - if (!maintenanceResponse.activeMaintenance) { - finish(); - } - }, RxErrorHandler.handleEmptyError()); - } - } - - @OnClick(R.id.playStoreButton) - public void openInPlayStore() { - final String appPackageName = getPackageName(); - try { - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + appPackageName))); - } catch (android.content.ActivityNotFoundException anfe) { - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=" + appPackageName))); - } - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MaintenanceActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MaintenanceActivity.kt new file mode 100644 index 000000000..8cf5ca0f7 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MaintenanceActivity.kt @@ -0,0 +1,87 @@ +package com.habitrpg.android.habitica.ui.activities + +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.text.method.LinkMovementMethod +import android.view.View +import android.widget.Button +import android.widget.TextView +import com.facebook.drawee.view.SimpleDraweeView +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.api.MaintenanceApiService +import com.habitrpg.android.habitica.components.AppComponent +import com.habitrpg.android.habitica.data.ApiClient +import com.habitrpg.android.habitica.helpers.RxErrorHandler +import com.habitrpg.android.habitica.ui.helpers.MarkdownParser +import com.habitrpg.android.habitica.ui.helpers.bindView +import io.reactivex.functions.Consumer +import net.pherth.android.emoji_library.EmojiTextView +import javax.inject.Inject + +class MaintenanceActivity : BaseActivity() { + + @Inject + lateinit var maintenanceService: MaintenanceApiService + + @Inject + lateinit var apiClient: ApiClient + + internal val titleTextView: TextView by bindView(R.id.titleTextView) + internal val imageView: SimpleDraweeView by bindView(R.id.imageView) + internal val descriptionTextView: EmojiTextView by bindView(R.id.descriptionTextView) + internal val playStoreButton: Button by bindView(R.id.playStoreButton) + private var isDeprecationNotice: Boolean = false + + override fun getLayoutResId(): Int { + return R.layout.activity_maintenance + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val data = intent.extras + + this.titleTextView.text = data?.getString("title") + + imageView.setImageURI(Uri.parse(data.getString("imageUrl"))) + this.descriptionTextView.text = MarkdownParser.parseMarkdown(data.getString("description")) + this.descriptionTextView.movementMethod = LinkMovementMethod.getInstance() + + isDeprecationNotice = data.getBoolean("deprecationNotice") + if (isDeprecationNotice) { + this.playStoreButton.visibility = View.VISIBLE + } else { + this.playStoreButton.visibility = View.GONE + } + + playStoreButton.setOnClickListener { openInPlayStore() } + } + + override fun injectActivity(component: AppComponent?) { + component?.inject(this) + } + + override fun onResume() { + super.onResume() + if (!isDeprecationNotice) { + this.maintenanceService.maintenanceStatus + .compose(apiClient.configureApiCallObserver()) + .subscribe(Consumer { maintenanceResponse -> + if (!maintenanceResponse.activeMaintenance) { + finish() + } + }, RxErrorHandler.handleEmptyError()) + } + } + + fun openInPlayStore() { + val appPackageName = packageName + try { + startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=$appPackageName"))) + } catch (anfe: android.content.ActivityNotFoundException) { + startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=$appPackageName"))) + } + + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/PartyInviteActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/PartyInviteActivity.java deleted file mode 100644 index 5933f5ca7..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/PartyInviteActivity.java +++ /dev/null @@ -1,189 +0,0 @@ -package com.habitrpg.android.habitica.ui.activities; - -import android.app.Activity; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.support.design.widget.TabLayout; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentPagerAdapter; -import android.support.v4.view.ViewPager; -import android.view.Menu; -import android.view.MenuItem; -import android.widget.Toast; - -import com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.components.AppComponent; -import com.habitrpg.android.habitica.data.SocialRepository; -import com.habitrpg.android.habitica.data.UserRepository; -import com.habitrpg.android.habitica.helpers.RxErrorHandler; -import com.habitrpg.android.habitica.models.user.User; -import com.habitrpg.android.habitica.modules.AppModule; -import com.habitrpg.android.habitica.prefs.scanner.IntentIntegrator; -import com.habitrpg.android.habitica.prefs.scanner.IntentResult; -import com.habitrpg.android.habitica.ui.fragments.social.party.PartyInviteFragment; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.inject.Inject; -import javax.inject.Named; - -import butterknife.BindView; - -public class PartyInviteActivity extends BaseActivity { - - public static final int RESULT_SEND_INVITES = 100; - public static final String USER_IDS_KEY = "userIDs"; - public static final String IS_EMAIL_KEY = "isEmail"; - public static final String EMAILS_KEY = "emails"; - @Inject - @Named(AppModule.NAMED_USER_ID) - protected String userId; - @Inject - SocialRepository socialRepository; - @Inject - UserRepository userRepository; - @BindView(R.id.tab_layout) - TabLayout tabLayout; - @BindView(R.id.viewPager) - ViewPager viewPager; - List fragments = new ArrayList<>(); - private String userIdToInvite; - - @Override - protected int getLayoutResId() { - return R.layout.activity_party_invite; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - viewPager.setCurrentItem(0); - - setViewPagerAdapter(); - } - - @Override - protected void injectActivity(AppComponent component) { - component.inject(this); - } - - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - // Inflate the menu; this adds items to the action bar if it is present. - getMenuInflater().inflate(R.menu.menu_party_invite, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - // Handle action bar item clicks here. The action bar will - // automatically handle clicks on the Home/Up button, so long - // as you specify a parent activity in AndroidManifest.xml. - int id = item.getItemId(); - - //noinspection SimplifiableIfStatement - if (id == R.id.action_send_invites) { - setResult(Activity.RESULT_OK, createResultIntent()); - finish(); - return true; - } - - return super.onOptionsItemSelected(item); - } - - private Intent createResultIntent() { - Intent intent = new Intent(); - PartyInviteFragment fragment = fragments.get(viewPager.getCurrentItem()); - if (viewPager.getCurrentItem() == 0) { - intent.putExtra(PartyInviteActivity.IS_EMAIL_KEY, true); - intent.putExtra(PartyInviteActivity.EMAILS_KEY, fragment.getValues()); - } else { - intent.putExtra(PartyInviteActivity.IS_EMAIL_KEY, false); - intent.putExtra(PartyInviteActivity.USER_IDS_KEY, fragment.getValues()); - } - return intent; - } - - public void setViewPagerAdapter() { - android.support.v4.app.FragmentManager fragmentManager = getSupportFragmentManager(); - - viewPager.setAdapter(new FragmentPagerAdapter(fragmentManager) { - - @Override - public Fragment getItem(int position) { - - PartyInviteFragment fragment = new PartyInviteFragment(); - fragment.setEmailInvite(position == 0); - if (fragments.size() > position) { - fragments.set(position, fragment); - } else { - fragments.add(fragment); - } - - return fragment; - } - - @Override - public int getCount() { - return 2; - } - - @Override - public CharSequence getPageTitle(int position) { - switch (position) { - case 0: - return getString(R.string.by_email); - case 1: - return getString(R.string.invite_existing_users); - } - return ""; - } - }); - - if (tabLayout != null && viewPager != null) { - tabLayout.setupWithViewPager(viewPager); - } - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - IntentResult scanningResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data); - - if (scanningResult != null && scanningResult.getContents() != null) { - String qrCodeUrl = scanningResult.getContents(); - Uri uri = Uri.parse(qrCodeUrl); - if (uri == null || uri.getPathSegments().size() < 3) { - return; - } - userIdToInvite = uri.getPathSegments().get(2); - - userRepository.getUser(userId).subscribe(this::handleUserRecieved, RxErrorHandler.handleEmptyError()); - } - } - - public void handleUserRecieved(User user) { - - if (this.userIdToInvite == null) { - return; - } - - Toast toast = Toast.makeText(getApplicationContext(), - "Invited: " + userIdToInvite, Toast.LENGTH_LONG); - toast.show(); - - Map inviteData = new HashMap<>(); - List invites = new ArrayList<>(); - invites.add(userIdToInvite); - inviteData.put("uuids", invites); - - this.socialRepository.inviteToGroup(user.getParty().getId(), inviteData) - .subscribe(aVoid -> {}, RxErrorHandler.handleEmptyError()); - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/PartyInviteActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/PartyInviteActivity.kt new file mode 100644 index 000000000..9b865dd74 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/PartyInviteActivity.kt @@ -0,0 +1,174 @@ +package com.habitrpg.android.habitica.ui.activities + +import android.app.Activity +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.support.design.widget.TabLayout +import android.support.v4.app.Fragment +import android.support.v4.app.FragmentPagerAdapter +import android.support.v4.view.ViewPager +import android.view.Menu +import android.view.MenuItem +import android.widget.Toast +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.components.AppComponent +import com.habitrpg.android.habitica.data.SocialRepository +import com.habitrpg.android.habitica.data.UserRepository +import com.habitrpg.android.habitica.extensions.notNull +import com.habitrpg.android.habitica.helpers.RxErrorHandler +import com.habitrpg.android.habitica.models.user.User +import com.habitrpg.android.habitica.modules.AppModule +import com.habitrpg.android.habitica.prefs.scanner.IntentIntegrator +import com.habitrpg.android.habitica.ui.fragments.social.party.PartyInviteFragment +import com.habitrpg.android.habitica.ui.helpers.bindView +import io.reactivex.functions.Consumer +import java.util.* +import javax.inject.Inject +import javax.inject.Named + +class PartyInviteActivity : BaseActivity() { + + @field:[Inject Named(AppModule.NAMED_USER_ID)] + lateinit var userId: String + @Inject + lateinit var socialRepository: SocialRepository + @Inject + lateinit var userRepository: UserRepository + + internal val tabLayout: TabLayout by bindView(R.id.tab_layout) + internal val viewPager: ViewPager by bindView(R.id.viewPager) + + internal var fragments: MutableList = ArrayList() + private var userIdToInvite: String? = null + + override fun getLayoutResId(): Int { + return R.layout.activity_party_invite + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + viewPager.currentItem = 0 + + setViewPagerAdapter() + } + + override fun injectActivity(component: AppComponent?) { + component?.inject(this) + } + + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + // Inflate the menu; this adds items to the action bar if it is present. + menuInflater.inflate(R.menu.menu_party_invite, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + val id = item.itemId + + + if (id == R.id.action_send_invites) { + setResult(Activity.RESULT_OK, createResultIntent()) + finish() + return true + } + + return super.onOptionsItemSelected(item) + } + + private fun createResultIntent(): Intent { + val intent = Intent() + val fragment = fragments[viewPager.currentItem] + if (viewPager.currentItem == 0) { + intent.putExtra(PartyInviteActivity.IS_EMAIL_KEY, true) + intent.putExtra(PartyInviteActivity.EMAILS_KEY, fragment.values) + } else { + intent.putExtra(PartyInviteActivity.IS_EMAIL_KEY, false) + intent.putExtra(PartyInviteActivity.USER_IDS_KEY, fragment.values) + } + return intent + } + + private fun setViewPagerAdapter() { + val fragmentManager = supportFragmentManager + + viewPager.adapter = object : FragmentPagerAdapter(fragmentManager) { + + override fun getItem(position: Int): Fragment { + + val fragment = PartyInviteFragment() + fragment.isEmailInvite = position == 0 + if (fragments.size > position) { + fragments[position] = fragment + } else { + fragments.add(fragment) + } + + return fragment + } + + override fun getCount(): Int { + return 2 + } + + override fun getPageTitle(position: Int): CharSequence? { + when (position) { + 0 -> return getString(R.string.by_email) + 1 -> return getString(R.string.invite_existing_users) + } + return "" + } + } + + tabLayout.setupWithViewPager(viewPager) + } + + public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) { + super.onActivityResult(requestCode, resultCode, data) + + val scanningResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data) + + if (scanningResult != null && scanningResult.contents != null) { + val qrCodeUrl = scanningResult.contents + val uri = Uri.parse(qrCodeUrl) + if (uri == null || uri.pathSegments.size < 3) { + return + } + userIdToInvite = uri.pathSegments[2] + + userRepository.getUser(userId).subscribe(Consumer { this.handleUserReceived(it) }, RxErrorHandler.handleEmptyError()) + } + } + + private fun handleUserReceived(user: User) { + if (this.userIdToInvite == null) { + return + } + + val toast = Toast.makeText(applicationContext, + "Invited: " + userIdToInvite!!, Toast.LENGTH_LONG) + toast.show() + + val inviteData = HashMap() + val invites = ArrayList() + userIdToInvite.notNull { + invites.add(it) + } + inviteData["uuids"] = invites + + this.socialRepository.inviteToGroup(user.party.getId(), inviteData) + .subscribe(Consumer { }, RxErrorHandler.handleEmptyError()) + } + + companion object { + + const val RESULT_SEND_INVITES = 100 + const val USER_IDS_KEY = "userIDs" + const val IS_EMAIL_KEY = "isEmail" + const val EMAILS_KEY = "emails" + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/PrefsActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/PrefsActivity.kt index 13be20681..2cbf57e60 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/PrefsActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/PrefsActivity.kt @@ -26,8 +26,8 @@ class PrefsActivity : BaseActivity(), PreferenceFragmentCompat.OnPreferenceStart .commit() } - override fun injectActivity(component: AppComponent) { - component.inject(this) + override fun injectActivity(component: AppComponent?) { + component?.inject(this) } override fun onSupportNavigateUp(): Boolean { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SetupActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SetupActivity.java deleted file mode 100644 index 0a654ce15..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SetupActivity.java +++ /dev/null @@ -1,293 +0,0 @@ -package com.habitrpg.android.habitica.ui.activities; - -import android.content.Intent; -import android.content.SharedPreferences; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentPagerAdapter; -import android.support.v4.content.ContextCompat; -import android.support.v4.view.ViewPager; -import android.support.v7.content.res.AppCompatResources; -import android.support.v7.preference.PreferenceManager; -import android.view.View; -import android.view.Window; -import android.widget.Button; - -import com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.api.HostConfig; -import com.habitrpg.android.habitica.components.AppComponent; -import com.habitrpg.android.habitica.data.ApiClient; -import com.habitrpg.android.habitica.data.TaskRepository; -import com.habitrpg.android.habitica.data.UserRepository; -import com.habitrpg.android.habitica.events.commands.EquipCommand; -import com.habitrpg.android.habitica.events.commands.UpdateUserCommand; -import com.habitrpg.android.habitica.helpers.AmplitudeManager; -import com.habitrpg.android.habitica.helpers.RxErrorHandler; -import com.habitrpg.android.habitica.models.tasks.Task; -import com.habitrpg.android.habitica.models.user.User; -import com.habitrpg.android.habitica.ui.fragments.setup.AvatarSetupFragment; -import com.habitrpg.android.habitica.ui.fragments.setup.TaskSetupFragment; -import com.habitrpg.android.habitica.ui.fragments.setup.WelcomeFragment; -import com.habitrpg.android.habitica.ui.views.FadingViewPager; -import com.viewpagerindicator.IconPageIndicator; -import com.viewpagerindicator.IconPagerAdapter; - -import org.greenrobot.eventbus.Subscribe; - -import java.util.Calendar; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -import javax.inject.Inject; - -import butterknife.BindView; -import butterknife.OnClick; -import io.reactivex.Flowable; - -public class SetupActivity extends BaseActivity implements ViewPager.OnPageChangeListener { - - @Inject - public ApiClient apiClient; - @Inject - protected HostConfig hostConfig; - @Inject - protected UserRepository userRepository; - @Inject - protected TaskRepository taskRepository; - @BindView(R.id.viewPager) - FadingViewPager pager; - @BindView(R.id.nextButton) - Button nextButton; - @BindView(R.id.previousButton) - Button previousButton; - @BindView(R.id.view_pager_indicator) - IconPageIndicator indicator; - AvatarSetupFragment avatarSetupFragment; - TaskSetupFragment taskSetupFragment; - @Nullable - User user; - boolean completedSetup = false; - - @Override - protected int getLayoutResId() { - return R.layout.activity_setup; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - compositeSubscription.add(userRepository.getUser(hostConfig.getUser()) - .flatMap(user -> { - if (user == null) { - return userRepository.retrieveUser(true); - } else { - return Flowable.just(user); - } - }) - .subscribe(this::onUserReceived, RxErrorHandler.handleEmptyError())); - - Map additionalData = new HashMap<>(); - additionalData.put("status", "displayed"); - AmplitudeManager.sendEvent("setup", AmplitudeManager.EVENT_CATEGORY_BEHAVIOUR, AmplitudeManager.EVENT_HITTYPE_EVENT, additionalData); - - String currentDeviceLanguage = Locale.getDefault().getLanguage(); - for (String language : getResources().getStringArray(R.array.LanguageValues)) { - if (language.equals(currentDeviceLanguage)) { - apiClient.registrationLanguage(currentDeviceLanguage) - .subscribe(habitRPGUser -> {}, RxErrorHandler.handleEmptyError()); - } - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - Window window = getWindow(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - View decor = getWindow().getDecorView(); - decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); - window.setStatusBarColor(ContextCompat.getColor(this, R.color.light_gray_bg)); - } else { - window.setStatusBarColor(ContextCompat.getColor(this, R.color.days_gray)); - } - } - - pager.disableFading = true; - } - - @Override - protected void injectActivity(AppComponent component) { - component.inject(this); - } - - @Override - protected void onDestroy() { - userRepository.close(); - super.onDestroy(); - } - - private void setupViewpager() { - android.support.v4.app.FragmentManager fragmentManager = getSupportFragmentManager(); - - pager.setAdapter(new ViewPageAdapter(fragmentManager)); - - pager.addOnPageChangeListener(this); - indicator.setViewPager(pager); - } - - @Subscribe - public void onEvent(UpdateUserCommand event) { - this.userRepository.updateUser(user, event.updateData) - .subscribe(this::onUserReceived, RxErrorHandler.handleEmptyError()); - } - - @Subscribe - public void onEvent(EquipCommand event) { - this.apiClient.equipItem(event.type, event.key) - .subscribe(items -> {}, RxErrorHandler.handleEmptyError()); - } - - @OnClick(R.id.nextButton) - public void nextClicked(View v) { - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - SharedPreferences.Editor editor = sharedPreferences.edit(); - editor.putString("FirstDayOfTheWeek", - Integer.toString(Calendar.getInstance().getFirstDayOfWeek())); - editor.apply(); - if (isLastPage()) { - if (this.taskSetupFragment == null) { - return; - } - List newTasks = this.taskSetupFragment.createSampleTasks(); - this.completedSetup = true; - this.taskRepository.createTasks(newTasks) - .subscribe(tasks -> onUserReceived(user), RxErrorHandler.handleEmptyError()); - } - this.pager.setCurrentItem(this.pager.getCurrentItem() + 1); - } - - @OnClick(R.id.previousButton) - public void previousClicked() { - this.pager.setCurrentItem(this.pager.getCurrentItem() - 1); - } - - private void setPreviousButtonEnabled(boolean enabled) { - Drawable leftDrawable; - if (enabled) { - previousButton.setText(R.string.action_back); - leftDrawable = AppCompatResources.getDrawable(this, R.drawable.back_arrow_enabled); - } else { - previousButton.setText(null); - leftDrawable = AppCompatResources.getDrawable(this, R.drawable.back_arrow_disabled); - } - previousButton.setCompoundDrawablesWithIntrinsicBounds(leftDrawable, null, null, null); - } - - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - - } - - @Override - public void onPageSelected(int position) { - if (position == 0) { - this.setPreviousButtonEnabled(false); - this.nextButton.setText(this.getString(R.string.next_button)); - } else if (isLastPage()) { - this.setPreviousButtonEnabled(true); - this.nextButton.setText(this.getString(R.string.intro_finish_button)); - } else { - this.setPreviousButtonEnabled(true); - this.nextButton.setText(this.getString(R.string.next_button)); - } - } - - @Override - public void onPageScrollStateChanged(int state) { - - } - - public void onUserReceived(User user) { - if (completedSetup) { - if (compositeSubscription != null && !compositeSubscription.isDisposed()) { - compositeSubscription.dispose(); - } - this.startMainActivity(); - return; - } - this.user = user; - if (this.pager.getAdapter() == null) { - this.setupViewpager(); - } else { - if (this.avatarSetupFragment != null) { - this.avatarSetupFragment.setUser(user); - } - if (this.taskSetupFragment != null) { - this.taskSetupFragment.setUser(user); - } - } - - Map additionalData = new HashMap<>(); - additionalData.put("status", "completed"); - AmplitudeManager.sendEvent("setup", AmplitudeManager.EVENT_CATEGORY_BEHAVIOUR, AmplitudeManager.EVENT_HITTYPE_EVENT, additionalData); - } - - private void startMainActivity() { - Intent intent = new Intent(SetupActivity.this, MainActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); - finish(); - } - - private class ViewPageAdapter extends FragmentPagerAdapter implements IconPagerAdapter { - - public ViewPageAdapter(FragmentManager fm) { - super(fm); - } - - @Override - public Fragment getItem(int position) { - Fragment fragment; - - switch (position) { - case 1: { - avatarSetupFragment = new AvatarSetupFragment(); - avatarSetupFragment.setActivity(SetupActivity.this); - avatarSetupFragment.setUser(user); - avatarSetupFragment.setWidth(pager.getWidth()); - fragment = avatarSetupFragment; - break; - } - case 2: { - taskSetupFragment = new TaskSetupFragment(); - taskSetupFragment.setUser(user); - fragment = taskSetupFragment; - break; - } - default: { - fragment = new WelcomeFragment(); - break; - } - } - - return fragment; - } - - @Override - public int getCount() { - return 3; - } - - @Override - public int getIconResId(int index) { - return R.drawable.indicator_diamond; - } - } - - private boolean isLastPage() { - return this.pager == null || this.pager.getAdapter() == null || this.pager.getCurrentItem() == this.pager.getAdapter().getCount() - 1; - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SetupActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SetupActivity.kt new file mode 100644 index 000000000..5ea9dfc28 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SetupActivity.kt @@ -0,0 +1,249 @@ +package com.habitrpg.android.habitica.ui.activities + +import android.content.Intent +import android.graphics.drawable.Drawable +import android.os.Build +import android.os.Bundle +import android.support.v4.app.Fragment +import android.support.v4.app.FragmentManager +import android.support.v4.app.FragmentPagerAdapter +import android.support.v4.content.ContextCompat +import android.support.v4.view.ViewPager +import android.support.v7.content.res.AppCompatResources +import android.support.v7.preference.PreferenceManager +import android.view.View +import android.widget.Button +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.api.HostConfig +import com.habitrpg.android.habitica.components.AppComponent +import com.habitrpg.android.habitica.data.ApiClient +import com.habitrpg.android.habitica.data.TaskRepository +import com.habitrpg.android.habitica.data.UserRepository +import com.habitrpg.android.habitica.events.commands.EquipCommand +import com.habitrpg.android.habitica.events.commands.UpdateUserCommand +import com.habitrpg.android.habitica.extensions.notNull +import com.habitrpg.android.habitica.helpers.AmplitudeManager +import com.habitrpg.android.habitica.helpers.RxErrorHandler +import com.habitrpg.android.habitica.models.user.User +import com.habitrpg.android.habitica.ui.fragments.setup.AvatarSetupFragment +import com.habitrpg.android.habitica.ui.fragments.setup.TaskSetupFragment +import com.habitrpg.android.habitica.ui.fragments.setup.WelcomeFragment +import com.habitrpg.android.habitica.ui.helpers.bindView +import com.habitrpg.android.habitica.ui.views.FadingViewPager +import com.viewpagerindicator.IconPageIndicator +import com.viewpagerindicator.IconPagerAdapter +import io.reactivex.functions.Consumer +import org.greenrobot.eventbus.Subscribe +import java.util.* +import javax.inject.Inject + +class SetupActivity : BaseActivity(), ViewPager.OnPageChangeListener { + + @Inject + lateinit var apiClient: ApiClient + @Inject + lateinit var hostConfig: HostConfig + @Inject + lateinit var userRepository: UserRepository + @Inject + lateinit var taskRepository: TaskRepository + + private val pager: FadingViewPager by bindView(R.id.viewPager) + private val nextButton: Button by bindView(R.id.nextButton) + private val previousButton: Button by bindView(R.id.previousButton) + private val indicator: IconPageIndicator by bindView(R.id.view_pager_indicator) + + internal var avatarSetupFragment: AvatarSetupFragment? = null + internal var taskSetupFragment: TaskSetupFragment? = null + internal var user: User? = null + private var completedSetup = false + + private val isLastPage: Boolean + get() = this.pager.adapter == null || this.pager.currentItem == this.pager.adapter?.count ?: 0 - 1 + + override fun getLayoutResId(): Int { + return R.layout.activity_setup + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + compositeSubscription?.add(userRepository.getUser(hostConfig.user) + .subscribe(Consumer { this.onUserReceived(it) }, RxErrorHandler.handleEmptyError())) + + val additionalData = HashMap() + additionalData["status"] = "displayed" + AmplitudeManager.sendEvent("setup", AmplitudeManager.EVENT_CATEGORY_BEHAVIOUR, AmplitudeManager.EVENT_HITTYPE_EVENT, additionalData) + + val currentDeviceLanguage = Locale.getDefault().language + for (language in resources.getStringArray(R.array.LanguageValues)) { + if (language == currentDeviceLanguage) { + apiClient.registrationLanguage(currentDeviceLanguage) + .subscribe(Consumer { }, RxErrorHandler.handleEmptyError()) + } + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + val window = window + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + val decor = getWindow().decorView + decor.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR + window.statusBarColor = ContextCompat.getColor(this, R.color.light_gray_bg) + } else { + window.statusBarColor = ContextCompat.getColor(this, R.color.days_gray) + } + } + + pager.disableFading = true + + previousButton.setOnClickListener { previousClicked() } + nextButton.setOnClickListener { nextClicked() } + } + + override fun injectActivity(component: AppComponent?) { + component?.inject(this) + } + + override fun onDestroy() { + userRepository.close() + super.onDestroy() + } + + private fun setupViewpager() { + val fragmentManager = supportFragmentManager + + pager.adapter = ViewPageAdapter(fragmentManager) + + pager.addOnPageChangeListener(this) + indicator.setViewPager(pager) + } + + @Subscribe + fun onEvent(event: UpdateUserCommand) { + this.userRepository.updateUser(user, event.updateData) + .subscribe(Consumer { this.onUserReceived(it) }, RxErrorHandler.handleEmptyError()) + } + + @Subscribe + fun onEvent(event: EquipCommand) { + this.apiClient.equipItem(event.type, event.key) + .subscribe(Consumer { }, RxErrorHandler.handleEmptyError()) + } + + private fun nextClicked() { + val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) + val editor = sharedPreferences.edit() + editor.putString("FirstDayOfTheWeek", Integer.toString(Calendar.getInstance().firstDayOfWeek)) + editor.apply() + if (isLastPage) { + if (this.taskSetupFragment == null) { + return + } + val newTasks = this.taskSetupFragment?.createSampleTasks() + this.completedSetup = true + newTasks.notNull { + this.taskRepository.createTasks(it).subscribe(Consumer { onUserReceived(user) }, RxErrorHandler.handleEmptyError()) + } + } + this.pager.currentItem = this.pager.currentItem + 1 + } + + private fun previousClicked() { + this.pager.currentItem = this.pager.currentItem - 1 + } + + private fun setPreviousButtonEnabled(enabled: Boolean) { + val leftDrawable: Drawable? + if (enabled) { + previousButton.setText(R.string.action_back) + leftDrawable = AppCompatResources.getDrawable(this, R.drawable.back_arrow_enabled) + } else { + previousButton.text = null + leftDrawable = AppCompatResources.getDrawable(this, R.drawable.back_arrow_disabled) + } + previousButton.setCompoundDrawablesWithIntrinsicBounds(leftDrawable, null, null, null) + } + + override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { + + } + + override fun onPageSelected(position: Int) { + when { + position == 0 -> { + this.setPreviousButtonEnabled(false) + this.nextButton.text = this.getString(R.string.next_button) + } + isLastPage -> { + this.setPreviousButtonEnabled(true) + this.nextButton.text = this.getString(R.string.intro_finish_button) + } + else -> { + this.setPreviousButtonEnabled(true) + this.nextButton.text = this.getString(R.string.next_button) + } + } + } + + override fun onPageScrollStateChanged(state: Int) { + + } + + private fun onUserReceived(user: User?) { + if (completedSetup) { + if (!compositeSubscription.isDisposed) { + compositeSubscription.dispose() + } + this.startMainActivity() + return + } + this.user = user + if (this.pager.adapter == null) { + this.setupViewpager() + } else { + this.avatarSetupFragment?.setUser(user) + this.taskSetupFragment?.setUser(user) + } + + val additionalData = HashMap() + additionalData["status"] = "completed" + AmplitudeManager.sendEvent("setup", AmplitudeManager.EVENT_CATEGORY_BEHAVIOUR, AmplitudeManager.EVENT_HITTYPE_EVENT, additionalData) + } + + private fun startMainActivity() { + val intent = Intent(this@SetupActivity, MainActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK) + startActivity(intent) + finish() + } + + private inner class ViewPageAdapter(fm: FragmentManager) : FragmentPagerAdapter(fm), IconPagerAdapter { + + override fun getItem(position: Int): Fragment { + return when (position) { + 1 -> { + val fragment = AvatarSetupFragment() + fragment.activity = this@SetupActivity + fragment.setUser(user) + fragment.width = pager.width + avatarSetupFragment = fragment + fragment + } + 2 -> { + val fragment = TaskSetupFragment() + fragment.setUser(user) + taskSetupFragment = fragment + fragment + } + else -> { WelcomeFragment() } + } + } + + override fun getCount(): Int { + return 3 + } + + override fun getIconResId(index: Int): Int { + return R.drawable.indicator_diamond + } + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillMemberActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillMemberActivity.kt index 34bd81a19..190a2f9bf 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillMemberActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillMemberActivity.kt @@ -29,8 +29,8 @@ class SkillMemberActivity : BaseActivity() { return R.layout.activity_skill_members } - override fun injectActivity(component: AppComponent) { - component.inject(this) + override fun injectActivity(component: AppComponent?) { + component?.inject(this) } public override fun onCreate(savedInstanceState: Bundle?) { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillTasksActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillTasksActivity.java deleted file mode 100644 index bb31e75bb..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillTasksActivity.java +++ /dev/null @@ -1,111 +0,0 @@ -package com.habitrpg.android.habitica.ui.activities; - -import android.app.Activity; -import android.content.Intent; -import android.os.Bundle; -import android.support.design.widget.TabLayout; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentPagerAdapter; -import android.support.v4.view.ViewPager; -import android.util.SparseArray; - -import com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.components.AppComponent; -import com.habitrpg.android.habitica.data.TaskRepository; -import com.habitrpg.android.habitica.helpers.RxErrorHandler; -import com.habitrpg.android.habitica.models.tasks.Task; -import com.habitrpg.android.habitica.modules.AppModule; -import com.habitrpg.android.habitica.ui.fragments.skills.SkillTasksRecyclerViewFragment; - -import javax.inject.Inject; -import javax.inject.Named; - -import butterknife.BindView; - -public class SkillTasksActivity extends BaseActivity { - - @Inject - TaskRepository taskRepository; - @Inject - @Named(AppModule.NAMED_USER_ID) - String userId; - - @BindView(R.id.viewpager) - public ViewPager viewPager; - - @BindView(R.id.tab_layout) - public TabLayout tabLayout; - SparseArray viewFragmentsDictionary = new SparseArray<>(); - - @Override - protected int getLayoutResId() { - return R.layout.activity_skill_tasks; - } - - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - loadTaskLists(); - } - - @Override - protected void injectActivity(AppComponent component) { - component.inject(this); - } - - public void loadTaskLists() { - android.support.v4.app.FragmentManager fragmentManager = getSupportFragmentManager(); - - viewPager.setAdapter(new FragmentPagerAdapter(fragmentManager) { - - @Override - public Fragment getItem(int position) { - SkillTasksRecyclerViewFragment fragment = new SkillTasksRecyclerViewFragment(); - switch (position) { - case 0: - fragment.setTaskType(Task.TYPE_HABIT); - break; - case 1: - fragment.setTaskType(Task.TYPE_DAILY); - break; - default: - fragment.setTaskType(Task.TYPE_TODO); - } - - compositeSubscription.add(fragment.getTaskSelectionEvents().subscribe(task -> taskSelected(task), RxErrorHandler.handleEmptyError())); - - viewFragmentsDictionary.put(position, fragment); - - return fragment; - } - - @Override - public int getCount() { - return 3; - } - - @Override - public CharSequence getPageTitle(int position) { - switch (position) { - case 0: - return getString(R.string.habits); - case 1: - return getString(R.string.dailies); - case 2: - return getString(R.string.todos); - } - return ""; - } - }); - - - tabLayout.setupWithViewPager(viewPager); - } - - public void taskSelected(Task task) { - Intent resultIntent = new Intent(); - resultIntent.putExtra("task_id", task.getId()); - setResult(Activity.RESULT_OK, resultIntent); - finish(); - } -} - diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillTasksActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillTasksActivity.kt new file mode 100644 index 000000000..e006e029a --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillTasksActivity.kt @@ -0,0 +1,93 @@ +package com.habitrpg.android.habitica.ui.activities + +import android.app.Activity +import android.content.Intent +import android.os.Bundle +import android.support.design.widget.TabLayout +import android.support.v4.app.Fragment +import android.support.v4.app.FragmentPagerAdapter +import android.support.v4.view.ViewPager +import android.util.SparseArray +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.components.AppComponent +import com.habitrpg.android.habitica.data.TaskRepository +import com.habitrpg.android.habitica.helpers.RxErrorHandler +import com.habitrpg.android.habitica.models.tasks.Task +import com.habitrpg.android.habitica.modules.AppModule +import com.habitrpg.android.habitica.ui.fragments.skills.SkillTasksRecyclerViewFragment +import com.habitrpg.android.habitica.ui.helpers.bindView +import io.reactivex.functions.Consumer +import javax.inject.Inject +import javax.inject.Named + +class SkillTasksActivity : BaseActivity() { + + @Inject + lateinit var taskRepository: TaskRepository + @field:[Inject Named(AppModule.NAMED_USER_ID)] + lateinit var userId: String + + private val viewPager: ViewPager by bindView(R.id.viewPager) + private val tabLayout: TabLayout by bindView(R.id.tab_layout) + + internal var viewFragmentsDictionary = SparseArray() + + override fun getLayoutResId(): Int { + return R.layout.activity_skill_tasks + } + + public override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + loadTaskLists() + } + + override fun injectActivity(component: AppComponent?) { + component?.inject(this) + } + + private fun loadTaskLists() { + val fragmentManager = supportFragmentManager + + viewPager.adapter = object : FragmentPagerAdapter(fragmentManager) { + + override fun getItem(position: Int): Fragment { + val fragment = SkillTasksRecyclerViewFragment() + when (position) { + 0 -> fragment.taskType = Task.TYPE_HABIT + 1 -> fragment.taskType = Task.TYPE_DAILY + else -> fragment.taskType = Task.TYPE_TODO + } + + compositeSubscription.add(fragment.taskSelectionEvents.subscribe(Consumer { task -> taskSelected(task) }, RxErrorHandler.handleEmptyError())) + + viewFragmentsDictionary.put(position, fragment) + + return fragment + } + + override fun getCount(): Int { + return 3 + } + + override fun getPageTitle(position: Int): CharSequence? { + when (position) { + 0 -> return getString(R.string.habits) + 1 -> return getString(R.string.dailies) + 2 -> return getString(R.string.todos) + } + return "" + } + } + + + tabLayout.setupWithViewPager(viewPager) + } + + fun taskSelected(task: Task) { + val resultIntent = Intent() + resultIntent.putExtra("task_id", task.id) + setResult(Activity.RESULT_OK, resultIntent) + finish() + } +} + diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskFormActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskFormActivity.java deleted file mode 100644 index 8b27e1b5f..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskFormActivity.java +++ /dev/null @@ -1,1436 +0,0 @@ -package com.habitrpg.android.habitica.ui.activities; - -import android.app.DatePickerDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.res.Resources; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.support.annotation.Nullable; -import android.support.design.widget.TextInputLayout; -import android.support.v4.content.ContextCompat; -import android.support.v7.app.ActionBar; -import android.support.v7.app.AlertDialog; -import android.support.v7.preference.PreferenceManager; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.helper.ItemTouchHelper; -import android.text.TextUtils; -import android.util.TypedValue; -import android.view.KeyEvent; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.view.inputmethod.InputMethodManager; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.DatePicker; -import android.widget.EditText; -import android.widget.ImageButton; -import android.widget.LinearLayout; -import android.widget.NumberPicker; -import android.widget.Spinner; -import android.widget.TableRow; -import android.widget.TextView; - -import com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.components.AppComponent; -import com.habitrpg.android.habitica.data.TagRepository; -import com.habitrpg.android.habitica.data.TaskRepository; -import com.habitrpg.android.habitica.helpers.FirstDayOfTheWeekHelper; -import com.habitrpg.android.habitica.helpers.RemindersManager; -import com.habitrpg.android.habitica.helpers.RemoteConfigManager; -import com.habitrpg.android.habitica.helpers.RxErrorHandler; -import com.habitrpg.android.habitica.helpers.TaskAlarmManager; -import com.habitrpg.android.habitica.helpers.TaskFilterHelper; -import com.habitrpg.android.habitica.models.Tag; -import com.habitrpg.android.habitica.models.tasks.ChecklistItem; -import com.habitrpg.android.habitica.models.tasks.Days; -import com.habitrpg.android.habitica.models.tasks.RemindersItem; -import com.habitrpg.android.habitica.models.tasks.Task; -import com.habitrpg.android.habitica.models.user.Stats; -import com.habitrpg.android.habitica.modules.AppModule; -import com.habitrpg.android.habitica.ui.WrapContentRecyclerViewLayoutManager; -import com.habitrpg.android.habitica.ui.adapter.tasks.CheckListAdapter; -import com.habitrpg.android.habitica.ui.adapter.tasks.RemindersAdapter; -import com.habitrpg.android.habitica.ui.helpers.MarkdownParser; -import com.habitrpg.android.habitica.ui.helpers.SimpleItemTouchHelperCallback; -import com.habitrpg.android.habitica.ui.helpers.ViewHelper; - -import net.pherth.android.emoji_library.EmojiEditText; -import net.pherth.android.emoji_library.EmojiPopup; - -import java.text.DateFormat; -import java.text.DecimalFormat; -import java.text.NumberFormat; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Locale; - -import javax.inject.Inject; -import javax.inject.Named; - -import butterknife.BindView; -import butterknife.OnClick; -import io.realm.RealmList; - -public class TaskFormActivity extends BaseActivity implements AdapterView.OnItemSelectedListener { - public static final String TASK_ID_KEY = "taskId"; - public static final String USER_ID_KEY = "userId"; - public static final String TASK_TYPE_KEY = "type"; - public static final String SHOW_TAG_SELECTION = "show_tag_selection"; - public static final String ALLOCATION_MODE_KEY = "allocationModeKey"; - public static final String SHOW_CHECKLIST = "show_checklist"; - - public static final String PARCELABLE_TASK = "parcelable_task"; - public static final String SAVE_TO_DB = "saveToDb"; - - // in order to disable the event handler in MainActivity - public static final String SET_IGNORE_FLAG = "ignoreFlag"; - - @BindView(R.id.task_value_edittext) - EditText taskValue; - @BindView(R.id.task_value_layout) - TextInputLayout taskValueLayout; - - @BindView(R.id.task_checklist_wrapper) - LinearLayout checklistWrapper; - - @BindView(R.id.task_difficulty_wrapper) - LinearLayout difficultyWrapper; - - @BindView(R.id.task_attribute_wrapper) - LinearLayout attributeWrapper; - - @BindView(R.id.task_main_wrapper) - LinearLayout mainWrapper; - - @BindView(R.id.task_text_edittext) - EmojiEditText taskText; - - @BindView(R.id.task_notes_edittext) - EmojiEditText taskNotes; - - @BindView(R.id.task_difficulty_spinner) - Spinner taskDifficultySpinner; - - @BindView(R.id.task_attribute_spinner) - Spinner taskAttributeSpinner; - - @BindView(R.id.btn_delete_task) - Button btnDelete; - - @BindView(R.id.task_startdate_layout) - LinearLayout startDateLayout; - - @BindView(R.id.task_task_wrapper) - LinearLayout taskWrapper; - - @BindView(R.id.task_positive_checkbox) - CheckBox positiveCheckBox; - - @BindView(R.id.task_negative_checkbox) - CheckBox negativeCheckBox; - - @BindView(R.id.task_actions_wrapper) - LinearLayout actionsLayout; - - @BindView(R.id.task_weekdays_wrapper) - LinearLayout weekdayWrapper; - - @BindView(R.id.frequency_title) - TextView frequencyTitleTextView; - - @BindView(R.id.task_frequency_spinner) - Spinner dailyFrequencySpinner; - - @BindView(R.id.task_frequency_container) - LinearLayout frequencyContainer; - - @BindView(R.id.checklist_recycler_view) - RecyclerView recyclerView; - - @BindView(R.id.new_checklist) - EmojiEditText newCheckListEditText; - - @BindView(R.id.add_checklist_button) - Button addChecklistItemButton; - - @BindView(R.id.task_reminders_wrapper) - LinearLayout remindersWrapper; - - @BindView(R.id.new_reminder_edittext) - EditText newRemindersEditText; - - @BindView(R.id.reminders_recycler_view) - RecyclerView remindersRecyclerView; - - @BindView(R.id.emoji_toggle_btn0) - ImageButton emojiToggle0; - - @BindView(R.id.emoji_toggle_btn1) - ImageButton emojiToggle1; - ImageButton emojiToggle2; - - @BindView(R.id.task_duedate_layout) - LinearLayout dueDateLayout; - - @BindView(R.id.task_duedate_picker_layout) - LinearLayout dueDatePickerLayout; - - @BindView(R.id.duedate_checkbox) - CheckBox dueDateCheckBox; - - @BindView(R.id.startdate_text_title) - TextView startDateTitleTextView; - @BindView(R.id.startdate_text_edittext) - EditText startDatePickerText; - @BindView (R.id.repeatables_startdate_text_edittext) - EditText repeatablesStartDatePickerText; - DateEditTextListener startDateListener; - - @BindView(R.id.repeatables) - LinearLayout repeatablesLayout; - - @BindView(R.id.repeatables_on_title) - TextView reapeatablesOnTextView; - @BindView(R.id.task_repeatables_on_spinner) - Spinner repeatablesOnSpinner; - - @BindView(R.id.task_repeatables_every_x_spinner) - NumberPicker repeatablesEveryXSpinner; - - @BindView(R.id.task_repeatables_frequency_container) - LinearLayout repeatablesFrequencyContainer; - - @BindView(R.id.summary) - TextView summaryTextView; - - @BindView(R.id.duedate_text_edittext) - EditText dueDatePickerText; - DateEditTextListener dueDateListener; - - @BindView(R.id.task_tags_wrapper) - LinearLayout tagsWrapper; - - @BindView(R.id.task_tags_checklist) - LinearLayout tagsContainerLinearLayout; - @BindView(R.id.task_repeatables_frequency_spinner) - Spinner repeatablesFrequencySpinner; - - - @Inject - TaskFilterHelper taskFilterHelper; - @Inject - TaskRepository taskRepository; - @Inject - TagRepository tagRepository; - @Inject - @Named(AppModule.NAMED_USER_ID) - String userId; - @Inject - RemoteConfigManager remoteConfigManager; - @Inject - TaskAlarmManager taskAlarmManager; - - private Task task; - private boolean taskBasedAllocation; - private List weekdayCheckboxes = new ArrayList<>(); - private List repeatablesWeekDayCheckboxes = new ArrayList<>(); - private NumberPicker frequencyPicker; - private List tags; - private CheckListAdapter checklistAdapter; - private RemindersAdapter remindersAdapter; - private List tagCheckBoxList; - private List selectedTags; - - private RemindersManager remindersManager; - private FirstDayOfTheWeekHelper firstDayOfTheWeekHelper; - - private String taskType; - private String taskId; - private EmojiPopup popup; - - @Override - protected int getLayoutResId() { - return R.layout.activity_task_form; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - Intent intent = getIntent(); - Bundle bundle = intent.getExtras(); - - taskType = bundle.getString(TASK_TYPE_KEY); - taskId = bundle.getString(TASK_ID_KEY); - taskBasedAllocation = bundle.getBoolean(ALLOCATION_MODE_KEY); - boolean showTagSelection = bundle.getBoolean(SHOW_TAG_SELECTION, true); - tagCheckBoxList = new ArrayList<>(); - - tagsWrapper.setVisibility(showTagSelection ? View.VISIBLE : View.GONE); - - if(bundle.containsKey(PARCELABLE_TASK)){ - task = bundle.getParcelable(PARCELABLE_TASK); - if (task != null) { - taskType = task.getType(); - } - } - - tagCheckBoxList = new ArrayList<>(); - selectedTags = new ArrayList<>(); - if (taskType == null) { - return; - } - - remindersManager = new RemindersManager(taskType); - - dueDateListener = new DateEditTextListener(dueDatePickerText); - startDateListener = new DateEditTextListener(startDatePickerText); - - btnDelete.setEnabled(false); - ViewHelper.SetBackgroundTint(btnDelete, ContextCompat.getColor(this, R.color.red_10)); - btnDelete.setOnClickListener(view -> new AlertDialog.Builder(view.getContext()) - .setTitle(getString(R.string.taskform_delete_title)) - .setMessage(getString(R.string.taskform_delete_message)).setPositiveButton(getString(R.string.yes), (dialog, which) -> { - if (task != null && task.isValid()) { - taskRepository.deleteTask(task.getId()); - } - - finish(); - dismissKeyboard(); - - taskRepository.deleteTask(taskId).subscribe(aVoid -> {}, RxErrorHandler.handleEmptyError()); - }).setNegativeButton(getString(R.string.no), (dialog, which) -> dialog.dismiss()).show()); - - ArrayAdapter difficultyAdapter = ArrayAdapter.createFromResource(this, - R.array.task_difficulties, android.R.layout.simple_spinner_item); - difficultyAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - taskDifficultySpinner.setAdapter(difficultyAdapter); - taskDifficultySpinner.setSelection(1); - - ArrayAdapter attributeAdapter = ArrayAdapter.createFromResource(this, - R.array.task_attributes, android.R.layout.simple_spinner_item); - attributeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - taskAttributeSpinner.setAdapter(attributeAdapter); - taskAttributeSpinner.setSelection(0); - - if (!taskBasedAllocation) { - attributeWrapper.setVisibility(View.GONE); - } - - if (taskType.equals("habit")) { - taskWrapper.removeView(startDateLayout); - - mainWrapper.removeView(checklistWrapper); - mainWrapper.removeView(remindersWrapper); - - positiveCheckBox.setChecked(true); - negativeCheckBox.setChecked(true); - } else { - mainWrapper.removeView(actionsLayout); - } - - if (taskType.equals("daily")) { - ArrayAdapter frequencyAdapter = ArrayAdapter.createFromResource(this, - R.array.daily_frequencies, android.R.layout.simple_spinner_item); - frequencyAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - this.dailyFrequencySpinner.setAdapter(frequencyAdapter); - this.dailyFrequencySpinner.setOnItemSelectedListener(this); - } else { - mainWrapper.removeView(weekdayWrapper); - mainWrapper.removeView(startDateLayout); - } - - if (taskType.equals("todo")) { - dueDatePickerLayout.removeView(dueDatePickerText); - //Allows user to decide if they want to add a due date or not - dueDateCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> { - if (buttonView.isChecked()) { - dueDatePickerLayout.addView(dueDatePickerText); - } else { - dueDatePickerLayout.removeView(dueDatePickerText); - } - }); - } else { - mainWrapper.removeView(dueDateLayout); - } - - if (!taskType.equals("reward")) { - taskValueLayout.setVisibility(View.GONE); - } else { - - mainWrapper.removeView(checklistWrapper); - mainWrapper.removeView(remindersWrapper); - - difficultyWrapper.setVisibility(View.GONE); - attributeWrapper.setVisibility(View.GONE); - } - - if (taskType.equals("todo") || taskType.equals("daily")) { - createCheckListRecyclerView(); - createRemindersRecyclerView(); - } - - // Emoji keyboard stuff - boolean isTodo = false; - if (taskType.equals("todo")) { - isTodo = true; - } - - // If it's a to-do, change the emojiToggle2 to the actual emojiToggle2 (prevents NPEs when not a to-do task) - if (isTodo) { - emojiToggle2 = (ImageButton) findViewById(R.id.emoji_toggle_btn2); - } else { - emojiToggle2 = emojiToggle0; - } - - // if showChecklist is inactive the wrapper is wrapper, so the reference can't be found - if(emojiToggle2 == null) { - emojiToggle2 = emojiToggle0; - } - - popup = new EmojiPopup(emojiToggle0.getRootView(), this, ContextCompat.getColor(this, R.color.brand)); - - popup.setSizeForSoftKeyboard(); - popup.setOnDismissListener(() -> changeEmojiKeyboardIcon(false)); - popup.setOnSoftKeyboardOpenCloseListener(new EmojiPopup.OnSoftKeyboardOpenCloseListener() { - - @Override - public void onKeyboardOpen(int keyBoardHeight) { - - } - - @Override - public void onKeyboardClose() { - if (popup != null && popup.isShowing()) { - popup.dismiss(); - } - } - }); - - popup.setOnEmojiconClickedListener(emojicon -> { - EmojiEditText emojiEditText = null; - if (getCurrentFocus() == null || !isEmojiEditText(getCurrentFocus()) || emojicon == null) { - return; - } else { - emojiEditText = (EmojiEditText) getCurrentFocus(); - } - int start = emojiEditText.getSelectionStart(); - int end = emojiEditText.getSelectionEnd(); - if (start < 0) { - emojiEditText.append(emojicon.getEmoji()); - } else { - emojiEditText.getText().replace(Math.min(start, end), - Math.max(start, end), emojicon.getEmoji(), 0, - emojicon.getEmoji().length()); - } - }); - - popup.setOnEmojiconBackspaceClickedListener(v -> { - if (isEmojiEditText(getCurrentFocus())) { - KeyEvent event = new KeyEvent( - 0, 0, 0, KeyEvent.KEYCODE_DEL, 0, 0, 0, 0, KeyEvent.KEYCODE_ENDCALL); - getCurrentFocus().dispatchKeyEvent(event); - } - }); - - emojiToggle0.setOnClickListener(new emojiClickListener(taskText)); - emojiToggle1.setOnClickListener(new emojiClickListener(taskNotes)); - if (isTodo) { - emojiToggle2.setOnClickListener(new emojiClickListener(newCheckListEditText)); - } - - enableRepeatables(); - - tagRepository.getTags(userId) - .firstElement() - .subscribe(loadedTags -> { - tags = loadedTags; - createTagsCheckBoxes(); - }, RxErrorHandler.handleEmptyError() - ); - - if (taskId != null) { - taskRepository.getTask(taskId) - .firstElement() - .subscribe(task -> { - this.task = task; - if (task != null) { - populate(task); - - setTitle(task); - if (taskType.equals("todo") || taskType.equals("daily")) { - populateChecklistRecyclerView(); - populateRemindersRecyclerView(); - } - } - - setTitle(task); - }, RxErrorHandler.handleEmptyError()); - - btnDelete.setEnabled(true); - } else { - setTitle((Task) null); - taskText.requestFocus(); - } - } - - @Override - protected void onDestroy() { - tagRepository.close(); - super.onDestroy(); - } - - @Override - protected void injectActivity(AppComponent component) { - component.inject(this); - } - - public void hideMonthOptions () { - ViewGroup.LayoutParams repeatablesOnSpinnerParams = repeatablesOnSpinner.getLayoutParams(); - repeatablesOnSpinnerParams.height = 0; - repeatablesOnSpinner.setLayoutParams(repeatablesOnSpinnerParams); - - ViewGroup.LayoutParams repeatablesOnTitleParams = reapeatablesOnTextView.getLayoutParams(); - repeatablesOnTitleParams.height = 0; - reapeatablesOnTextView.setLayoutParams(repeatablesOnTitleParams); - } - - public void hideWeekOptions () { - ViewGroup.LayoutParams repeatablesFrequencyContainerParams = repeatablesFrequencyContainer.getLayoutParams(); - repeatablesFrequencyContainerParams.height = 0; - repeatablesFrequencyContainer.setLayoutParams(repeatablesFrequencyContainerParams); - } - - // @TODO: abstract business logic to Presenter and only modify view? - private void enableRepeatables() - { - if (!remoteConfigManager.repeatablesAreEnabled() || !taskType.equals("daily")){ - repeatablesLayout.setVisibility(View.INVISIBLE); - ViewGroup.LayoutParams repeatablesLayoutParams = repeatablesLayout.getLayoutParams(); - repeatablesLayoutParams.height = 0; - repeatablesLayout.setLayoutParams(repeatablesLayoutParams); - return; - }; - - startDateLayout.setVisibility(View.INVISIBLE); - - // Hide old stuff - ViewGroup.LayoutParams startDateLayoutParams = startDateLayout.getLayoutParams(); - startDateLayoutParams.height = 0; - startDateLayout.setLayoutParams(startDateLayoutParams); - - ViewGroup.LayoutParams startDatePickerTextParams = startDatePickerText.getLayoutParams(); - startDatePickerTextParams.height = 0; - startDatePickerText.setLayoutParams(startDatePickerTextParams); - - ViewGroup.LayoutParams startDateTitleTextViewParams = startDateTitleTextView.getLayoutParams(); - startDateTitleTextViewParams.height = 0; - startDateTitleTextView.setLayoutParams(startDateTitleTextViewParams); - - weekdayWrapper.setVisibility(View.INVISIBLE); - ViewGroup.LayoutParams weekdayWrapperParams = weekdayWrapper.getLayoutParams(); - weekdayWrapperParams.height = 0; - weekdayWrapper.setLayoutParams(weekdayWrapperParams); - - ViewGroup.LayoutParams frequencyTitleTextViewParams = frequencyTitleTextView.getLayoutParams(); - frequencyTitleTextViewParams.height = 0; - frequencyTitleTextView.setLayoutParams(frequencyTitleTextViewParams); - - ViewGroup.LayoutParams dailyFrequencySpinnerParams = dailyFrequencySpinner.getLayoutParams(); - dailyFrequencySpinnerParams.height = 0; - dailyFrequencySpinner.setLayoutParams(dailyFrequencySpinnerParams); - - // Start Date - startDateListener = new DateEditTextListener(repeatablesStartDatePickerText); - - // Frequency - ArrayAdapter frequencyAdapter = ArrayAdapter.createFromResource(this, - R.array.repeatables_frequencies, android.R.layout.simple_spinner_item); - frequencyAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - this.repeatablesFrequencySpinner.setAdapter(frequencyAdapter); - this.repeatablesFrequencySpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - generateSummary(); - Resources r = getResources(); - - // @TODO: remove magic numbers - - if (position == 2) { - hideWeekOptions(); - - ViewGroup.LayoutParams repeatablesOnSpinnerParams = repeatablesOnSpinner.getLayoutParams(); - repeatablesOnSpinnerParams.height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 72, r.getDisplayMetrics()); - repeatablesOnSpinner.setLayoutParams(repeatablesOnSpinnerParams); - - ViewGroup.LayoutParams repeatablesOnTitleParams = reapeatablesOnTextView.getLayoutParams(); - repeatablesOnTitleParams.height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, r.getDisplayMetrics()); - reapeatablesOnTextView.setLayoutParams(repeatablesOnTitleParams); - }else if (position == 1) { - hideMonthOptions(); - - ViewGroup.LayoutParams repeatablesFrequencyContainerParams = repeatablesFrequencyContainer.getLayoutParams(); - repeatablesFrequencyContainerParams.height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 220, r.getDisplayMetrics()); - repeatablesFrequencyContainer.setLayoutParams(repeatablesFrequencyContainerParams); - } else { - hideWeekOptions(); - hideMonthOptions(); - } - } - - @Override - public void onNothingSelected(AdapterView parent) { - - } - }); - - // Repeat On - ArrayAdapter repeatablesOnAdapter = ArrayAdapter.createFromResource(this, - R.array.repeatables_on, android.R.layout.simple_spinner_item); - repeatablesOnAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - this.repeatablesOnSpinner.setAdapter(repeatablesOnAdapter); - this.repeatablesOnSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - generateSummary(); - } - - @Override - public void onNothingSelected(AdapterView parent) { - - } - }); - - // Every X - setupEveryXSpinner(repeatablesEveryXSpinner); - repeatablesEveryXSpinner.setOnValueChangedListener((picker, oldVal, newVal) -> generateSummary()); - - // WeekDays - this.repeatablesFrequencyContainer.removeAllViews(); - String[] weekdays = getResources().getStringArray(R.array.weekdays); - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - String dayOfTheWeek = sharedPreferences.getString("FirstDayOfTheWeek", - Integer.toString(Calendar.getInstance().getFirstDayOfWeek())); - firstDayOfTheWeekHelper = - FirstDayOfTheWeekHelper.newInstance(Integer.parseInt(dayOfTheWeek)); - ArrayList weekdaysTemp = new ArrayList<>(Arrays.asList(weekdays)); - Collections.rotate(weekdaysTemp, firstDayOfTheWeekHelper.getDailyTaskFormOffset()); - weekdays = weekdaysTemp.toArray(new String[1]); - - for (int i = 0; i < 7; i++) { - View weekdayRow = getLayoutInflater().inflate(R.layout.row_checklist, this.repeatablesFrequencyContainer, false); - CheckBox checkbox = (CheckBox) weekdayRow.findViewById(R.id.checkbox); - checkbox.setText(weekdays[i]); - checkbox.setChecked(true); - checkbox.setOnClickListener(v -> generateSummary()); - repeatablesWeekDayCheckboxes.add(checkbox); - repeatablesFrequencyContainer.addView(weekdayRow); - } - - generateSummary(); - } - - @Override - protected void onRestoreInstanceState(Bundle savedInstanceState) { - } - - private void generateSummary () { - String frequency = repeatablesFrequencySpinner.getSelectedItem().toString(); - String everyX = String.valueOf(repeatablesEveryXSpinner.getValue()); - String frequencyQualifier = ""; - - switch (frequency) { - case "Daily": - frequencyQualifier = "day(s)"; - break; - case "Weekly": - frequencyQualifier = "week(s)"; - break; - case "Monthly": - frequencyQualifier = "month(s)"; - break; - case "Yearly": - frequencyQualifier = "year(s)"; - break; - } - - String weekdays; - List weekdayStrings = new ArrayList<>(); - int offset = firstDayOfTheWeekHelper.getDailyTaskFormOffset(); - if (this.repeatablesWeekDayCheckboxes.get(offset).isChecked()) { - weekdayStrings.add("Monday"); - } - if (this.repeatablesWeekDayCheckboxes.get((offset + 1) % 7).isChecked()) { - weekdayStrings.add("Tuesday"); - } - if (this.repeatablesWeekDayCheckboxes.get((offset + 2) % 7).isChecked()) { - weekdayStrings.add("Wednesday"); - } - if (this.repeatablesWeekDayCheckboxes.get((offset + 3) % 7).isChecked()) { - weekdayStrings.add("Thursday"); - } - if (this.repeatablesWeekDayCheckboxes.get((offset + 4) % 7).isChecked()) { - weekdayStrings.add("Friday"); - } - if (this.repeatablesWeekDayCheckboxes.get((offset + 5) % 7).isChecked()) { - weekdayStrings.add("Saturday"); - } - if (this.repeatablesWeekDayCheckboxes.get((offset + 6) % 7).isChecked()) { - weekdayStrings.add("Sunday"); - } - weekdays = " on " + TextUtils.join(", ", weekdayStrings); - if (!frequency.equals("Weekly")) { - weekdays = ""; - } - - if (frequency.equals("Monthly")) { - weekdays = ""; - Calendar calendar = startDateListener.getCalendar(); - String monthlyFreq = repeatablesOnSpinner.getSelectedItem().toString(); - if (monthlyFreq.equals("Day of Month")) { - Integer date = calendar.get(Calendar.DATE); - weekdays = " on the " + date.toString(); - } else { - Integer week = calendar.get(Calendar.WEEK_OF_MONTH); - String dayLongName = calendar.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.getDefault()); - weekdays = " on the " + week.toString() + " week on " + dayLongName; - } - } - - String summary = getResources().getString(R.string.repeat_summary, frequency, everyX, frequencyQualifier, weekdays); - summaryTextView.setText(summary); - } - - private void populateRepeatables(Task task) { - // Frequency - int frequencySelection = 0; - if (task.getFrequency().equals("weekly")) { - frequencySelection = 1; - } else if (task.getFrequency().equals("monthly")) { - frequencySelection = 2; - } else if (task.getFrequency().equals("yearly")) { - frequencySelection = 3; - } - this.repeatablesFrequencySpinner.setSelection(frequencySelection); - - // Every X - this.repeatablesEveryXSpinner.setValue(task.getEveryX()); - - // Weekdays - if (task.getFrequency().equals("weekly")) { - if (repeatablesWeekDayCheckboxes.size() == 7) { - int offset = firstDayOfTheWeekHelper.getDailyTaskFormOffset(); - this.repeatablesWeekDayCheckboxes.get(offset).setChecked(this.task.getRepeat().getM()); - this.repeatablesWeekDayCheckboxes.get((offset + 1) % 7).setChecked(this.task.getRepeat().getT()); - this.repeatablesWeekDayCheckboxes.get((offset + 2) % 7).setChecked(this.task.getRepeat().getW()); - this.repeatablesWeekDayCheckboxes.get((offset + 3) % 7).setChecked(this.task.getRepeat().getTh()); - this.repeatablesWeekDayCheckboxes.get((offset + 4) % 7).setChecked(this.task.getRepeat().getF()); - this.repeatablesWeekDayCheckboxes.get((offset + 5) % 7).setChecked(this.task.getRepeat().getS()); - this.repeatablesWeekDayCheckboxes.get((offset + 6) % 7).setChecked(this.task.getRepeat().getSu()); - } - } - - // Repeats On - - if (task.getDaysOfMonth() != null && task.getDaysOfMonth().size() > 0) { - this.repeatablesOnSpinner.setSelection(0); - } else if (task.getWeeksOfMonth() != null && task.getWeeksOfMonth().size() > 0) { - this.repeatablesOnSpinner.setSelection(1); - } - - } - - private void setupEveryXSpinner(NumberPicker frequencyPicker) { -// View dayRow = getLayoutInflater().inflate(R.layout.row_number_picker, this.frequencyContainer, false); -// frequencyPicker = (NumberPicker) dayRow.findViewById(R.id.numberPicker); - frequencyPicker.setMinValue(0); - frequencyPicker.setMaxValue(366); - frequencyPicker.setValue(1); -// TextView tv = (TextView) dayRow.findViewById(R.id.label); -// tv.setText(getResources().getString(R.string.frequency_daily)); -// this.frequencyContainer.addView(dayRow); - } - - private boolean isEmojiEditText(@Nullable View view) { - return view instanceof EmojiEditText; - } - - private void changeEmojiKeyboardIcon(Boolean keyboardOpened) { - if (keyboardOpened) { - emojiToggle0.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_keyboard_grey600_24dp)); - emojiToggle1.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_keyboard_grey600_24dp)); - emojiToggle2.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_keyboard_grey600_24dp)); - } else { - emojiToggle0.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_emoticon_grey600_24dp)); - emojiToggle1.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_emoticon_grey600_24dp)); - emojiToggle2.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_emoticon_grey600_24dp)); - } - } - - private void createCheckListRecyclerView() { - checklistAdapter = new CheckListAdapter(); - - LinearLayoutManager llm = new LinearLayoutManager(this); - llm.setOrientation(LinearLayoutManager.VERTICAL); - - recyclerView.setLayoutManager(llm); - recyclerView.setAdapter(checklistAdapter); - - recyclerView.setLayoutManager(new WrapContentRecyclerViewLayoutManager(this)); - - ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(checklistAdapter); - ItemTouchHelper mItemTouchHelper = new ItemTouchHelper(callback); - mItemTouchHelper.attachToRecyclerView(recyclerView); - } - - private void populateChecklistRecyclerView() { - List checklistItems = new ArrayList<>(); - if (task != null && task.isManaged()) { - checklistItems = taskRepository.getUnmanagedCopy(task.getChecklist()); - } - checklistAdapter.setItems(checklistItems); - } - - @OnClick(R.id.add_checklist_button) - public void addChecklistItem() { - String text = newCheckListEditText.getText().toString(); - ChecklistItem item = new ChecklistItem(text); - checklistAdapter.addItem(item); - newCheckListEditText.setText(""); - } - - private void createRemindersRecyclerView() { - remindersAdapter = new RemindersAdapter(taskType); - - LinearLayoutManager llm = new LinearLayoutManager(this); - llm.setOrientation(LinearLayoutManager.VERTICAL); - - remindersRecyclerView.setLayoutManager(llm); - remindersRecyclerView.setAdapter(remindersAdapter); - - remindersRecyclerView.setLayoutManager(new WrapContentRecyclerViewLayoutManager(this)); - - - ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(remindersAdapter); - ItemTouchHelper mItemTouchHelper = new ItemTouchHelper(callback); - mItemTouchHelper.attachToRecyclerView(remindersRecyclerView); - } - - private void populateRemindersRecyclerView() { - List reminders = new ArrayList<>(); - if (task != null && task.getReminders() != null) { - reminders = taskRepository.getUnmanagedCopy(task.getReminders()); - } - - remindersAdapter.setReminders(reminders); - } - - private void addNewReminder(RemindersItem remindersItem) { - remindersAdapter.addItem(remindersItem); - } - - @OnClick(R.id.new_reminder_edittext) - public void selectNewReminderTime() { - remindersManager.createReminderTimeDialog(this::addNewReminder, taskType, this, null); - } - - private void createTagsCheckBoxes() { - int position = 0; - this.tagsContainerLinearLayout.removeAllViews(); - for (Tag tag : tags) { - TableRow row = (TableRow) getLayoutInflater().inflate(R.layout.row_checklist, this.tagsContainerLinearLayout, false); - CheckBox checkbox = (CheckBox) row.findViewById(R.id.checkbox); - row.setId(position); - checkbox.setText(tag.getName()); // set text Name - checkbox.setId(position); - //This is to check if the tag was selected by the user. Similar to onClickListener - checkbox.setOnCheckedChangeListener((buttonView, isChecked) -> { - if (buttonView.isChecked()) { - if (!selectedTags.contains(tag)) { - selectedTags.add(tag); - } - } else { - if (selectedTags.contains(tag)) { - selectedTags.remove(tag); - } - } - }); - checkbox.setChecked(taskFilterHelper.isTagChecked(tag.getId())); - tagsContainerLinearLayout.addView(row); - tagCheckBoxList.add(checkbox); - position++; - } - - if (task != null) { - fillTagCheckboxes(); - } - } - - private void setTitle(@Nullable Task task) { - ActionBar actionBar = getSupportActionBar(); - - if (actionBar != null) { - - String title = ""; - - if (task != null && task.isValid()) { - title = getResources().getString(R.string.action_edit) + " " + task.getText(); - } else { - switch (taskType) { - case "todo": - title = getResources().getString(R.string.new_todo); - break; - case "daily": - title = getResources().getString(R.string.new_daily); - break; - case "habit": - title = getResources().getString(R.string.new_habit); - break; - case "reward": - title = getResources().getString(R.string.new_reward); - break; - } - } - - actionBar.setTitle(title); - } - } - - private void setDailyFrequencyViews() { - this.frequencyContainer.removeAllViews(); - if (this.dailyFrequencySpinner.getSelectedItemPosition() == 0) { - String[] weekdays = getResources().getStringArray(R.array.weekdays); - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); - String dayOfTheWeek = sharedPreferences.getString("FirstDayOfTheWeek", - Integer.toString(Calendar.getInstance().getFirstDayOfWeek())); - firstDayOfTheWeekHelper = - FirstDayOfTheWeekHelper.newInstance(Integer.parseInt(dayOfTheWeek)); - ArrayList weekdaysTemp = new ArrayList<>(Arrays.asList(weekdays)); - Collections.rotate(weekdaysTemp, firstDayOfTheWeekHelper.getDailyTaskFormOffset()); - weekdays = weekdaysTemp.toArray(new String[1]); - - for (int i = 0; i < 7; i++) { - View weekdayRow = getLayoutInflater().inflate(R.layout.row_checklist, this.frequencyContainer, false); - CheckBox checkbox = (CheckBox) weekdayRow.findViewById(R.id.checkbox); - checkbox.setText(weekdays[i]); - checkbox.setChecked(true); - this.weekdayCheckboxes.add(checkbox); - this.frequencyContainer.addView(weekdayRow); - } - } else { - View dayRow = getLayoutInflater().inflate(R.layout.row_number_picker, this.frequencyContainer, false); - this.frequencyPicker = (NumberPicker) dayRow.findViewById(R.id.numberPicker); - this.frequencyPicker.setMinValue(1); - this.frequencyPicker.setMaxValue(366); - TextView tv = (TextView) dayRow.findViewById(R.id.label); - tv.setText(getResources().getString(R.string.frequency_daily)); - this.frequencyContainer.addView(dayRow); - } - - if (this.task != null && this.task.isValid()) { - if (this.dailyFrequencySpinner.getSelectedItemPosition() == 0) { - int offset = firstDayOfTheWeekHelper.getDailyTaskFormOffset(); - this.weekdayCheckboxes.get(offset).setChecked(this.task.getRepeat().getM()); - this.weekdayCheckboxes.get((offset + 1) % 7).setChecked(this.task.getRepeat().getT()); - this.weekdayCheckboxes.get((offset + 2) % 7).setChecked(this.task.getRepeat().getW()); - this.weekdayCheckboxes.get((offset + 3) % 7).setChecked(this.task.getRepeat().getTh()); - this.weekdayCheckboxes.get((offset + 4) % 7).setChecked(this.task.getRepeat().getF()); - this.weekdayCheckboxes.get((offset + 5) % 7).setChecked(this.task.getRepeat().getS()); - this.weekdayCheckboxes.get((offset + 6) % 7).setChecked(this.task.getRepeat().getSu()); - } else { - this.frequencyPicker.setValue(this.task.getEveryX()); - } - } - - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - // Inflate the menu; this adds items to the action bar if it is present. - getMenuInflater().inflate(R.menu.menu_save, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - // Handle action bar item clicks here. The action bar will - // automatically handle clicks on the Home/Up button, so long - // as you specify a parent activity in AndroidManifest.xml. - int id = item.getItemId(); - - //noinspection SimplifiableIfStatement - if (id == R.id.action_save_changes) { - finishActivitySuccessfully(); - return true; - } - - return super.onOptionsItemSelected(item); - } - - private void populate(Task task) { - if (!task.isValid()) { - return; - } - taskText.setText(task.getText()); - taskNotes.setText(task.getNotes()); - taskValue.setText(String.format(Locale.getDefault(), "%.2f", task.getValue())); - - for (Tag tag : task.getTags()) { - selectedTags.add(tag); - } - - if (tags != null) { - fillTagCheckboxes(); - } - - - float priority = task.getPriority(); - if (Math.abs(priority - 0.1) < 0.000001) { - this.taskDifficultySpinner.setSelection(0); - } else if (Math.abs(priority - 1.0) < 0.000001) { - this.taskDifficultySpinner.setSelection(1); - } else if (Math.abs(priority - 1.5) < 0.000001) { - this.taskDifficultySpinner.setSelection(2); - } else if (Math.abs(priority - 2.0) < 0.000001) { - this.taskDifficultySpinner.setSelection(3); - } - - String attribute = task.getAttribute(); - if (attribute != null) { - switch (attribute) { - case Stats.STRENGTH: - taskAttributeSpinner.setSelection(0); - break; - case Stats.INTELLIGENCE: - taskAttributeSpinner.setSelection(1); - break; - case Stats.CONSTITUTION: - taskAttributeSpinner.setSelection(2); - break; - case Stats.PERCEPTION: - taskAttributeSpinner.setSelection(3); - break; - } - } - - if (task.getType().equals("habit")) { - positiveCheckBox.setChecked(task.getUp()); - negativeCheckBox.setChecked(task.getDown()); - } - - if (task.getType().equals("daily")) { - - if (task.getStartDate() != null) { - startDateListener.setCalendar(task.getStartDate()); - } - - if (task.getFrequency().equals("weekly")) { - this.dailyFrequencySpinner.setSelection(0); - if (weekdayCheckboxes.size() == 7) { - int offset = firstDayOfTheWeekHelper.getDailyTaskFormOffset(); - this.weekdayCheckboxes.get(offset).setChecked(this.task.getRepeat().getM()); - this.weekdayCheckboxes.get((offset + 1) % 7).setChecked(this.task.getRepeat().getT()); - this.weekdayCheckboxes.get((offset + 2) % 7).setChecked(this.task.getRepeat().getW()); - this.weekdayCheckboxes.get((offset + 3) % 7).setChecked(this.task.getRepeat().getTh()); - this.weekdayCheckboxes.get((offset + 4) % 7).setChecked(this.task.getRepeat().getF()); - this.weekdayCheckboxes.get((offset + 5) % 7).setChecked(this.task.getRepeat().getS()); - this.weekdayCheckboxes.get((offset + 6) % 7).setChecked(this.task.getRepeat().getSu()); - } - } else { - this.dailyFrequencySpinner.setSelection(1); - if (this.frequencyPicker != null) { - this.frequencyPicker.setValue(task.getEveryX()); - } - } - - populateRepeatables(task); - } - - if (task.getType().equals("todo")) { - if (task.getDueDate() != null) { - dueDateCheckBox.setChecked(true); - dueDateListener.setCalendar(task.getDueDate()); - } - } - - if (task.isGroupTask()) { - new AlertDialog.Builder(this) - .setTitle(R.string.group_tasks_edit_title) - .setMessage(R.string.group_tasks_edit_description) - .setPositiveButton(android.R.string.ok, (dialog, which) -> finish()) - .show(); - } - } - - private void fillTagCheckboxes() { - for (Tag tag : task.getTags()) { - int position = tags.indexOf(tag); - if (tagCheckBoxList.size() > position && position >= 0) { - tagCheckBoxList.get(position).setChecked(true); - } - } - } - - private boolean saveTask(Task task) { - - String text = MarkdownParser.INSTANCE.parseCompiled(taskText.getText()); - if (text == null || text.isEmpty()) { - return false; - } - - if (!task.isValid()) { - return true; - } - - taskRepository.executeTransaction(realm -> { - try { - task.setText(text); - task.setNotes(MarkdownParser.INSTANCE.parseCompiled(taskNotes.getText())); - } catch (IllegalArgumentException ignored) { - - } - if (checklistAdapter != null) { - if (checklistAdapter.getCheckListItems() != null) { - RealmList newChecklist = new RealmList<>(); - newChecklist.addAll(realm.copyToRealmOrUpdate(checklistAdapter.getCheckListItems())); - task.setChecklist(newChecklist); - } - } - - if (remindersAdapter != null) { - if (remindersAdapter.getRemindersItems() != null) { - RealmList newReminders = new RealmList<>(); - newReminders.addAll(realm.copyToRealmOrUpdate(remindersAdapter.getRemindersItems())); - task.setReminders(newReminders); - } - } - - - RealmList taskTags = new RealmList<>(); - taskTags.addAll(selectedTags); - task.setTags(taskTags); - - - if (taskDifficultySpinner.getSelectedItemPosition() == 0) { - task.setPriority((float) 0.1); - } else if (taskDifficultySpinner.getSelectedItemPosition() == 1) { - task.setPriority((float) 1.0); - } else if (taskDifficultySpinner.getSelectedItemPosition() == 2) { - task.setPriority((float) 1.5); - } else if (taskDifficultySpinner.getSelectedItemPosition() == 3) { - task.setPriority((float) 2.0); - } - - if (!taskBasedAllocation) { - task.setAttribute(Stats.STRENGTH); - } else { - switch (taskAttributeSpinner.getSelectedItemPosition()) { - case 0: - task.setAttribute(Stats.STRENGTH); - break; - case 1: - task.setAttribute(Stats.INTELLIGENCE); - break; - case 2: - task.setAttribute(Stats.CONSTITUTION); - break; - case 3: - task.setAttribute(Stats.PERCEPTION); - break; - } - } - - switch (task.getType() != null ? task.getType() : "") { - case "habit": { - task.setUp(positiveCheckBox.isChecked()); - task.setDown(negativeCheckBox.isChecked()); - } - break; - - case "daily": { - task.setStartDate(new Date(startDateListener.getCalendar().getTimeInMillis())); - - if (this.dailyFrequencySpinner.getSelectedItemPosition() == 0) { - task.setFrequency("weekly"); - - Days repeat = task.getRepeat(); - if (repeat == null) { - repeat = new Days(); - task.setRepeat(repeat); - } - - int offset = firstDayOfTheWeekHelper.getDailyTaskFormOffset(); - repeat.setM(this.weekdayCheckboxes.get(offset).isChecked()); - repeat.setT(this.weekdayCheckboxes.get((offset + 1) % 7).isChecked()); - repeat.setW(this.weekdayCheckboxes.get((offset + 2) % 7).isChecked()); - repeat.setTh(this.weekdayCheckboxes.get((offset + 3) % 7).isChecked()); - repeat.setF(this.weekdayCheckboxes.get((offset + 4) % 7).isChecked()); - repeat.setS(this.weekdayCheckboxes.get((offset + 5) % 7).isChecked()); - repeat.setSu(this.weekdayCheckboxes.get((offset + 6) % 7).isChecked()); - } else { - task.setFrequency("daily"); - task.setEveryX(this.frequencyPicker.getValue()); - } - - if (remoteConfigManager.repeatablesAreEnabled()) { - int frequency = this.repeatablesFrequencySpinner.getSelectedItemPosition(); - String frequencyString = ""; - switch (frequency) { - case 0: - frequencyString = "daily"; - break; - case 1: - frequencyString = "weekly"; - break; - case 2: - frequencyString = "monthly"; - break; - case 3: - frequencyString = "yearly"; - break; - } - task.setFrequency(frequencyString); - - task.setEveryX(this.repeatablesEveryXSpinner.getValue()); - - Days repeat = task.getRepeat(); - if (repeat == null) { - repeat = new Days(); - task.setRepeat(repeat); - } - - if ("weekly".equals(frequencyString)) { - int offset = firstDayOfTheWeekHelper.getDailyTaskFormOffset(); - repeat.setM(this.repeatablesWeekDayCheckboxes.get(offset).isChecked()); - repeat.setT(this.repeatablesWeekDayCheckboxes.get((offset + 1) % 7).isChecked()); - repeat.setW(this.repeatablesWeekDayCheckboxes.get((offset + 2) % 7).isChecked()); - repeat.setTh(this.repeatablesWeekDayCheckboxes.get((offset + 3) % 7).isChecked()); - repeat.setF(this.repeatablesWeekDayCheckboxes.get((offset + 4) % 7).isChecked()); - repeat.setS(this.repeatablesWeekDayCheckboxes.get((offset + 5) % 7).isChecked()); - repeat.setSu(this.repeatablesWeekDayCheckboxes.get((offset + 6) % 7).isChecked()); - } - - if ("monthly".equals(frequencyString)) { - Calendar calendar = startDateListener.getCalendar(); - String monthlyFreq = repeatablesOnSpinner.getSelectedItem().toString(); - - if (monthlyFreq.equals("Day of Month")) { - Integer date = calendar.get(Calendar.DATE); - List daysOfMonth = new ArrayList<>(); - daysOfMonth.add(date); - task.setDaysOfMonth(daysOfMonth); - task.setWeeksOfMonth(new ArrayList<>()); - } else { - Integer week = calendar.get(Calendar.WEEK_OF_MONTH); - List weeksOfMonth = new ArrayList<>(); - weeksOfMonth.add(week); - task.setWeeksOfMonth(weeksOfMonth); - task.setDaysOfMonth(new ArrayList<>()); - } - } - } - } - break; - - case "todo": { - if (dueDateCheckBox.isChecked()) { - task.setDueDate(new Date(dueDateListener.getCalendar().getTimeInMillis())); - } else { - task.setDueDate(null); - } - } - break; - - case "reward": { - String value = taskValue.getText().toString(); - if (!value.isEmpty()) { - NumberFormat localFormat = DecimalFormat.getInstance(Locale.getDefault()); - try { - task.setValue(localFormat.parse(value).doubleValue()); - } catch (ParseException ignored) { - } - } else { - task.setValue(0.0d); - } - - } - break; - } - }); - return true; - } - - public void onItemSelected(AdapterView parent, View view, - int pos, long id) { - this.setDailyFrequencyViews(); - } - - public void onNothingSelected(AdapterView parent) { - this.setDailyFrequencyViews(); - } - - private void prepareSave() { - if (this.task == null) { - this.task = new Task(); - this.task.setType(taskType); - } - - if (this.saveTask(this.task) && task.isValid()) { - //send back to other elements. - if (TaskFormActivity.this.task.getId() == null || TaskFormActivity.this.task.getId().length() == 0) { - taskRepository.createTaskInBackground(task); - } else { - taskRepository.updateTaskInBackground(task); - } - Task unmanagedTask = taskRepository.getUnmanagedCopy(task); - taskAlarmManager.scheduleAlarmsForTask(unmanagedTask); - } - } - - @Override - public boolean onSupportNavigateUp() { - finish(); - dismissKeyboard(); - return true; - } - - @Override - public void onBackPressed() { - finish(); - dismissKeyboard(); - } - - private void finishActivitySuccessfully() { - this.prepareSave(); - finishWithSuccess(); - dismissKeyboard(); - } - - private void finishWithSuccess() { - Handler mainHandler = new Handler(this.getMainLooper()); - mainHandler.postDelayed(() -> { - Intent resultIntent = new Intent(); - resultIntent.putExtra(TaskFormActivity.TASK_TYPE_KEY, taskType); - setResult(RESULT_OK, resultIntent); - finish(); - }, 500); - } - - private void dismissKeyboard() { - InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - View currentFocus = getCurrentFocus(); - if (currentFocus != null) { - imm.hideSoftInputFromWindow(currentFocus.getWindowToken(), 0); - } - if (popup != null) { - popup.dismiss(); - popup = null; - } - } - - private class DateEditTextListener implements View.OnClickListener, DatePickerDialog.OnDateSetListener { - Calendar calendar; - DatePickerDialog datePickerDialog; - EditText datePickerText; - DateFormat dateFormatter; - - DateEditTextListener(EditText dateText) { - calendar = Calendar.getInstance(); - - this.datePickerText = dateText; - this.datePickerText.setOnClickListener(this); - this.dateFormatter = DateFormat.getDateInstance(); - this.datePickerDialog = new DatePickerDialog(datePickerText.getContext(), this, - calendar.get(Calendar.YEAR), - calendar.get(Calendar.MONTH), - calendar.get(Calendar.DAY_OF_MONTH)); - - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); - String dayOfTheWeek = sharedPreferences.getString("FirstDayOfTheWeek", - Integer.toString(Calendar.getInstance().getFirstDayOfWeek())); - FirstDayOfTheWeekHelper firstDayOfTheWeekHelper = - FirstDayOfTheWeekHelper.newInstance(Integer.parseInt(dayOfTheWeek)); - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT_WATCH) { - datePickerDialog.getDatePicker().getCalendarView().setFirstDayOfWeek( - firstDayOfTheWeekHelper.getFirstDayOfTheWeek()); - } else { - datePickerDialog.getDatePicker().setFirstDayOfWeek(firstDayOfTheWeekHelper - .getFirstDayOfTheWeek()); - } - - this.datePickerDialog.setButton(DialogInterface.BUTTON_NEUTRAL, getResources().getString(R.string.today), (dialog, which) -> { - setCalendar(Calendar.getInstance().getTime()); - }); - updateDateText(); - } - - public void onClick(View view) { - datePickerDialog.show(); - } - - public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) { - calendar.set(year, monthOfYear, dayOfMonth); - updateDateText(); - } - - public Calendar getCalendar() { - return (Calendar) calendar.clone(); - } - - public void setCalendar(Date date) { - calendar.setTime(date); - datePickerDialog.updateDate(calendar.get(Calendar.YEAR), - calendar.get(Calendar.MONTH), - calendar.get(Calendar.DAY_OF_MONTH)); - updateDateText(); - } - - private void updateDateText() { - datePickerText.setText(dateFormatter.format(calendar.getTime())); - } - } - - private class emojiClickListener implements View.OnClickListener { - - EmojiEditText view; - - emojiClickListener(EmojiEditText view) { - this.view = view; - } - - @Override - public void onClick(View v) { - if (!popup.isShowing()) { - - if (popup.isKeyBoardOpen()) { - popup.showAtBottom(); - changeEmojiKeyboardIcon(true); - } else { - view.setFocusableInTouchMode(true); - view.requestFocus(); - popup.showAtBottomPending(); - final InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - inputMethodManager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT); - changeEmojiKeyboardIcon(true); - } - } else { - popup.dismiss(); - changeEmojiKeyboardIcon(false); - } - } - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskFormActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskFormActivity.kt new file mode 100644 index 000000000..4f13457b3 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskFormActivity.kt @@ -0,0 +1,1219 @@ +package com.habitrpg.android.habitica.ui.activities + +import android.app.Activity +import android.app.DatePickerDialog +import android.content.Context +import android.content.DialogInterface +import android.content.Intent +import android.os.Build +import android.os.Bundle +import android.os.Handler +import android.support.design.widget.TextInputLayout +import android.support.v4.content.ContextCompat +import android.support.v7.app.AlertDialog +import android.support.v7.preference.PreferenceManager +import android.support.v7.widget.LinearLayoutManager +import android.support.v7.widget.RecyclerView +import android.support.v7.widget.helper.ItemTouchHelper +import android.text.TextUtils +import android.util.TypedValue +import android.view.KeyEvent +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.view.inputmethod.InputMethodManager +import android.widget.* +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.components.AppComponent +import com.habitrpg.android.habitica.data.TagRepository +import com.habitrpg.android.habitica.data.TaskRepository +import com.habitrpg.android.habitica.extensions.notNull +import com.habitrpg.android.habitica.helpers.* +import com.habitrpg.android.habitica.models.Tag +import com.habitrpg.android.habitica.models.tasks.ChecklistItem +import com.habitrpg.android.habitica.models.tasks.Days +import com.habitrpg.android.habitica.models.tasks.RemindersItem +import com.habitrpg.android.habitica.models.tasks.Task +import com.habitrpg.android.habitica.models.user.Stats +import com.habitrpg.android.habitica.modules.AppModule +import com.habitrpg.android.habitica.ui.WrapContentRecyclerViewLayoutManager +import com.habitrpg.android.habitica.ui.adapter.tasks.CheckListAdapter +import com.habitrpg.android.habitica.ui.adapter.tasks.RemindersAdapter +import com.habitrpg.android.habitica.ui.helpers.MarkdownParser +import com.habitrpg.android.habitica.ui.helpers.SimpleItemTouchHelperCallback +import com.habitrpg.android.habitica.ui.helpers.ViewHelper +import com.habitrpg.android.habitica.ui.helpers.bindView +import io.reactivex.functions.Consumer +import io.realm.Realm +import io.realm.RealmList +import net.pherth.android.emoji_library.EmojiEditText +import net.pherth.android.emoji_library.EmojiPopup +import java.text.DateFormat +import java.text.DecimalFormat +import java.text.ParseException +import java.util.* +import javax.inject.Inject +import javax.inject.Named + +class TaskFormActivity : BaseActivity(), AdapterView.OnItemSelectedListener { + private val taskValue: EditText by bindView(R.id.task_value_edittext) + private val taskValueLayout: TextInputLayout by bindView(R.id.task_value_layout) + private val checklistWrapper: LinearLayout by bindView(R.id.task_checklist_wrapper) + private val difficultyWrapper: LinearLayout by bindView(R.id.task_difficulty_wrapper) + private val attributeWrapper: LinearLayout by bindView(R.id.task_attribute_wrapper) + private val mainWrapper: LinearLayout by bindView(R.id.task_main_wrapper) + private val taskText: EmojiEditText by bindView(R.id.task_text_edittext) + private val taskNotes: EmojiEditText by bindView(R.id.task_notes_edittext) + private val taskDifficultySpinner: Spinner by bindView(R.id.task_difficulty_spinner) + private val taskAttributeSpinner: Spinner by bindView(R.id.task_attribute_spinner) + private val btnDelete: Button by bindView(R.id.btn_delete_task) + private val startDateLayout: LinearLayout by bindView(R.id.task_startdate_layout) + private val taskWrapper: LinearLayout by bindView(R.id.task_task_wrapper) + private val positiveCheckBox: CheckBox by bindView(R.id.task_positive_checkbox) + private val negativeCheckBox: CheckBox by bindView(R.id.task_negative_checkbox) + private val actionsLayout: LinearLayout by bindView(R.id.task_actions_wrapper) + private val weekdayWrapper: LinearLayout by bindView(R.id.task_weekdays_wrapper) + private val frequencyTitleTextView: TextView by bindView(R.id.frequency_title) + private val dailyFrequencySpinner: Spinner by bindView(R.id.task_frequency_spinner) + private val frequencyContainer: LinearLayout by bindView(R.id.task_frequency_container) + private val recyclerView: RecyclerView by bindView(R.id.checklist_recycler_view) + private val newCheckListEditText: EmojiEditText by bindView(R.id.new_checklist) + private val addChecklistItemButton: Button by bindView(R.id.add_checklist_button) + private val remindersWrapper: LinearLayout by bindView(R.id.task_reminders_wrapper) + private val newRemindersEditText: EditText by bindView(R.id.new_reminder_edittext) + private val remindersRecyclerView: RecyclerView by bindView(R.id.reminders_recycler_view) + private val emojiToggle0: ImageButton by bindView(R.id.emoji_toggle_btn0) + private val emojiToggle1: ImageButton by bindView(R.id.emoji_toggle_btn1) + private var emojiToggle2: ImageButton? = null + private val dueDateLayout: LinearLayout by bindView(R.id.task_duedate_layout) + private val dueDatePickerLayout: LinearLayout by bindView(R.id.task_duedate_picker_layout) + private val dueDateCheckBox: CheckBox by bindView(R.id.duedate_checkbox) + private val startDateTitleTextView: TextView by bindView(R.id.startdate_text_title) + private val startDatePickerText: EditText by bindView(R.id.startdate_text_edittext) + private val repeatablesStartDatePickerText: EditText by bindView(R.id.repeatables_startdate_text_edittext) + private var startDateListener: DateEditTextListener? = null + private val repeatablesLayout: LinearLayout by bindView(R.id.repeatables) + private val reapeatablesOnTextView: TextView by bindView(R.id.repeatables_on_title) + private val repeatablesOnSpinner: Spinner by bindView(R.id.task_repeatables_on_spinner) + private val repeatablesEveryXSpinner: NumberPicker by bindView(R.id.task_repeatables_every_x_spinner) + private val repeatablesFrequencyContainer: LinearLayout by bindView(R.id.task_repeatables_frequency_container) + private val summaryTextView: TextView by bindView(R.id.summary) + private val dueDatePickerText: EditText by bindView(R.id.duedate_text_edittext) + private var dueDateListener: DateEditTextListener? = null + private val tagsWrapper: LinearLayout by bindView(R.id.task_tags_wrapper) + private val tagsContainerLinearLayout: LinearLayout by bindView(R.id.task_tags_checklist) + private val repeatablesFrequencySpinner: Spinner by bindView(R.id.task_repeatables_frequency_spinner) + + + @Inject + internal lateinit var taskFilterHelper: TaskFilterHelper + @Inject + internal lateinit var taskRepository: TaskRepository + @Inject + internal lateinit var tagRepository: TagRepository + @field:[Inject Named(AppModule.NAMED_USER_ID)] + internal lateinit var userId: String + @Inject + internal lateinit var remoteConfigManager: RemoteConfigManager + @Inject + internal lateinit var taskAlarmManager: TaskAlarmManager + + private var task: Task? = null + private var taskBasedAllocation: Boolean = false + private val weekdayCheckboxes = ArrayList() + private val repeatablesWeekDayCheckboxes = ArrayList() + private var frequencyPicker: NumberPicker? = null + private var tags: List? = null + private var checklistAdapter: CheckListAdapter? = null + private var remindersAdapter: RemindersAdapter? = null + private var tagCheckBoxList: MutableList? = null + private var selectedTags: MutableList? = null + + private lateinit var remindersManager: RemindersManager + private var firstDayOfTheWeekHelper: FirstDayOfTheWeekHelper? = null + + private var taskType: String? = null + private var taskId: String? = null + private var popup: EmojiPopup? = EmojiPopup(emojiToggle0.rootView, this, ContextCompat.getColor(this, R.color.brand)) + + override fun getLayoutResId(): Int { + return R.layout.activity_task_form + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val intent = intent + val bundle = intent.extras + + taskType = bundle.getString(TASK_TYPE_KEY) + taskId = bundle.getString(TASK_ID_KEY) + taskBasedAllocation = bundle.getBoolean(ALLOCATION_MODE_KEY) + val showTagSelection = bundle.getBoolean(SHOW_TAG_SELECTION, true) + tagCheckBoxList = ArrayList() + + tagsWrapper.visibility = if (showTagSelection) View.VISIBLE else View.GONE + + if (bundle.containsKey(PARCELABLE_TASK)) { + task = bundle.getParcelable(PARCELABLE_TASK) + taskType = task?.type + } + + tagCheckBoxList = ArrayList() + selectedTags = ArrayList() + if (taskType == null) { + return + } + + remindersManager = RemindersManager(taskType) + + dueDateListener = DateEditTextListener(dueDatePickerText) + startDateListener = DateEditTextListener(startDatePickerText) + + btnDelete.isEnabled = false + ViewHelper.SetBackgroundTint(btnDelete, ContextCompat.getColor(this, R.color.red_10)) + btnDelete.setOnClickListener { view -> + AlertDialog.Builder(view.context) + .setTitle(getString(R.string.taskform_delete_title)) + .setMessage(getString(R.string.taskform_delete_message)).setPositiveButton(getString(R.string.yes)) { _, _ -> + + finish() + dismissKeyboard() + + taskId.notNull { taskRepository.deleteTask(it).subscribe(Consumer { }, RxErrorHandler.handleEmptyError()) } + }.setNegativeButton(getString(R.string.no)) { dialog, _ -> dialog.dismiss() }.show() + } + + val difficultyAdapter = ArrayAdapter.createFromResource(this, + R.array.task_difficulties, android.R.layout.simple_spinner_item) + difficultyAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + taskDifficultySpinner.adapter = difficultyAdapter + taskDifficultySpinner.setSelection(1) + + val attributeAdapter = ArrayAdapter.createFromResource(this, + R.array.task_attributes, android.R.layout.simple_spinner_item) + attributeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + taskAttributeSpinner.adapter = attributeAdapter + taskAttributeSpinner.setSelection(0) + + if (!taskBasedAllocation) { + attributeWrapper.visibility = View.GONE + } + + if (taskType == "habit") { + taskWrapper.removeView(startDateLayout) + + mainWrapper.removeView(checklistWrapper) + mainWrapper.removeView(remindersWrapper) + + positiveCheckBox.isChecked = true + negativeCheckBox.isChecked = true + } else { + mainWrapper.removeView(actionsLayout) + } + + if (taskType == "daily") { + val frequencyAdapter = ArrayAdapter.createFromResource(this, + R.array.daily_frequencies, android.R.layout.simple_spinner_item) + frequencyAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + this.dailyFrequencySpinner.adapter = frequencyAdapter + this.dailyFrequencySpinner.onItemSelectedListener = this + } else { + mainWrapper.removeView(weekdayWrapper) + mainWrapper.removeView(startDateLayout) + } + + if (taskType == "todo") { + dueDatePickerLayout.removeView(dueDatePickerText) + //Allows user to decide if they want to add a due date or not + dueDateCheckBox.setOnCheckedChangeListener { buttonView, _ -> + if (buttonView.isChecked) { + dueDatePickerLayout.addView(dueDatePickerText) + } else { + dueDatePickerLayout.removeView(dueDatePickerText) + } + } + } else { + mainWrapper.removeView(dueDateLayout) + } + + if (taskType != "reward") { + taskValueLayout.visibility = View.GONE + } else { + + mainWrapper.removeView(checklistWrapper) + mainWrapper.removeView(remindersWrapper) + + difficultyWrapper.visibility = View.GONE + attributeWrapper.visibility = View.GONE + } + + if (taskType == "todo" || taskType == "daily") { + createCheckListRecyclerView() + createRemindersRecyclerView() + } + + // Emoji keyboard stuff + var isTodo = false + if (taskType == "todo") { + isTodo = true + } + + // If it's a to-do, change the emojiToggle2 to the actual emojiToggle2 (prevents NPEs when not a to-do task) + emojiToggle2 = if (isTodo) { + findViewById(R.id.emoji_toggle_btn2) as ImageButton + } else { + emojiToggle0 + } + + // if showChecklist is inactive the wrapper is wrapper, so the reference can't be found + if (emojiToggle2 == null) { + emojiToggle2 = emojiToggle0 + } + + popup?.setSizeForSoftKeyboard() + popup?.setOnDismissListener { changeEmojiKeyboardIcon(false) } + popup?.setOnSoftKeyboardOpenCloseListener(object : EmojiPopup.OnSoftKeyboardOpenCloseListener { + + override fun onKeyboardOpen(keyBoardHeight: Int) { + + } + + override fun onKeyboardClose() { + if (popup?.isShowing == true) { + popup?.dismiss() + } + } + }) + + popup?.setOnEmojiconClickedListener { emojicon -> + if (currentFocus == null || !isEmojiEditText(currentFocus) || emojicon == null) { + return@setOnEmojiconClickedListener + } + val emojiEditText = currentFocus as EmojiEditText + val start = emojiEditText.selectionStart + val end = emojiEditText.selectionEnd + if (start < 0) { + emojiEditText.append(emojicon.emoji) + } else { + emojiEditText.text.replace(Math.min(start, end), + Math.max(start, end), emojicon.emoji, 0, + emojicon.emoji.length) + } + } + + popup?.setOnEmojiconBackspaceClickedListener { + if (isEmojiEditText(currentFocus)) { + val event = KeyEvent( + 0, 0, 0, KeyEvent.KEYCODE_DEL, 0, 0, 0, 0, KeyEvent.KEYCODE_ENDCALL) + currentFocus.dispatchKeyEvent(event) + } + } + + emojiToggle0.setOnClickListener(EmojiClickListener(taskText)) + emojiToggle1.setOnClickListener(EmojiClickListener(taskNotes)) + if (isTodo) { + emojiToggle2?.setOnClickListener(EmojiClickListener(newCheckListEditText)) + } + + enableRepeatables() + + tagRepository.getTags(userId) + .firstElement() + .subscribe(Consumer { loadedTags -> + tags = loadedTags + createTagsCheckBoxes() + }, RxErrorHandler.handleEmptyError() + ) + + if (taskId != null) { + taskRepository.getTask(taskId!!) + .firstElement() + .subscribe(Consumer { task -> + this.task = task + if (task != null) { + populate(task) + + setTitle(task) + if (taskType == "todo" || taskType == "daily") { + populateChecklistRecyclerView() + populateRemindersRecyclerView() + } + } + + setTitle(task) + }, RxErrorHandler.handleEmptyError()) + + btnDelete.isEnabled = true + } else { + setTitle(null as Task?) + taskText.requestFocus() + } + + addChecklistItemButton.setOnClickListener { addChecklistItem() } + newRemindersEditText.setOnClickListener { selectNewReminderTime() } + } + + override fun onDestroy() { + tagRepository.close() + super.onDestroy() + } + + override fun injectActivity(component: AppComponent?) { + component?.inject(this) + } + + fun hideMonthOptions() { + val repeatablesOnSpinnerParams = repeatablesOnSpinner.layoutParams + repeatablesOnSpinnerParams.height = 0 + repeatablesOnSpinner.layoutParams = repeatablesOnSpinnerParams + + val repeatablesOnTitleParams = reapeatablesOnTextView.layoutParams + repeatablesOnTitleParams.height = 0 + reapeatablesOnTextView.layoutParams = repeatablesOnTitleParams + } + + fun hideWeekOptions() { + val repeatablesFrequencyContainerParams = repeatablesFrequencyContainer.layoutParams + repeatablesFrequencyContainerParams.height = 0 + repeatablesFrequencyContainer.layoutParams = repeatablesFrequencyContainerParams + } + + // @TODO: abstract business logic to Presenter and only modify view? + private fun enableRepeatables() { + if (!remoteConfigManager.repeatablesAreEnabled() || taskType != "daily") { + repeatablesLayout.visibility = View.INVISIBLE + val repeatablesLayoutParams = repeatablesLayout.layoutParams + repeatablesLayoutParams.height = 0 + repeatablesLayout.layoutParams = repeatablesLayoutParams + return + } + + startDateLayout.visibility = View.INVISIBLE + + // Hide old stuff + val startDateLayoutParams = startDateLayout.layoutParams + startDateLayoutParams.height = 0 + startDateLayout.layoutParams = startDateLayoutParams + + val startDatePickerTextParams = startDatePickerText.layoutParams + startDatePickerTextParams.height = 0 + startDatePickerText.layoutParams = startDatePickerTextParams + + val startDateTitleTextViewParams = startDateTitleTextView.layoutParams + startDateTitleTextViewParams.height = 0 + startDateTitleTextView.layoutParams = startDateTitleTextViewParams + + weekdayWrapper.visibility = View.INVISIBLE + val weekdayWrapperParams = weekdayWrapper.layoutParams + weekdayWrapperParams.height = 0 + weekdayWrapper.layoutParams = weekdayWrapperParams + + val frequencyTitleTextViewParams = frequencyTitleTextView.layoutParams + frequencyTitleTextViewParams.height = 0 + frequencyTitleTextView.layoutParams = frequencyTitleTextViewParams + + val dailyFrequencySpinnerParams = dailyFrequencySpinner.layoutParams + dailyFrequencySpinnerParams.height = 0 + dailyFrequencySpinner.layoutParams = dailyFrequencySpinnerParams + + // Start Date + startDateListener = DateEditTextListener(repeatablesStartDatePickerText) + + // Frequency + val frequencyAdapter = ArrayAdapter.createFromResource(this, + R.array.repeatables_frequencies, android.R.layout.simple_spinner_item) + frequencyAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + this.repeatablesFrequencySpinner.adapter = frequencyAdapter + this.repeatablesFrequencySpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) { + generateSummary() + val r = resources + + // @TODO: remove magic numbers + + if (position == 2) { + hideWeekOptions() + + val repeatablesOnSpinnerParams = repeatablesOnSpinner.layoutParams + repeatablesOnSpinnerParams.height = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 72f, r.displayMetrics).toInt() + repeatablesOnSpinner.layoutParams = repeatablesOnSpinnerParams + + val repeatablesOnTitleParams = reapeatablesOnTextView.layoutParams + repeatablesOnTitleParams.height = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30f, r.displayMetrics).toInt() + reapeatablesOnTextView.layoutParams = repeatablesOnTitleParams + } else if (position == 1) { + hideMonthOptions() + + val repeatablesFrequencyContainerParams = repeatablesFrequencyContainer.layoutParams + repeatablesFrequencyContainerParams.height = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 220f, r.displayMetrics).toInt() + repeatablesFrequencyContainer.layoutParams = repeatablesFrequencyContainerParams + } else { + hideWeekOptions() + hideMonthOptions() + } + } + + override fun onNothingSelected(parent: AdapterView<*>) { + + } + } + + // Repeat On + val repeatablesOnAdapter = ArrayAdapter.createFromResource(this, + R.array.repeatables_on, android.R.layout.simple_spinner_item) + repeatablesOnAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + this.repeatablesOnSpinner.adapter = repeatablesOnAdapter + this.repeatablesOnSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) { + generateSummary() + } + + override fun onNothingSelected(parent: AdapterView<*>) { + + } + } + + // Every X + setupEveryXSpinner(repeatablesEveryXSpinner) + repeatablesEveryXSpinner.setOnValueChangedListener { _, _, _ -> generateSummary() } + + // WeekDays + this.repeatablesFrequencyContainer.removeAllViews() + var weekdays = resources.getStringArray(R.array.weekdays) + val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) + val dayOfTheWeek = sharedPreferences.getString("FirstDayOfTheWeek", + Integer.toString(Calendar.getInstance().firstDayOfWeek)) + firstDayOfTheWeekHelper = FirstDayOfTheWeekHelper.newInstance(Integer.parseInt(dayOfTheWeek)) + val weekdaysTemp = ArrayList(Arrays.asList(*weekdays)) + Collections.rotate(weekdaysTemp, firstDayOfTheWeekHelper?.dailyTaskFormOffset ?: 0) + weekdays = weekdaysTemp.toTypedArray() + + for (i in 0..6) { + val weekdayRow = layoutInflater.inflate(R.layout.row_checklist, this.repeatablesFrequencyContainer, false) + val checkbox = weekdayRow.findViewById(R.id.checkbox) as CheckBox + checkbox.text = weekdays[i] + checkbox.isChecked = true + checkbox.setOnClickListener { generateSummary() } + repeatablesWeekDayCheckboxes.add(checkbox) + repeatablesFrequencyContainer.addView(weekdayRow) + } + + generateSummary() + } + + override fun onRestoreInstanceState(savedInstanceState: Bundle) {} + + private fun generateSummary() { + val frequency = repeatablesFrequencySpinner.selectedItem.toString() + val everyX = repeatablesEveryXSpinner.value.toString() + var frequencyQualifier = "" + + when (frequency) { + "Daily" -> frequencyQualifier = "day(s)" + "Weekly" -> frequencyQualifier = "week(s)" + "Monthly" -> frequencyQualifier = "month(s)" + "Yearly" -> frequencyQualifier = "year(s)" + } + + var weekdays: String + val weekdayStrings = ArrayList() + val offset = firstDayOfTheWeekHelper?.dailyTaskFormOffset ?: 0 + if (this.repeatablesWeekDayCheckboxes[offset].isChecked) { + weekdayStrings.add("Monday") + } + if (this.repeatablesWeekDayCheckboxes[(offset + 1) % 7].isChecked) { + weekdayStrings.add("Tuesday") + } + if (this.repeatablesWeekDayCheckboxes[(offset + 2) % 7].isChecked) { + weekdayStrings.add("Wednesday") + } + if (this.repeatablesWeekDayCheckboxes[(offset + 3) % 7].isChecked) { + weekdayStrings.add("Thursday") + } + if (this.repeatablesWeekDayCheckboxes[(offset + 4) % 7].isChecked) { + weekdayStrings.add("Friday") + } + if (this.repeatablesWeekDayCheckboxes[(offset + 5) % 7].isChecked) { + weekdayStrings.add("Saturday") + } + if (this.repeatablesWeekDayCheckboxes[(offset + 6) % 7].isChecked) { + weekdayStrings.add("Sunday") + } + weekdays = " on " + TextUtils.join(", ", weekdayStrings) + if (frequency != "Weekly") { + weekdays = "" + } + + if (frequency == "Monthly") { + val calendar = startDateListener?.getCalendar() + val monthlyFreq = repeatablesOnSpinner.selectedItem.toString() + weekdays = if (monthlyFreq == "Day of Month") { + val date = calendar?.get(Calendar.DATE) + " on the " + date.toString() + } else { + val week = calendar?.get(Calendar.WEEK_OF_MONTH) + val dayLongName = calendar?.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.getDefault()) + " on the " + week.toString() + " week on " + dayLongName + } + } + + val summary = resources.getString(R.string.repeat_summary, frequency, everyX, frequencyQualifier, weekdays) + summaryTextView.text = summary + } + + private fun populateRepeatables(task: Task) { + // Frequency + var frequencySelection = 0 + when { + task.frequency == "weekly" -> frequencySelection = 1 + task.frequency == "monthly" -> frequencySelection = 2 + task.frequency == "yearly" -> frequencySelection = 3 + } + this.repeatablesFrequencySpinner.setSelection(frequencySelection) + this.repeatablesEveryXSpinner.value = task.everyX ?: 0 + + if (task.frequency == "weekly") { + if (repeatablesWeekDayCheckboxes.size == 7) { + val offset = firstDayOfTheWeekHelper?.dailyTaskFormOffset ?: 0 + this.repeatablesWeekDayCheckboxes[offset].isChecked = this.task?.repeat?.m ?: false + this.repeatablesWeekDayCheckboxes[(offset + 1) % 7].isChecked = this.task?.repeat?.t ?: false + this.repeatablesWeekDayCheckboxes[(offset + 2) % 7].isChecked = this.task?.repeat?.w ?: false + this.repeatablesWeekDayCheckboxes[(offset + 3) % 7].isChecked = this.task?.repeat?.th ?: false + this.repeatablesWeekDayCheckboxes[(offset + 4) % 7].isChecked = this.task?.repeat?.f ?: false + this.repeatablesWeekDayCheckboxes[(offset + 5) % 7].isChecked = this.task?.repeat?.s ?: false + this.repeatablesWeekDayCheckboxes[(offset + 6) % 7].isChecked = this.task?.repeat?.su ?: false + } + } + + // Repeats On + + if (task.getDaysOfMonth()?.isNotEmpty() == true) { + this.repeatablesOnSpinner.setSelection(0) + } else if (task.getWeeksOfMonth()?.isNotEmpty() == true) { + this.repeatablesOnSpinner.setSelection(1) + } + } + + private fun setupEveryXSpinner(frequencyPicker: NumberPicker) { + frequencyPicker.minValue = 0 + frequencyPicker.maxValue = 366 + frequencyPicker.value = 1 + } + + private fun isEmojiEditText(view: View?): Boolean { + return view is EmojiEditText + } + + private fun changeEmojiKeyboardIcon(keyboardOpened: Boolean) { + if (keyboardOpened) { + emojiToggle0.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_keyboard_grey600_24dp)) + emojiToggle1.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_keyboard_grey600_24dp)) + emojiToggle2?.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_keyboard_grey600_24dp)) + } else { + emojiToggle0.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_emoticon_grey600_24dp)) + emojiToggle1.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_emoticon_grey600_24dp)) + emojiToggle2?.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_emoticon_grey600_24dp)) + } + } + + private fun createCheckListRecyclerView() { + checklistAdapter = CheckListAdapter() + + val llm = LinearLayoutManager(this) + llm.orientation = LinearLayoutManager.VERTICAL + + recyclerView.layoutManager = llm + recyclerView.adapter = checklistAdapter + + recyclerView.layoutManager = WrapContentRecyclerViewLayoutManager(this) + + val callback = SimpleItemTouchHelperCallback(checklistAdapter) + val mItemTouchHelper = ItemTouchHelper(callback) + mItemTouchHelper.attachToRecyclerView(recyclerView) + } + + private fun populateChecklistRecyclerView() { + var checklistItems: List = ArrayList() + if (task?.isManaged == true) { + task?.checklist.notNull { + checklistItems = taskRepository.getUnmanagedCopy(it) + } + } + checklistAdapter?.setItems(checklistItems) + } + + private fun addChecklistItem() { + val text = newCheckListEditText.text.toString() + val item = ChecklistItem(text) + checklistAdapter?.addItem(item) + newCheckListEditText.setText("") + } + + private fun createRemindersRecyclerView() { + taskType.notNull { remindersAdapter = RemindersAdapter(it) } + + val llm = LinearLayoutManager(this) + llm.orientation = LinearLayoutManager.VERTICAL + + remindersRecyclerView.layoutManager = llm + remindersRecyclerView.adapter = remindersAdapter + + remindersRecyclerView.layoutManager = WrapContentRecyclerViewLayoutManager(this) + + + val callback = SimpleItemTouchHelperCallback(remindersAdapter) + val mItemTouchHelper = ItemTouchHelper(callback) + mItemTouchHelper.attachToRecyclerView(remindersRecyclerView) + } + + private fun populateRemindersRecyclerView() { + var reminders: List = ArrayList() + task?.reminders.notNull { + reminders = taskRepository.getUnmanagedCopy(it) + } + + remindersAdapter?.setReminders(reminders) + } + + private fun addNewReminder(remindersItem: RemindersItem) { + remindersAdapter?.addItem(remindersItem) + } + + private fun selectNewReminderTime() { + remindersManager.createReminderTimeDialog({ it.notNull { this.addNewReminder(it) } }, taskType, this, null) + } + + private fun createTagsCheckBoxes() { + this.tagsContainerLinearLayout.removeAllViews() + for ((position, tag) in (tags ?: emptyList()).withIndex()) { + val row = layoutInflater.inflate(R.layout.row_checklist, this.tagsContainerLinearLayout, false) as TableRow + val checkbox = row.findViewById(R.id.checkbox) as CheckBox + row.id = position + checkbox.text = tag.name // set text Name + checkbox.id = position + //This is to check if the tag was selected by the user. Similar to onClickListener + checkbox.setOnCheckedChangeListener { buttonView, _ -> + if (buttonView.isChecked) { + if (selectedTags?.contains(tag) == false) { + selectedTags?.add(tag) + } + } else { + if (selectedTags?.contains(tag) == true) { + selectedTags?.remove(tag) + } + } + } + checkbox.isChecked = taskFilterHelper.isTagChecked(tag.getId()) + tagsContainerLinearLayout.addView(row) + tagCheckBoxList?.add(checkbox) + } + + if (task != null) { + fillTagCheckboxes() + } + } + + private fun setTitle(task: Task?) { + val actionBar = supportActionBar + + if (actionBar != null) { + + var title = "" + + if (task != null && task.isValid) { + title = resources.getString(R.string.action_edit) + " " + task.text + } else { + when (taskType) { + "todo" -> title = resources.getString(R.string.new_todo) + "daily" -> title = resources.getString(R.string.new_daily) + "habit" -> title = resources.getString(R.string.new_habit) + "reward" -> title = resources.getString(R.string.new_reward) + } + } + + actionBar.title = title + } + } + + private fun setDailyFrequencyViews() { + this.frequencyContainer.removeAllViews() + if (this.dailyFrequencySpinner.selectedItemPosition == 0) { + var weekdays = resources.getStringArray(R.array.weekdays) + val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) + val dayOfTheWeek = sharedPreferences.getString("FirstDayOfTheWeek", + Integer.toString(Calendar.getInstance().firstDayOfWeek)) + firstDayOfTheWeekHelper = FirstDayOfTheWeekHelper.newInstance(Integer.parseInt(dayOfTheWeek)) + val weekdaysTemp = ArrayList(Arrays.asList(*weekdays)) + Collections.rotate(weekdaysTemp, firstDayOfTheWeekHelper?.dailyTaskFormOffset ?: 0) + weekdays = weekdaysTemp.toTypedArray() + + for (i in 0..6) { + val weekdayRow = layoutInflater.inflate(R.layout.row_checklist, this.frequencyContainer, false) + val checkbox = weekdayRow.findViewById(R.id.checkbox) as CheckBox + checkbox.text = weekdays[i] + checkbox.isChecked = true + this.weekdayCheckboxes.add(checkbox) + this.frequencyContainer.addView(weekdayRow) + } + } else { + val dayRow = layoutInflater.inflate(R.layout.row_number_picker, this.frequencyContainer, false) + this.frequencyPicker = dayRow.findViewById(R.id.numberPicker) as NumberPicker + this.frequencyPicker?.minValue = 1 + this.frequencyPicker?.maxValue = 366 + val tv = dayRow.findViewById(R.id.label) as TextView + tv.text = resources.getString(R.string.frequency_daily) + this.frequencyContainer.addView(dayRow) + } + + if (this.task?.isValid == true) { + if (this.dailyFrequencySpinner.selectedItemPosition == 0) { + val offset = firstDayOfTheWeekHelper?.dailyTaskFormOffset ?: 0 + this.weekdayCheckboxes[offset].isChecked = this.task?.repeat?.m ?: false + this.weekdayCheckboxes[(offset + 1) % 7].isChecked = this.task?.repeat?.t ?: false + this.weekdayCheckboxes[(offset + 2) % 7].isChecked = this.task?.repeat?.w ?: false + this.weekdayCheckboxes[(offset + 3) % 7].isChecked = this.task?.repeat?.th ?: false + this.weekdayCheckboxes[(offset + 4) % 7].isChecked = this.task?.repeat?.f ?: false + this.weekdayCheckboxes[(offset + 5) % 7].isChecked = this.task?.repeat?.s ?: false + this.weekdayCheckboxes[(offset + 6) % 7].isChecked = this.task?.repeat?.su ?: false + } else { + this.frequencyPicker?.value = this.task?.everyX ?: 0 + } + } + + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + // Inflate the menu; this adds items to the action bar if it is present. + menuInflater.inflate(R.menu.menu_save, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + val id = item.itemId + + + if (id == R.id.action_save_changes) { + finishActivitySuccessfully() + return true + } + + return super.onOptionsItemSelected(item) + } + + private fun populate(task: Task) { + if (!task.isValid) { + return + } + taskText.setText(task.text) + taskNotes.setText(task.notes) + taskValue.setText(String.format(Locale.getDefault(), "%.2f", task.value)) + + for (tag in task.tags ?: emptyList()) { + selectedTags?.add(tag) + } + + if (tags != null) { + fillTagCheckboxes() + } + + val priority = task.priority + when { + Math.abs(priority - 0.1) < 0.000001 -> this.taskDifficultySpinner.setSelection(0) + Math.abs(priority - 1.0) < 0.000001 -> this.taskDifficultySpinner.setSelection(1) + Math.abs(priority - 1.5) < 0.000001 -> this.taskDifficultySpinner.setSelection(2) + Math.abs(priority - 2.0) < 0.000001 -> this.taskDifficultySpinner.setSelection(3) + } + + val attribute = task.attribute + if (attribute != null) { + when (attribute) { + Stats.STRENGTH -> taskAttributeSpinner.setSelection(0) + Stats.INTELLIGENCE -> taskAttributeSpinner.setSelection(1) + Stats.CONSTITUTION -> taskAttributeSpinner.setSelection(2) + Stats.PERCEPTION -> taskAttributeSpinner.setSelection(3) + } + } + + if (task.type == "habit") { + positiveCheckBox.isChecked = task.up ?: false + negativeCheckBox.isChecked = task.down ?: false + } + + if (task.type == "daily") { + + if (task.startDate != null) { + startDateListener?.setCalendar(task.startDate) + } + + if (task.frequency == "weekly") { + this.dailyFrequencySpinner.setSelection(0) + if (weekdayCheckboxes.size == 7) { + val offset = firstDayOfTheWeekHelper?.dailyTaskFormOffset ?: 0 + this.weekdayCheckboxes[offset].isChecked = this.task?.repeat?.m ?: false + this.weekdayCheckboxes[(offset + 1) % 7].isChecked = this.task?.repeat?.t ?: false + this.weekdayCheckboxes[(offset + 2) % 7].isChecked = this.task?.repeat?.w ?: false + this.weekdayCheckboxes[(offset + 3) % 7].isChecked = this.task?.repeat?.th ?: false + this.weekdayCheckboxes[(offset + 4) % 7].isChecked = this.task?.repeat?.f ?: false + this.weekdayCheckboxes[(offset + 5) % 7].isChecked = this.task?.repeat?.s ?: false + this.weekdayCheckboxes[(offset + 6) % 7].isChecked = this.task?.repeat?.su ?: false + } + } else { + this.dailyFrequencySpinner.setSelection(1) + if (this.frequencyPicker != null) { + this.frequencyPicker?.value = task.everyX ?: 0 + } + } + + populateRepeatables(task) + } + + if (task.type == "todo") { + if (task.dueDate != null) { + dueDateCheckBox.isChecked = true + dueDateListener?.setCalendar(task.dueDate) + } + } + + if (task.isGroupTask) { + AlertDialog.Builder(this) + .setTitle(R.string.group_tasks_edit_title) + .setMessage(R.string.group_tasks_edit_description) + .setPositiveButton(android.R.string.ok) { _, _ -> finish() } + .show() + } + } + + private fun fillTagCheckboxes() { + for (tag in task?.tags ?: emptyList()) { + val position = tags?.indexOf(tag) ?: 0 + if (tagCheckBoxList?.size ?: 0 > position && position >= 0) { + tagCheckBoxList?.get(position)?.isChecked = true + } + } + } + + private fun saveTask(task: Task): Boolean { + + val text = MarkdownParser.parseCompiled(taskText.text) + if (text == null || text.isEmpty()) { + return false + } + + if (!task.isValid) { + return true + } + + taskRepository.executeTransaction(Realm.Transaction { realm -> + try { + task.text = text + task.notes = MarkdownParser.parseCompiled(taskNotes.text) + } catch (ignored: IllegalArgumentException) { + + } + + if (checklistAdapter != null) { + val newChecklist = RealmList() + checklistAdapter?.checkListItems.notNull { newChecklist.addAll(realm.copyToRealmOrUpdate(it)) } + task.checklist = newChecklist + } + if (remindersAdapter != null) { + val newReminders = RealmList() + remindersAdapter?.remindersItems.notNull { newReminders.addAll(realm.copyToRealmOrUpdate(it)) } + task.reminders = newReminders + } + + + val taskTags = RealmList() + selectedTags.notNull { taskTags.addAll(it) } + task.tags = taskTags + + + task.priority = when { + taskDifficultySpinner.selectedItemPosition == 0 -> 0.1 + taskDifficultySpinner.selectedItemPosition == 1 -> 1.0 + taskDifficultySpinner.selectedItemPosition == 2 -> 1.5 + taskDifficultySpinner.selectedItemPosition == 3 -> 2.0 + else -> { 1.0 } + }.toFloat() + + if (!taskBasedAllocation) { + task.attribute = Stats.STRENGTH + } else { + when (taskAttributeSpinner.selectedItemPosition) { + 0 -> task.attribute = Stats.STRENGTH + 1 -> task.attribute = Stats.INTELLIGENCE + 2 -> task.attribute = Stats.CONSTITUTION + 3 -> task.attribute = Stats.PERCEPTION + } + } + + when (task.type) { + "habit" -> { + task.up = positiveCheckBox.isChecked + task.down = negativeCheckBox.isChecked + } + + "daily" -> { + task.startDate = Date(startDateListener?.getCalendar()?.timeInMillis ?: Date().time) + + if (this.dailyFrequencySpinner.selectedItemPosition == 0) { + task.frequency = "weekly" + + var repeat = task.repeat + if (repeat == null) { + repeat = Days() + task.repeat = repeat + } + + val offset = firstDayOfTheWeekHelper?.dailyTaskFormOffset ?: 0 + repeat.m = this.weekdayCheckboxes[offset].isChecked + repeat.t = this.weekdayCheckboxes[(offset + 1) % 7].isChecked + repeat.w = this.weekdayCheckboxes[(offset + 2) % 7].isChecked + repeat.th = this.weekdayCheckboxes[(offset + 3) % 7].isChecked + repeat.f = this.weekdayCheckboxes[(offset + 4) % 7].isChecked + repeat.s = this.weekdayCheckboxes[(offset + 5) % 7].isChecked + repeat.su = this.weekdayCheckboxes[(offset + 6) % 7].isChecked + } else { + task.frequency = "daily" + task.everyX = this.frequencyPicker?.value + } + + if (remoteConfigManager.repeatablesAreEnabled()) { + val frequency = this.repeatablesFrequencySpinner.selectedItemPosition + var frequencyString = "" + when (frequency) { + 0 -> frequencyString = "daily" + 1 -> frequencyString = "weekly" + 2 -> frequencyString = "monthly" + 3 -> frequencyString = "yearly" + } + task.frequency = frequencyString + + task.everyX = this.repeatablesEveryXSpinner.value + + var repeat = task.repeat + if (repeat == null) { + repeat = Days() + task.repeat = repeat + } + + if ("weekly" == frequencyString) { + val offset = firstDayOfTheWeekHelper?.dailyTaskFormOffset ?: 0 + repeat.m = this.repeatablesWeekDayCheckboxes[offset].isChecked + repeat.t = this.repeatablesWeekDayCheckboxes[(offset + 1) % 7].isChecked + repeat.w = this.repeatablesWeekDayCheckboxes[(offset + 2) % 7].isChecked + repeat.th = this.repeatablesWeekDayCheckboxes[(offset + 3) % 7].isChecked + repeat.f = this.repeatablesWeekDayCheckboxes[(offset + 4) % 7].isChecked + repeat.s = this.repeatablesWeekDayCheckboxes[(offset + 5) % 7].isChecked + repeat.su = this.repeatablesWeekDayCheckboxes[(offset + 6) % 7].isChecked + } + + if ("monthly" == frequencyString) { + val calendar = startDateListener?.getCalendar() + val monthlyFreq = repeatablesOnSpinner.selectedItem.toString() + + if (monthlyFreq == "Day of Month") { + val date = calendar?.get(Calendar.DATE) + val daysOfMonth = ArrayList() + date.notNull { daysOfMonth.add(it) } + task.setDaysOfMonth(daysOfMonth) + task.setWeeksOfMonth(ArrayList()) + } else { + val week = calendar?.get(Calendar.WEEK_OF_MONTH) + val weeksOfMonth = ArrayList() + week.notNull { weeksOfMonth.add(it) } + task.setWeeksOfMonth(weeksOfMonth) + task.setDaysOfMonth(ArrayList()) + } + } + } + } + + "todo" -> { + if (dueDateCheckBox.isChecked) { + task.dueDate = Date(dueDateListener?.getCalendar()?.timeInMillis ?: Date().time) + } else { + task.dueDate = null + } + } + + "reward" -> { + val value = taskValue.text.toString() + if (!value.isEmpty()) { + val localFormat = DecimalFormat.getInstance(Locale.getDefault()) + try { + task.value = localFormat.parse(value).toDouble() + } catch (ignored: ParseException) { + } + + } else { + task.value = 0.0 + } + + } + } + }) + return true + } + + override fun onItemSelected(parent: AdapterView<*>, view: View, + pos: Int, id: Long) { + this.setDailyFrequencyViews() + } + + override fun onNothingSelected(parent: AdapterView<*>) { + this.setDailyFrequencyViews() + } + + private fun prepareSave() { + var thisTask = task + if (thisTask == null) { + thisTask = Task() + thisTask.type = taskType ?: "habit" + } + + if (this.saveTask(thisTask) && thisTask.isValid) { + //send back to other elements. + if (thisTask.id == null || thisTask.id?.isEmpty() == true) { + taskRepository.createTaskInBackground(thisTask) + } else { + taskRepository.updateTaskInBackground(thisTask) + } + val unmanagedTask = taskRepository.getUnmanagedCopy(thisTask) + taskAlarmManager.scheduleAlarmsForTask(unmanagedTask) + } + } + + override fun onSupportNavigateUp(): Boolean { + finish() + dismissKeyboard() + return true + } + + override fun onBackPressed() { + finish() + dismissKeyboard() + } + + private fun finishActivitySuccessfully() { + this.prepareSave() + finishWithSuccess() + dismissKeyboard() + } + + private fun finishWithSuccess() { + val mainHandler = Handler(this.mainLooper) + mainHandler.postDelayed({ + val resultIntent = Intent() + resultIntent.putExtra(TaskFormActivity.TASK_TYPE_KEY, taskType) + setResult(Activity.RESULT_OK, resultIntent) + finish() + }, 500) + } + + private fun dismissKeyboard() { + val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + val currentFocus = currentFocus + if (currentFocus != null) { + imm.hideSoftInputFromWindow(currentFocus.windowToken, 0) + } + popup?.dismiss() + popup = null + } + + private inner class DateEditTextListener internal constructor(internal var datePickerText: EditText) : View.OnClickListener, DatePickerDialog.OnDateSetListener { + internal var calendar: Calendar = Calendar.getInstance() + internal var datePickerDialog: DatePickerDialog + internal var dateFormatter: DateFormat + + init { + this.datePickerText.setOnClickListener(this) + this.dateFormatter = DateFormat.getDateInstance() + this.datePickerDialog = DatePickerDialog(datePickerText.context, this, + calendar.get(Calendar.YEAR), + calendar.get(Calendar.MONTH), + calendar.get(Calendar.DAY_OF_MONTH)) + + val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(applicationContext) + val dayOfTheWeek = sharedPreferences.getString("FirstDayOfTheWeek", + Integer.toString(Calendar.getInstance().firstDayOfWeek)) + val firstDayOfTheWeekHelper = FirstDayOfTheWeekHelper.newInstance(Integer.parseInt(dayOfTheWeek)) + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT_WATCH) { + datePickerDialog.datePicker.calendarView.firstDayOfWeek = firstDayOfTheWeekHelper.firstDayOfTheWeek + } else { + datePickerDialog.datePicker.firstDayOfWeek = firstDayOfTheWeekHelper + .firstDayOfTheWeek + } + + this.datePickerDialog.setButton(DialogInterface.BUTTON_NEUTRAL, resources.getString(R.string.today)) { _, _ -> setCalendar(Calendar.getInstance().time) } + updateDateText() + } + + override fun onClick(view: View) { + datePickerDialog.show() + } + + override fun onDateSet(view: DatePicker, year: Int, monthOfYear: Int, dayOfMonth: Int) { + calendar.set(year, monthOfYear, dayOfMonth) + updateDateText() + } + + fun getCalendar(): Calendar { + return calendar.clone() as Calendar + } + + fun setCalendar(date: Date?) { + calendar.time = date + datePickerDialog.updateDate(calendar.get(Calendar.YEAR), + calendar.get(Calendar.MONTH), + calendar.get(Calendar.DAY_OF_MONTH)) + updateDateText() + } + + private fun updateDateText() { + datePickerText.setText(dateFormatter.format(calendar.time)) + } + } + + private inner class EmojiClickListener internal constructor(internal var view: EmojiEditText) : View.OnClickListener { + + override fun onClick(v: View) { + if (popup?.isShowing == false) { + + if (popup?.isKeyBoardOpen == true) { + popup?.showAtBottom() + changeEmojiKeyboardIcon(true) + } else { + view.isFocusableInTouchMode = true + view.requestFocus() + popup?.showAtBottomPending() + val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + inputMethodManager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT) + changeEmojiKeyboardIcon(true) + } + } else { + popup?.dismiss() + changeEmojiKeyboardIcon(false) + } + } + } + + companion object { + const val TASK_ID_KEY = "taskId" + const val USER_ID_KEY = "userId" + const val TASK_TYPE_KEY = "type" + const val SHOW_TAG_SELECTION = "show_tag_selection" + const val ALLOCATION_MODE_KEY = "allocationModeKey" + const val SHOW_CHECKLIST = "show_checklist" + + const val PARCELABLE_TASK = "parcelable_task" + const val SAVE_TO_DB = "saveToDb" + + // in order to disable the event handler in MainActivity + const val SET_IGNORE_FLAG = "ignoreFlag" + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/challenges/ChallengeTasksRecyclerViewAdapter.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/challenges/ChallengeTasksRecyclerViewAdapter.java index c2808bdc6..795018af3 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/challenges/ChallengeTasksRecyclerViewAdapter.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/social/challenges/ChallengeTasksRecyclerViewAdapter.java @@ -1,6 +1,7 @@ package com.habitrpg.android.habitica.ui.adapter.social.challenges; import android.content.Context; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.view.View; import android.view.ViewGroup; @@ -89,7 +90,7 @@ public class ChallengeTasksRecyclerViewAdapter return addItemSubject.toFlowable(BackpressureStrategy.BUFFER); } - public int addTaskUnder(Task taskToAdd, Task taskAbove) { + public int addTaskUnder(Task taskToAdd, @Nullable Task taskAbove) { int position = U.findIndex(this.getContent(), t -> t.getId().equals(taskAbove.getId())); getContent().add(position + 1, taskToAdd); @@ -159,18 +160,18 @@ public class ChallengeTasksRecyclerViewAdapter private PublishSubject callback; private Task newTask; - public AddItemViewHolder(View itemView, PublishSubject callback) { - super(itemView, false); + AddItemViewHolder(View itemView, PublishSubject callback) { + super(itemView); this.callback = callback; - addBtn = (Button) itemView.findViewById(R.id.btn_add_task); + addBtn = itemView.findViewById(R.id.btn_add_task); addBtn.setClickable(true); addBtn.setOnClickListener(view -> callback.onNext(newTask)); setContext(itemView.getContext()); } @Override - public void bindHolder(Task newTask, int position) { + public void bindHolder(@NonNull Task newTask, int position) { this.newTask = newTask; addBtn.setText(newTask.getText()); } @@ -180,16 +181,16 @@ public class ChallengeTasksRecyclerViewAdapter private TextView divider_name; - public DividerViewHolder(View itemView) { - super(itemView, false); + DividerViewHolder(View itemView) { + super(itemView); - divider_name = (TextView) itemView.findViewById(R.id.divider_name); + divider_name = itemView.findViewById(R.id.divider_name); setContext(itemView.getContext()); } @Override - public void bindHolder(Task newTask, int position) { + public void bindHolder(@NonNull Task newTask, int position) { divider_name.setText(newTask.getText()); } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/BaseTasksRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/BaseTasksRecyclerViewAdapter.kt index a5fd11377..74b6921ff 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/BaseTasksRecyclerViewAdapter.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/BaseTasksRecyclerViewAdapter.kt @@ -8,6 +8,7 @@ import android.view.ViewGroup import com.habitrpg.android.habitica.HabiticaBaseApplication import com.habitrpg.android.habitica.components.AppComponent import com.habitrpg.android.habitica.data.TaskRepository +import com.habitrpg.android.habitica.extensions.notNull import com.habitrpg.android.habitica.helpers.RxErrorHandler import com.habitrpg.android.habitica.helpers.TaskFilterHelper import com.habitrpg.android.habitica.models.tasks.Task @@ -34,7 +35,7 @@ abstract class BaseTasksRecyclerViewAdapter(var taskTyp this.setHasStableIds(true) this.context = newContext.applicationContext this.filteredContent = ArrayList() - injectThis(HabiticaBaseApplication.getComponent()) + HabiticaBaseApplication.component.notNull { injectThis(it) } if (loadFromDatabase()) { this.loadContent(true) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseFragment.kt index 1d39cfb6e..5578627dd 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseFragment.kt @@ -10,6 +10,7 @@ import com.habitrpg.android.habitica.HabiticaBaseApplication import com.habitrpg.android.habitica.components.AppComponent import com.habitrpg.android.habitica.data.TutorialRepository import com.habitrpg.android.habitica.events.DisplayTutorialEvent +import com.habitrpg.android.habitica.extensions.notNull import com.habitrpg.android.habitica.helpers.AmplitudeManager import com.habitrpg.android.habitica.helpers.RxErrorHandler import io.reactivex.android.schedulers.AndroidSchedulers @@ -42,7 +43,9 @@ abstract class BaseFragment : DialogFragment() { } override fun onCreate(savedInstanceState: Bundle?) { - injectFragment(HabiticaBaseApplication.getComponent()) + HabiticaBaseApplication.component.notNull { + injectFragment(it) + } this.showsDialog = false super.onCreate(savedInstanceState) } @@ -107,8 +110,10 @@ abstract class BaseFragment : DialogFragment() { } super.onDestroyView() - val refWatcher = HabiticaApplication.getInstance(context).refWatcher - refWatcher.watch(this) + context.notNull { + val refWatcher = HabiticaBaseApplication.getInstance(it).refWatcher + refWatcher?.watch(this) + } } override fun onDestroy() { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseMainFragment.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseMainFragment.java deleted file mode 100644 index b4f9e5c16..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseMainFragment.java +++ /dev/null @@ -1,190 +0,0 @@ -package com.habitrpg.android.habitica.ui.fragments; - -import android.content.Context; -import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.design.widget.AppBarLayout; -import android.support.design.widget.CollapsingToolbarLayout; -import android.support.design.widget.TabLayout; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; - -import com.habitrpg.android.habitica.data.ApiClient; -import com.habitrpg.android.habitica.data.UserRepository; -import com.habitrpg.android.habitica.helpers.RxErrorHandler; -import com.habitrpg.android.habitica.helpers.SoundManager; -import com.habitrpg.android.habitica.models.user.User; -import com.habitrpg.android.habitica.ui.activities.MainActivity; -import com.roughike.bottombar.BottomBar; - -import javax.inject.Inject; - -public abstract class BaseMainFragment extends BaseFragment { - - @Inject - public ApiClient apiClient; - @Inject - protected UserRepository userRepository; - @Nullable - public MainActivity activity; - @Nullable - public TabLayout tabLayout; - @Nullable - private CollapsingToolbarLayout collapsingToolbar; - @Nullable - protected FrameLayout toolbarAccessoryContainer; - @Nullable - public BottomBar bottomNavigation; - public ViewGroup floatingMenuWrapper; - public boolean usesTabLayout; - public boolean usesBottomNavigation = false; - public String fragmentSidebarIdentifier; - @Inject - protected SoundManager soundManager; - @Nullable - protected User user; - - public void setUser(@Nullable User user) { - this.user = user; - } - - public void updateUserData(User user) { - this.user = user; - } - - public void setTabLayout(@Nullable TabLayout tabLayout) { - this.tabLayout = tabLayout; - } - - public void setBottomNavigation(@Nullable BottomBar bottomNavigation) { - this.bottomNavigation = bottomNavigation; - } - - public void setFloatingMenuWrapper(ViewGroup view) { - this.floatingMenuWrapper = view; - } - - public void setActivity(@Nullable MainActivity activity) { - this.activity = activity; - } - - @Override - public void onViewStateRestored(Bundle savedInstanceState) { - super.onViewStateRestored(savedInstanceState); - } - - @Override - public void onAttach(Context context) { - super.onAttach(context); - - if (getActivity().getClass().equals(MainActivity.class)) { - this.activity = (MainActivity) getActivity(); - } - } - - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - super.onCreateView(inflater, container, savedInstanceState); - if (savedInstanceState != null && savedInstanceState.containsKey("userId")) { - String userId = savedInstanceState.getString("userId"); - if (userId != null && userRepository != null) { - getCompositeSubscription().add(userRepository.getUser(userId).subscribe(habitRPGUser -> user = habitRPGUser, RxErrorHandler.handleEmptyError())); - } - } - - if (bottomNavigation != null) { - if (this.usesBottomNavigation) { - bottomNavigation.removeOnTabSelectListener(); - bottomNavigation.removeOnTabReselectListener(); - bottomNavigation.setVisibility(View.VISIBLE); - } else { - bottomNavigation.setVisibility(View.GONE); - } - } - - if (floatingMenuWrapper != null) { - floatingMenuWrapper.removeAllViews(); - } - - setHasOptionsMenu(true); - - if (activity != null) { - activity.setActiveFragment(this); - } - - updateTabLayoutVisibility(); - - return null; - } - - private void updateTabLayoutVisibility() { - if (tabLayout != null) { - if (this.usesTabLayout) { - tabLayout.removeAllTabs(); - tabLayout.setVisibility(View.VISIBLE); - tabLayout.setTabMode(TabLayout.MODE_FIXED); - } else { - tabLayout.setVisibility(View.GONE); - } - } - } - - @Override - public void onDestroy() { - if (userRepository != null) { - userRepository.close(); - } - super.onDestroy(); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - if (user != null && user.isValid()) { - outState.putString("userId", user.getId()); - } - - super.onSaveInstanceState(outState); - } - - public String customTitle() { - return ""; - } - - public void hideToolbar() { - if (activity != null) { - activity.avatar_with_bars.setVisibility(View.GONE); - } - } - - public void showToolbar() { - if (activity != null) { - activity.avatar_with_bars.setVisibility(View.VISIBLE); - } - } - - public void disableToolbarScrolling() { - if (collapsingToolbar != null) { - AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) collapsingToolbar.getLayoutParams(); - params.setScrollFlags(0); - } - } - - public void enableToolbarScrolling() { - if (collapsingToolbar != null) { - AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) collapsingToolbar.getLayoutParams(); - params.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED); - } - } - - public void setCollapsingToolbar(@Nullable CollapsingToolbarLayout collapsingToolbar) { - this.collapsingToolbar = collapsingToolbar; - } - - public void setToolbarAccessoryContainer(FrameLayout toolbarAccessoryContainer) { - this.toolbarAccessoryContainer = toolbarAccessoryContainer; - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseMainFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseMainFragment.kt new file mode 100644 index 000000000..7303e9ea3 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseMainFragment.kt @@ -0,0 +1,130 @@ +package com.habitrpg.android.habitica.ui.fragments + +import android.content.Context +import android.os.Bundle +import android.support.design.widget.AppBarLayout +import android.support.design.widget.CollapsingToolbarLayout +import android.support.design.widget.TabLayout +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout + +import com.habitrpg.android.habitica.data.ApiClient +import com.habitrpg.android.habitica.data.UserRepository +import com.habitrpg.android.habitica.helpers.RxErrorHandler +import com.habitrpg.android.habitica.helpers.SoundManager +import com.habitrpg.android.habitica.models.user.User +import com.habitrpg.android.habitica.ui.activities.MainActivity +import com.roughike.bottombar.BottomBar +import io.reactivex.functions.Consumer + +import javax.inject.Inject + +abstract class BaseMainFragment : BaseFragment() { + + @Inject + lateinit var apiClient: ApiClient + @Inject + lateinit var userRepository: UserRepository + @Inject + lateinit var soundManager: SoundManager + + open var activity: MainActivity? = null + var tabLayout: TabLayout? = null + var collapsingToolbar: CollapsingToolbarLayout? = null + var toolbarAccessoryContainer: FrameLayout? = null + var bottomNavigation: BottomBar? = null + var floatingMenuWrapper: ViewGroup? = null + var usesTabLayout: Boolean = false + var usesBottomNavigation = false + var fragmentSidebarIdentifier: String? = null + open var user: User? = null + + open fun updateUserData(user: User?) { + this.user = user + } + + override fun onAttach(context: Context?) { + super.onAttach(context) + + if (getActivity()?.javaClass == MainActivity::class.java) { + this.activity = getActivity() as MainActivity? + } + } + + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + super.onCreateView(inflater, container, savedInstanceState) + if (savedInstanceState != null && savedInstanceState.containsKey("userId")) { + val userId = savedInstanceState.getString("userId") + if (userId != null) { + compositeSubscription.add(userRepository.getUser(userId).subscribe(Consumer { habitRPGUser -> user = habitRPGUser }, RxErrorHandler.handleEmptyError())) + } + } + + if (this.usesBottomNavigation) { + bottomNavigation?.removeOnTabSelectListener() + bottomNavigation?.removeOnTabReselectListener() + bottomNavigation?.visibility = View.VISIBLE + } else { + bottomNavigation?.visibility = View.GONE + } + + floatingMenuWrapper?.removeAllViews() + + setHasOptionsMenu(true) + + activity?.setActiveFragment(this) + + updateTabLayoutVisibility() + + return null + } + + private fun updateTabLayoutVisibility() { + if (this.usesTabLayout) { + tabLayout?.removeAllTabs() + tabLayout?.visibility = View.VISIBLE + tabLayout?.tabMode = TabLayout.MODE_FIXED + } else { + tabLayout?.visibility = View.GONE + } + } + + override fun onDestroy() { + userRepository.close() + super.onDestroy() + } + + override fun onSaveInstanceState(outState: Bundle) { + if (user?.isValid == true) { + outState.putString("userId", user?.id) + } + + super.onSaveInstanceState(outState) + } + + open fun customTitle(): String { + return "" + } + + fun hideToolbar() { + activity?.avatarWithBars?.visibility = View.GONE + } + + fun showToolbar() { + activity?.avatarWithBars?.visibility = View.VISIBLE + } + + fun disableToolbarScrolling() { + val params = collapsingToolbar?.layoutParams as AppBarLayout.LayoutParams? + params?.scrollFlags = 0 + } + + fun enableToolbarScrolling() { + val params = collapsingToolbar?.layoutParams as AppBarLayout.LayoutParams? + params?.scrollFlags = AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL or AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/GemsPurchaseFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/GemsPurchaseFragment.kt index 61700ed13..127cea523 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/GemsPurchaseFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/GemsPurchaseFragment.kt @@ -86,7 +86,7 @@ class GemsPurchaseFragment : BaseFragment(), GemPurchaseActivity.CheckoutFragmen this.listener = listener } - override fun setBillingRequests(billingRequests: BillingRequests) { + override fun setBillingRequests(billingRequests: BillingRequests?) { this.billingRequests = billingRequests } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/NavigationDrawerFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/NavigationDrawerFragment.kt index c04a5f73a..e3fe8efa2 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/NavigationDrawerFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/NavigationDrawerFragment.kt @@ -144,7 +144,7 @@ class NavigationDrawerFragment : DialogFragment() { NavigationDrawerAdapter(0, 0) } subscriptions = CompositeDisposable() - HabiticaBaseApplication.getComponent().inject(this) + HabiticaBaseApplication.component?.inject(this) super.onCreate(savedInstanceState) if (savedInstanceState != null) { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/StatsFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/StatsFragment.kt index 647ce1e41..2c5716338 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/StatsFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/StatsFragment.kt @@ -133,7 +133,7 @@ class StatsFragment: BaseMainFragment() { private fun showBulkAllocateDialog() { context.notNull { context -> - val dialog = BulkAllocateStatsDialog(context, HabiticaBaseApplication.getComponent()) + val dialog = BulkAllocateStatsDialog(context, HabiticaBaseApplication.component) dialog.show() } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/SubscriptionFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/SubscriptionFragment.kt index b315c497d..7f1a0e669 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/SubscriptionFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/SubscriptionFragment.kt @@ -196,7 +196,7 @@ class SubscriptionFragment : BaseFragment(), GemPurchaseActivity.CheckoutFragmen this.listener = listener } - override fun setBillingRequests(billingRequests: BillingRequests) { + override fun setBillingRequests(billingRequests: BillingRequests?) { this.billingRequests = billingRequests } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarCustomizationFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarCustomizationFragment.kt index 7244d0f1f..aa6beca3a 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarCustomizationFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/customization/AvatarCustomizationFragment.kt @@ -141,13 +141,13 @@ class AvatarCustomizationFragment : BaseMainFragment() { layoutManager.spanCount = spanCount } - override fun updateUserData(user: User) { + override fun updateUserData(user: User?) { super.updateUserData(user) - this.adapter.gemBalance = (user.balance * 4).toInt() + this.adapter.gemBalance = user?.gemCount ?: 0 this.updateActiveCustomization() if (adapter.customizationList.size != 0) { val ownedCustomizations = ArrayList() - user.purchased?.customizations?.filter { it.type == this.type }?.mapTo(ownedCustomizations) { it.id } + user?.purchased?.customizations?.filter { it.type == this.type }?.mapTo(ownedCustomizations) { it.id } adapter.updateOwnership(ownedCustomizations) } else { this.loadCustomizations() @@ -155,7 +155,7 @@ class AvatarCustomizationFragment : BaseMainFragment() { } private fun updateActiveCustomization() { - if (this.type == null || this.user == null || this.user!!.preferences == null) { + if (this.type == null || user?.preferences == null) { return } val prefs = this.user?.preferences diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemsFragment.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemsFragment.java deleted file mode 100644 index 6e030146b..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemsFragment.java +++ /dev/null @@ -1,139 +0,0 @@ -package com.habitrpg.android.habitica.ui.fragments.inventory.items; - -import android.os.Bundle; -import android.support.design.widget.TabLayout; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentPagerAdapter; -import android.support.v4.view.ViewPager; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.components.AppComponent; -import com.habitrpg.android.habitica.events.commands.HatchingCommand; -import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment; - -import org.greenrobot.eventbus.Subscribe; - -public class ItemsFragment extends BaseMainFragment { - - public ViewPager viewPager; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - this.usesTabLayout = true; - super.onCreateView(inflater, container, savedInstanceState); - View v = inflater.inflate(R.layout.fragment_viewpager, container, false); - - viewPager = (ViewPager) v.findViewById(R.id.viewPager); - - viewPager.setCurrentItem(0); - - setViewPagerAdapter(); - - return v; - } - - @Override - public void injectFragment(AppComponent component) { - component.inject(this); - } - - public void setViewPagerAdapter() { - android.support.v4.app.FragmentManager fragmentManager = getChildFragmentManager(); - - viewPager.setAdapter(new FragmentPagerAdapter(fragmentManager) { - - @Override - public Fragment getItem(int position) { - - ItemRecyclerFragment fragment = new ItemRecyclerFragment(); - - switch (position) { - case 0: { - fragment.setItemType("eggs"); - break; - } - case 1: { - fragment.setItemType("hatchingPotions"); - break; - } - case 2: { - fragment.setItemType("food"); - break; - } - case 3: { - fragment.setItemType("quests"); - break; - } - case 4: { - fragment.setItemType("special"); - } - } - fragment.setHatching(false); - fragment.setFeeding(false); - fragment.setItemTypeText(this.getPageTitle(position).toString()); - fragment.setUser(ItemsFragment.this.user); - - return fragment; - } - - @Override - public int getCount() { - return 5; - } - - @Override - public CharSequence getPageTitle(int position) { - if (activity == null) { - return ""; - } - switch (position) { - case 0: - return activity.getString(R.string.eggs); - case 1: - return activity.getString(R.string.hatching_potions); - case 2: - return activity.getString(R.string.food); - case 3: - return activity.getString(R.string.quests); - case 4: - return activity.getString(R.string.special); - } - return ""; - } - }); - if (tabLayout != null && viewPager != null) { - tabLayout.setupWithViewPager(viewPager); - tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE); - } - } - - @Subscribe - public void showHatchingDialog(HatchingCommand event) { - if (event.usingEgg == null || event.usingHatchingPotion == null) { - ItemRecyclerFragment fragment = new ItemRecyclerFragment(); - if (event.usingEgg != null) { - fragment.setItemType("hatchingPotions"); - fragment.setHatchingItem(event.usingEgg); - } else { - fragment.setItemType("eggs"); - fragment.setHatchingItem(event.usingHatchingPotion); - } - fragment.setHatching(true); - fragment.setFeeding(false); - fragment.show(getFragmentManager(), "hatchingDialog"); - } - } - - - @Override - public String customTitle() { - if (!isAdded()) { - return ""; - } - return getString(R.string.sidebar_items); - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemsFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemsFragment.kt new file mode 100644 index 000000000..f957af781 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/items/ItemsFragment.kt @@ -0,0 +1,118 @@ +package com.habitrpg.android.habitica.ui.fragments.inventory.items + +import android.os.Bundle +import android.support.design.widget.TabLayout +import android.support.v4.app.Fragment +import android.support.v4.app.FragmentPagerAdapter +import android.support.v4.view.ViewPager +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup + +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.components.AppComponent +import com.habitrpg.android.habitica.events.commands.HatchingCommand +import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment +import com.habitrpg.android.habitica.ui.helpers.bindView + +import org.greenrobot.eventbus.Subscribe + +class ItemsFragment : BaseMainFragment() { + + private val viewPager: ViewPager? by bindView(R.id.viewPager) + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + this.usesTabLayout = true + super.onCreateView(inflater, container, savedInstanceState) + return inflater.inflate(R.layout.fragment_viewpager, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + viewPager?.currentItem = 0 + setViewPagerAdapter() + } + + override fun injectFragment(component: AppComponent) { + component.inject(this) + } + + private fun setViewPagerAdapter() { + val fragmentManager = childFragmentManager + + viewPager?.adapter = object : FragmentPagerAdapter(fragmentManager) { + + override fun getItem(position: Int): Fragment { + + val fragment = ItemRecyclerFragment() + + when (position) { + 0 -> { + fragment.itemType = "eggs" + } + 1 -> { + fragment.itemType = "hatchingPotions" + } + 2 -> { + fragment.itemType = "food" + } + 3 -> { + fragment.itemType = "quests" + } + 4 -> { + fragment.itemType = "special" + } + } + fragment.isHatching = false + fragment.isFeeding = false + fragment.itemTypeText = this.getPageTitle(position).toString() + fragment.user = this@ItemsFragment.user + + return fragment + } + + override fun getCount(): Int { + return 5 + } + + override fun getPageTitle(position: Int): CharSequence { + return when (position) { + 0 -> activity?.getString(R.string.eggs) + 1 -> activity?.getString(R.string.hatching_potions) + 2 -> activity?.getString(R.string.food) + 3 -> activity?.getString(R.string.quests) + 4 -> activity?.getString(R.string.special) + else -> "" + } ?: "" + } + } + tabLayout?.setupWithViewPager(viewPager) + tabLayout?.tabMode = TabLayout.MODE_SCROLLABLE + } + + @Subscribe + fun showHatchingDialog(event: HatchingCommand) { + if (event.usingEgg == null || event.usingHatchingPotion == null) { + val fragment = ItemRecyclerFragment() + if (event.usingEgg != null) { + fragment.itemType = "hatchingPotions" + fragment.hatchingItem = event.usingEgg + } else { + fragment.itemType = "eggs" + fragment.hatchingItem = event.usingHatchingPotion + } + fragment.isHatching = true + fragment.isFeeding = false + fragment.show(fragmentManager!!, "hatchingDialog") + } + } + + + override fun customTitle(): String { + return if (!isAdded) { + "" + } else getString(R.string.sidebar_items) + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopsFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopsFragment.kt index 6e914983c..eafb41fd1 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopsFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/shops/ShopsFragment.kt @@ -87,8 +87,8 @@ class ShopsFragment : BaseMainFragment() { } } - if (tabLayout != null && viewPager != null) { - tabLayout!!.setupWithViewPager(viewPager) + if (viewPager != null) { + tabLayout?.setupWithViewPager(viewPager) } } @@ -99,7 +99,7 @@ class ShopsFragment : BaseMainFragment() { } else getString(R.string.sidebar_shops) } - override fun updateUserData(user: User) { + override fun updateUserData(user: User?) { super.updateUserData(user) updateCurrencyView() } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/MountDetailRecyclerFragment.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/MountDetailRecyclerFragment.java deleted file mode 100644 index e0b87175e..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/MountDetailRecyclerFragment.java +++ /dev/null @@ -1,121 +0,0 @@ -package com.habitrpg.android.habitica.ui.fragments.inventory.stable; - -import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v7.widget.GridLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.components.AppComponent; -import com.habitrpg.android.habitica.data.InventoryRepository; -import com.habitrpg.android.habitica.helpers.RxErrorHandler; -import com.habitrpg.android.habitica.ui.adapter.inventory.MountDetailRecyclerAdapter; -import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment; -import com.habitrpg.android.habitica.ui.helpers.MarginDecoration; -import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator; - -import javax.inject.Inject; - -public class MountDetailRecyclerFragment extends BaseMainFragment { - private static final String ANIMAL_TYPE_KEY = "ANIMAL_TYPE_KEY"; - - @Inject - InventoryRepository inventoryRepository; - - public RecyclerView recyclerView; - public MountDetailRecyclerAdapter adapter; - public String animalType; - public String animalGroup; - GridLayoutManager layoutManager = null; - - private View view; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - this.usesTabLayout = false; - super.onCreateView(inflater, container, savedInstanceState); - view = inflater.inflate(R.layout.fragment_recyclerview, container, false); - - recyclerView = (RecyclerView) view.findViewById(R.id.recyclerView); - - layoutManager = new GridLayoutManager(getActivity(), 2); - recyclerView.setLayoutManager(layoutManager); - recyclerView.addItemDecoration(new MarginDecoration(getActivity())); - - adapter = (MountDetailRecyclerAdapter) recyclerView.getAdapter(); - if (adapter == null) { - adapter = new MountDetailRecyclerAdapter(null, true); - adapter.setItemType(this.animalType); - recyclerView.setAdapter(adapter); - recyclerView.setItemAnimator(new SafeDefaultItemAnimator()); - this.loadItems(); - - getCompositeSubscription().add(adapter.getEquipFlowable() - .flatMap(key -> inventoryRepository.equip(user, "mount", key)) - .subscribe(items -> {}, RxErrorHandler.handleEmptyError())); - } - - if (savedInstanceState != null) { - this.animalType = savedInstanceState.getString(ANIMAL_TYPE_KEY, ""); - } - - return view; - } - - @Override - public void onDestroy() { - inventoryRepository.close(); - super.onDestroy(); - } - - @Override - public void injectFragment(AppComponent component) { - component.inject(this); - } - - @Override - public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - final View finalView = view; - finalView.post(() -> setGridSpanCount(finalView.getWidth())); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putString(ANIMAL_TYPE_KEY, this.animalType); - } - - - private void setGridSpanCount(int width) { - int spanCount = 0; - if (getContext() != null && getContext().getResources() != null) { - float itemWidth; - itemWidth = getContext().getResources().getDimension(R.dimen.pet_width); - - spanCount = (int) (width / itemWidth); - } - if (spanCount == 0) { - spanCount = 1; - } - layoutManager.setSpanCount(spanCount); - layoutManager.requestLayout(); - } - - private void loadItems() { - if (animalType != null && animalGroup != null) { - inventoryRepository.getMounts(animalType, animalGroup).firstElement().subscribe(adapter::updateData, RxErrorHandler.handleEmptyError()); - } - } - - @Override - public String customTitle() { - if (!isAdded()) { - return ""; - } - return getString(R.string.mounts); - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/MountDetailRecyclerFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/MountDetailRecyclerFragment.kt new file mode 100644 index 000000000..f4118da50 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/MountDetailRecyclerFragment.kt @@ -0,0 +1,110 @@ +package com.habitrpg.android.habitica.ui.fragments.inventory.stable + +import android.os.Bundle +import android.support.v7.widget.GridLayoutManager +import android.support.v7.widget.RecyclerView +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup + +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.components.AppComponent +import com.habitrpg.android.habitica.data.InventoryRepository +import com.habitrpg.android.habitica.extensions.notNull +import com.habitrpg.android.habitica.helpers.RxErrorHandler +import com.habitrpg.android.habitica.ui.adapter.inventory.MountDetailRecyclerAdapter +import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment +import com.habitrpg.android.habitica.ui.helpers.MarginDecoration +import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator +import com.habitrpg.android.habitica.ui.helpers.bindView +import io.reactivex.functions.Consumer + +import javax.inject.Inject + +class MountDetailRecyclerFragment : BaseMainFragment() { + + @Inject + internal lateinit var inventoryRepository: InventoryRepository + + private val recyclerView: RecyclerView by bindView(R.id.recyclerView) + var adapter: MountDetailRecyclerAdapter? = null + var animalType: String? = null + var animalGroup: String? = null + internal var layoutManager: GridLayoutManager? = null + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + this.usesTabLayout = false + super.onCreateView(inflater, container, savedInstanceState) + return inflater.inflate(R.layout.fragment_recyclerview, container, false) + } + + override fun onDestroy() { + inventoryRepository.close() + super.onDestroy() + } + + override fun injectFragment(component: AppComponent) { + component.inject(this) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + layoutManager = GridLayoutManager(activity, 2) + recyclerView.layoutManager = layoutManager + recyclerView.addItemDecoration(MarginDecoration(activity)) + + adapter = recyclerView.adapter as MountDetailRecyclerAdapter? + if (adapter == null) { + adapter = MountDetailRecyclerAdapter(null, true) + adapter?.itemType = this.animalType + recyclerView.adapter = adapter + recyclerView.itemAnimator = SafeDefaultItemAnimator() + this.loadItems() + + adapter?.getEquipFlowable()?.flatMap { key -> inventoryRepository.equip(user, "mount", key) } + ?.subscribe(Consumer { }, RxErrorHandler.handleEmptyError()).notNull { compositeSubscription.add(it) } + } + + if (savedInstanceState != null) { + this.animalType = savedInstanceState.getString(ANIMAL_TYPE_KEY, "") + } + + view.post { setGridSpanCount(view.width) } + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putString(ANIMAL_TYPE_KEY, this.animalType) + } + + + private fun setGridSpanCount(width: Int) { + var spanCount = 0 + if (context != null && context!!.resources != null) { + val itemWidth: Float = context!!.resources.getDimension(R.dimen.pet_width) + + spanCount = (width / itemWidth).toInt() + } + if (spanCount == 0) { + spanCount = 1 + } + layoutManager?.spanCount = spanCount + layoutManager?.requestLayout() + } + + private fun loadItems() { + if (animalType != null && animalGroup != null) { + inventoryRepository.getMounts(animalType!!, animalGroup!!).firstElement().subscribe(Consumer { adapter?.updateData(it) }, RxErrorHandler.handleEmptyError()) + } + } + + override fun customTitle(): String { + return if (!isAdded) { + "" + } else getString(R.string.mounts) + } + + companion object { + private val ANIMAL_TYPE_KEY = "ANIMAL_TYPE_KEY" + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/StableFragment.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/StableFragment.java deleted file mode 100644 index 506a9a7f1..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/StableFragment.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.habitrpg.android.habitica.ui.fragments.inventory.stable; - -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentPagerAdapter; -import android.support.v4.view.ViewPager; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.components.AppComponent; -import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment; - -public class StableFragment extends BaseMainFragment { - - public ViewPager viewPager; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - this.usesTabLayout = true; - super.onCreateView(inflater, container, savedInstanceState); - View v = inflater.inflate(R.layout.fragment_viewpager, container, false); - - viewPager = (ViewPager) v.findViewById(R.id.viewPager); - - viewPager.setCurrentItem(0); - - setViewPagerAdapter(); - - return v; - } - - @Override - public void injectFragment(AppComponent component) { - component.inject(this); - } - - public void setViewPagerAdapter() { - android.support.v4.app.FragmentManager fragmentManager = getChildFragmentManager(); - - viewPager.setAdapter(new FragmentPagerAdapter(fragmentManager) { - - @Override - public Fragment getItem(int position) { - - StableRecyclerFragment fragment = new StableRecyclerFragment(); - - switch (position) { - case 0: { - fragment.setItemType("pets"); - break; - } - case 1: { - fragment.setItemType("mounts"); - break; - } - } - fragment.setUser(StableFragment.this.user); - fragment.setItemTypeText(this.getPageTitle(position).toString()); - - return fragment; - } - - @Override - public int getCount() { - return 2; - } - - @Override - public CharSequence getPageTitle(int position) { - switch (position) { - case 0: - return activity.getString(R.string.pets); - case 1: - return activity.getString(R.string.mounts); - } - return ""; - } - }); - - if (tabLayout != null && viewPager != null) { - tabLayout.setupWithViewPager(viewPager); - } - } - - - @Override - public String customTitle() { - if (!isAdded()) { - return ""; - } - return getString(R.string.sidebar_stable); - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/StableFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/StableFragment.kt new file mode 100644 index 000000000..35c690f6f --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/inventory/stable/StableFragment.kt @@ -0,0 +1,82 @@ +package com.habitrpg.android.habitica.ui.fragments.inventory.stable + +import android.os.Bundle +import android.support.v4.app.Fragment +import android.support.v4.app.FragmentPagerAdapter +import android.support.v4.view.ViewPager +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup + +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.components.AppComponent +import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment +import com.habitrpg.android.habitica.ui.helpers.bindView + +class StableFragment : BaseMainFragment() { + + private val viewPager: ViewPager? by bindView(R.id.viewPager) + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + this.usesTabLayout = true + super.onCreateView(inflater, container, savedInstanceState) + return inflater.inflate(R.layout.fragment_viewpager, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + viewPager?.currentItem = 0 + + setViewPagerAdapter() + } + + override fun injectFragment(component: AppComponent) { + component.inject(this) + } + + private fun setViewPagerAdapter() { + val fragmentManager = childFragmentManager + + viewPager?.adapter = object : FragmentPagerAdapter(fragmentManager) { + + override fun getItem(position: Int): Fragment { + + val fragment = StableRecyclerFragment() + + when (position) { + 0 -> { + fragment.itemType = "pets" + } + 1 -> { + fragment.itemType = "mounts" + } + } + fragment.user = this@StableFragment.user + fragment.itemTypeText = this.getPageTitle(position).toString() + + return fragment + } + + override fun getCount(): Int { + return 2 + } + + override fun getPageTitle(position: Int): CharSequence { + return when (position) { + 0 -> activity?.getString(R.string.pets) + 1 -> activity?.getString(R.string.mounts) + else -> "" + } ?: "" + } + } + tabLayout?.setupWithViewPager(viewPager) + } + + + override fun customTitle(): String { + return if (!isAdded) { + "" + } else getString(R.string.sidebar_stable) + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/APIPreferenceFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/APIPreferenceFragment.kt index 3f5450c16..30ddeac4f 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/APIPreferenceFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/APIPreferenceFragment.kt @@ -19,7 +19,7 @@ class APIPreferenceFragment: BasePreferencesFragment() { override fun onCreate(savedInstanceState: Bundle?) { - HabiticaBaseApplication.getComponent().inject(this) + HabiticaBaseApplication.component?.inject(this) qrCodeManager = QrCodeManager(userRepository, this.context) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/AuthenticationPreferenceFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/AuthenticationPreferenceFragment.kt index 6395df324..17f2d4ccb 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/AuthenticationPreferenceFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/AuthenticationPreferenceFragment.kt @@ -35,7 +35,7 @@ class AuthenticationPreferenceFragment: BasePreferencesFragment() { override fun onCreate(savedInstanceState: Bundle?) { - HabiticaBaseApplication.getComponent().inject(this) + HabiticaBaseApplication.component?.inject(this) super.onCreate(savedInstanceState) } @@ -155,7 +155,7 @@ class AuthenticationPreferenceFragment: BasePreferencesFragment() { private fun deleteAccount(password: String) { val dialog = ProgressDialog.show(context, context?.getString(R.string.deleting_account), null, true) userRepository.deleteAccount(password).subscribe({ _ -> - HabiticaApplication.logout(context) + context.notNull { HabiticaBaseApplication.logout(it) } activity?.finish() }) { throwable -> dialog.dismiss() diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PreferencesFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PreferencesFragment.kt index 9bd13b18b..2a920188e 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PreferencesFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PreferencesFragment.kt @@ -44,7 +44,7 @@ class PreferencesFragment : BasePreferencesFragment(), SharedPreferences.OnShare private var classSelectionPreference: Preference? = null override fun onCreate(savedInstanceState: Bundle?) { - HabiticaBaseApplication.getComponent().inject(this) + HabiticaBaseApplication.component?.inject(this) super.onCreate(savedInstanceState) val userID = preferenceManager.sharedPreferences.getString(context?.getString(R.string.SP_userID), null) @@ -82,7 +82,7 @@ class PreferencesFragment : BasePreferencesFragment(), SharedPreferences.OnShare override fun onPreferenceTreeClick(preference: Preference): Boolean { when(preference.key) { "logout" -> { - HabiticaApplication.logout(context) + context.notNull { HabiticaBaseApplication.logout(it) } activity?.finish() } "choose_class" -> { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/ProfilePreferencesFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/ProfilePreferencesFragment.kt index fbd5636c1..1b9aa3577 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/ProfilePreferencesFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/ProfilePreferencesFragment.kt @@ -20,7 +20,7 @@ class ProfilePreferencesFragment: BasePreferencesFragment(), SharedPreferences.O } override fun onCreate(savedInstanceState: Bundle?) { - HabiticaBaseApplication.getComponent().inject(this) + HabiticaBaseApplication.component?.inject(this) super.onCreate(savedInstanceState) } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PushNotificationsPreferencesFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PushNotificationsPreferencesFragment.kt index 9bc39ae14..43d342ec0 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PushNotificationsPreferencesFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PushNotificationsPreferencesFragment.kt @@ -7,7 +7,7 @@ import com.habitrpg.android.habitica.HabiticaBaseApplication class PushNotificationsPreferencesFragment : BasePreferencesFragment(), SharedPreferences.OnSharedPreferenceChangeListener { override fun onCreate(savedInstanceState: Bundle?) { - HabiticaBaseApplication.getComponent().inject(this) + HabiticaBaseApplication.component?.inject(this) super.onCreate(savedInstanceState) } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/skills/SkillsFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/skills/SkillsFragment.kt index f844aa5a5..753837a15 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/skills/SkillsFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/skills/SkillsFragment.kt @@ -42,6 +42,12 @@ class SkillsFragment : BaseMainFragment() { private var selectedSkill: Skill? = null private var progressDialog: ProgressDialog? = null + override var user: User? = null + set(value) { + field = value + checkUserLoadSkills() + } + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { super.onCreateView(inflater, container, savedInstanceState) adapter = SkillsRecyclerViewAdapter() @@ -81,12 +87,6 @@ class SkillsFragment : BaseMainFragment() { } } - override fun setUser(user: User?) { - super.setUser(user) - - checkUserLoadSkills() - } - @Subscribe fun onEvent(command: UseSkillCommand) { val skill = command.skill @@ -111,9 +111,9 @@ class SkillsFragment : BaseMainFragment() { adapter?.mana = response.user.stats.mp val activity = activity ?: return if ("special" == usedSkill?.habitClass) { - showSnackbar(activity.getFloatingMenuWrapper(), context!!.getString(R.string.used_skill_without_mana, usedSkill.text), HabiticaSnackbar.SnackbarDisplayType.BLUE) + showSnackbar(activity.floatingMenuWrapper, context!!.getString(R.string.used_skill_without_mana, usedSkill.text), HabiticaSnackbar.SnackbarDisplayType.BLUE) } else { - showSnackbar(activity.getFloatingMenuWrapper(), null, + showSnackbar(activity.floatingMenuWrapper, null, context?.getString(R.string.used_skill_without_mana, usedSkill?.text), BitmapDrawable(resources, HabiticaIconsHelper.imageOfMagic()), ContextCompat.getColor(context!!, R.color.blue_10), "-" + usedSkill?.mana, diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChatListFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChatListFragment.kt index a082b6d65..681ec6745 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChatListFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/ChatListFragment.kt @@ -105,7 +105,9 @@ class ChatListFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener { chatAdapter = ChatRecyclerViewAdapter(null, true, user, true) chatAdapter.notNull { - compositeSubscription.add(it.getUserLabelClickFlowable().subscribe(Consumer { userId -> FullProfileActivity.open(context, userId) }, RxErrorHandler.handleEmptyError())) + compositeSubscription.add(it.getUserLabelClickFlowable().subscribe(Consumer { userId -> + context.notNull { FullProfileActivity.open(it, userId) } + }, RxErrorHandler.handleEmptyError())) compositeSubscription.add(it.getDeleteMessageFlowable().subscribe(Consumer { this.showDeleteConfirmationDialog(it) }, RxErrorHandler.handleEmptyError())) compositeSubscription.add(it.getFlagMessageClickFlowable().subscribe(Consumer { this.showFlagConfirmationDialog(it) }, RxErrorHandler.handleEmptyError())) compositeSubscription.add(it.getCopyMessageAsTodoFlowable().subscribe(Consumer{ this.copyMessageAsTodo(it) }, RxErrorHandler.handleEmptyError())) @@ -113,7 +115,6 @@ class ChatListFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener { compositeSubscription.add(it.getLikeMessageFlowable().flatMap { socialRepository.likeMessage(it) }.subscribe(Consumer { }, RxErrorHandler.handleEmptyError())) } - chatBarView.sendAction = { sendChatMessage(it) } recyclerView.adapter = chatAdapter @@ -160,7 +161,7 @@ class ChatListFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener { clipMan.primaryClip = messageText val activity = activity as MainActivity? if (activity != null) { - showSnackbar(activity.getFloatingMenuWrapper(), getString(R.string.chat_message_copied), SnackbarDisplayType.NORMAL) + showSnackbar(activity.floatingMenuWrapper, getString(R.string.chat_message_copied), SnackbarDisplayType.NORMAL) } } @@ -173,7 +174,9 @@ class ChatListFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener { socialRepository.flagMessage(chatMessage) .subscribe(Consumer { val activity = activity as MainActivity? - showSnackbar(activity!!.getFloatingMenuWrapper(), "Flagged message by " + chatMessage.user, SnackbarDisplayType.NORMAL) + activity?.floatingMenuWrapper.notNull { + showSnackbar(it, "Flagged message by " + chatMessage.user, SnackbarDisplayType.NORMAL) + } }, RxErrorHandler.handleEmptyError()) } .setNegativeButton(R.string.action_cancel) { _, _ -> } @@ -199,7 +202,7 @@ class ChatListFragment : BaseFragment(), SwipeRefreshLayout.OnRefreshListener { clipboard.primaryClip = clip val activity = activity as MainActivity? if (activity != null) { - HabiticaSnackbar.showSnackbar(activity.getFloatingMenuWrapper(), getString(R.string.chat_message_copied), HabiticaSnackbar.SnackbarDisplayType.NORMAL) + HabiticaSnackbar.showSnackbar(activity.floatingMenuWrapper, getString(R.string.chat_message_copied), HabiticaSnackbar.SnackbarDisplayType.NORMAL) } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GroupInformationFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GroupInformationFragment.kt index f5f81f686..b17a1b09d 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GroupInformationFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GroupInformationFragment.kt @@ -87,7 +87,7 @@ class GroupInformationFragment : BaseFragment() { clipboard.primaryClip = clip val activity = activity as MainActivity? if (activity != null) { - HabiticaSnackbar.showSnackbar(activity.getFloatingMenuWrapper(), getString(R.string.id_copied), HabiticaSnackbar.SnackbarDisplayType.NORMAL) + HabiticaSnackbar.showSnackbar(activity.floatingMenuWrapper, getString(R.string.id_copied), HabiticaSnackbar.SnackbarDisplayType.NORMAL) } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GuildFragment.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GuildFragment.java deleted file mode 100644 index 246d5f1ac..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GuildFragment.java +++ /dev/null @@ -1,260 +0,0 @@ -package com.habitrpg.android.habitica.ui.fragments.social; - -import android.app.Activity; -import android.content.Intent; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentPagerAdapter; -import android.support.v4.view.ViewPager; -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 com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.components.AppComponent; -import com.habitrpg.android.habitica.data.SocialRepository; -import com.habitrpg.android.habitica.helpers.RxErrorHandler; -import com.habitrpg.android.habitica.models.social.Group; -import com.habitrpg.android.habitica.ui.activities.GroupFormActivity; -import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment; - -import javax.inject.Inject; - -public class GuildFragment extends BaseMainFragment { - - @Inject - SocialRepository socialRepository; - - public boolean isMember; - public ViewPager viewPager; - private Group guild; - private GroupInformationFragment guildInformationFragment; - private ChatListFragment chatListFragment; - private String guildId; - - public void setGuildId(String guildId) { - this.guildId = guildId; - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - this.usesTabLayout = true; - hideToolbar(); - disableToolbarScrolling(); - super.onCreateView(inflater, container, savedInstanceState); - View v = inflater.inflate(R.layout.fragment_viewpager, container, false); - - viewPager = (ViewPager) v.findViewById(R.id.viewPager); - - viewPager.setCurrentItem(0); - - setViewPagerAdapter(); - - if (guildId != null && this.socialRepository != null) { - getCompositeSubscription().add(socialRepository.getGroup(this.guildId).subscribe(this::setGroup, RxErrorHandler.handleEmptyError())); - socialRepository.retrieveGroup(this.guildId).subscribe(group -> {}, RxErrorHandler.handleEmptyError()); - } - - return v; - } - - @Override - public void onDestroyView() { - showToolbar(); - enableToolbarScrolling(); - super.onDestroyView(); - } - - @Override - public void onDestroy() { - socialRepository.close(); - super.onDestroy(); - } - - @Override - public void injectFragment(AppComponent component) { - component.inject(this); - } - - @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - if (this.socialRepository != null && this.guild != null) { - socialRepository.retrieveGroup(this.guild.getId()).subscribe(this::setGroup, RxErrorHandler.handleEmptyError()); - } - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - if (this.activity != null && this.guild != null) { - if (this.isMember) { - if (this.user != null && this.user.getId().equals(this.guild.getLeaderID())) { - this.activity.getMenuInflater().inflate(R.menu.guild_admin, menu); - } else { - this.activity.getMenuInflater().inflate(R.menu.guild_member, menu); - } - } else { - this.activity.getMenuInflater().inflate(R.menu.guild_nonmember, menu); - } - } - super.onCreateOptionsMenu(menu, inflater); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - int id = item.getItemId(); - - switch (id) { - case R.id.menu_guild_join: - this.socialRepository.joinGroup(this.guild.getId()).subscribe(this::setGroup, RxErrorHandler.handleEmptyError()); - this.isMember = true; - return true; - case R.id.menu_guild_leave: - this.socialRepository.leaveGroup(this.guild.getId()) - .subscribe(aVoid -> { - if (this.activity != null) { - this.activity.supportInvalidateOptionsMenu(); - } - }, RxErrorHandler.handleEmptyError()); - this.isMember = false; - return true; - case R.id.menu_guild_edit: - this.displayEditForm(); - return true; - } - return super.onOptionsItemSelected(item); - } - - public void setViewPagerAdapter() { - android.support.v4.app.FragmentManager fragmentManager = getChildFragmentManager(); - - viewPager.setAdapter(new FragmentPagerAdapter(fragmentManager) { - - @Override - public Fragment getItem(int position) { - - Fragment fragment; - - switch (position) { - case 0: { - fragment = guildInformationFragment = GroupInformationFragment.Companion.newInstance(GuildFragment.this.guild, user); - break; - } - case 1: { - chatListFragment = new ChatListFragment(); - chatListFragment.configure(GuildFragment.this.guildId, user, false); - fragment = chatListFragment; - break; - } - default: - fragment = new Fragment(); - } - - return fragment; - } - - @Override - public int getCount() { - return 2; - } - - @Override - public CharSequence getPageTitle(int position) { - switch (position) { - case 0: - return getContext().getString(R.string.guild); - case 1: - return getContext().getString(R.string.chat); - } - return ""; - } - }); - - viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - if (position == 1 && GuildFragment.this.guild != null) { - chatListFragment.setNavigatedToFragment(GuildFragment.this.guild.getId()); - } - } - - @Override - public void onPageSelected(int position) { - if (position == 1 && GuildFragment.this.guild != null && chatListFragment != null) { - chatListFragment.setNavigatedToFragment(GuildFragment.this.guild.getId()); - } - } - - @Override - public void onPageScrollStateChanged(int state) { - - } - }); - - if (tabLayout != null) { - tabLayout.setupWithViewPager(viewPager); - } - } - - private void displayEditForm() { - Bundle bundle = new Bundle(); - bundle.putString("groupID", this.guild.getId()); - bundle.putString("name", this.guild.getName()); - bundle.putString("description", this.guild.getDescription()); - bundle.putString("privacy", this.guild.getPrivacy()); - bundle.putString("leader", this.guild.getLeaderID()); - - Intent intent = new Intent(activity, GroupFormActivity.class); - intent.putExtras(bundle); - intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - startActivityForResult(intent, GroupFormActivity.GROUP_FORM_ACTIVITY); - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - switch (requestCode) { - case (GroupFormActivity.GROUP_FORM_ACTIVITY): { - if (resultCode == Activity.RESULT_OK) { - Bundle bundle = data.getExtras(); - this.socialRepository.updateGroup(this.guild, - bundle.getString("name"), - bundle.getString("description"), - bundle.getString("leader"), - bundle.getString("privacy")) - .subscribe(aVoid -> {}, RxErrorHandler.handleEmptyError()); - } - break; - } - } - } - - public void setGroup(Group group) { - if (group != null) { - if (this.guildInformationFragment != null) { - this.guildInformationFragment.setGroup(group); - } - - if (this.chatListFragment != null) { - this.chatListFragment.setSeenGroupId(group.getId()); - } - - this.guild = group; - } - if (this.activity != null) { - this.activity.supportInvalidateOptionsMenu(); - } - } - - @Override - public String customTitle() { - if (!isAdded()) { - return ""; - } - return getString(R.string.guild); - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GuildFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GuildFragment.kt new file mode 100644 index 000000000..2849cb5c9 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GuildFragment.kt @@ -0,0 +1,229 @@ +package com.habitrpg.android.habitica.ui.fragments.social + +import android.app.Activity +import android.content.Intent +import android.os.Bundle +import android.support.v4.app.Fragment +import android.support.v4.app.FragmentPagerAdapter +import android.support.v4.view.ViewPager +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 com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.components.AppComponent +import com.habitrpg.android.habitica.data.SocialRepository +import com.habitrpg.android.habitica.extensions.notNull +import com.habitrpg.android.habitica.helpers.RxErrorHandler +import com.habitrpg.android.habitica.models.social.Group +import com.habitrpg.android.habitica.ui.activities.GroupFormActivity +import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment +import com.habitrpg.android.habitica.ui.helpers.bindView +import io.reactivex.functions.Consumer + +import javax.inject.Inject + +class GuildFragment : BaseMainFragment() { + + @Inject + internal lateinit var socialRepository: SocialRepository + + var isMember: Boolean = false + private val viewPager: ViewPager? by bindView(R.id.viewPager) + private var guild: Group? = null + private var guildInformationFragment: GroupInformationFragment? = null + private var chatListFragment: ChatListFragment? = null + private var guildId: String? = null + + fun setGuildId(guildId: String) { + this.guildId = guildId + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + this.usesTabLayout = true + hideToolbar() + disableToolbarScrolling() + super.onCreateView(inflater, container, savedInstanceState) + return inflater.inflate(R.layout.fragment_viewpager, container, false) + } + + override fun onDestroyView() { + showToolbar() + enableToolbarScrolling() + super.onDestroyView() + } + + override fun onDestroy() { + socialRepository.close() + super.onDestroy() + } + + override fun injectFragment(component: AppComponent) { + component.inject(this) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + viewPager?.currentItem = 0 + + setViewPagerAdapter() + + guildId.notNull { guildId -> + compositeSubscription.add(socialRepository.getGroup(guildId).subscribe(Consumer { this.setGroup(it) }, RxErrorHandler.handleEmptyError())) + socialRepository.retrieveGroup(guildId).subscribe(Consumer { }, RxErrorHandler.handleEmptyError()) + socialRepository.getGroup(guildId).subscribe(Consumer { this.setGroup(it) }, RxErrorHandler.handleEmptyError()) + } + } + + override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { + if (this.activity != null && this.guild != null) { + if (this.isMember) { + if (this.user != null && this.user!!.id == this.guild!!.leaderID) { + this.activity!!.menuInflater.inflate(R.menu.guild_admin, menu) + } else { + this.activity!!.menuInflater.inflate(R.menu.guild_member, menu) + } + } else { + this.activity!!.menuInflater.inflate(R.menu.guild_nonmember, menu) + } + } + super.onCreateOptionsMenu(menu, inflater) + } + + override fun onOptionsItemSelected(item: MenuItem?): Boolean { + val id = item!!.itemId + + when (id) { + R.id.menu_guild_join -> { + this.socialRepository.joinGroup(this.guild?.id).subscribe(Consumer { this.setGroup(it) }, RxErrorHandler.handleEmptyError()) + this.isMember = true + return true + } + R.id.menu_guild_leave -> { + this.socialRepository.leaveGroup(this.guild?.id) + .subscribe(Consumer { + this.activity?.supportInvalidateOptionsMenu() + }, RxErrorHandler.handleEmptyError()) + this.isMember = false + return true + } + R.id.menu_guild_edit -> { + this.displayEditForm() + return true + } + } + return super.onOptionsItemSelected(item) + } + + private fun setViewPagerAdapter() { + val fragmentManager = childFragmentManager + + viewPager?.adapter = object : FragmentPagerAdapter(fragmentManager) { + + override fun getItem(position: Int): Fragment? { + + val fragment: Fragment? + + when (position) { + 0 -> { + guildInformationFragment = GroupInformationFragment.newInstance(this@GuildFragment.guild, user) + fragment = guildInformationFragment + } + 1 -> { + chatListFragment = ChatListFragment() + chatListFragment!!.configure(this@GuildFragment.guildId!!, user, false) + fragment = chatListFragment + } + else -> fragment = Fragment() + } + + return fragment + } + + override fun getCount(): Int { + return 2 + } + + override fun getPageTitle(position: Int): CharSequence? { + when (position) { + 0 -> return context!!.getString(R.string.guild) + 1 -> return context!!.getString(R.string.chat) + } + return "" + } + } + + viewPager?.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { + override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { + if (position == 1 && this@GuildFragment.guild != null) { + chatListFragment!!.setNavigatedToFragment(this@GuildFragment.guild!!.id) + } + } + + override fun onPageSelected(position: Int) { + if (position == 1 && this@GuildFragment.guild != null && chatListFragment != null) { + chatListFragment!!.setNavigatedToFragment(this@GuildFragment.guild!!.id) + } + } + + override fun onPageScrollStateChanged(state: Int) { + + } + }) + + tabLayout?.setupWithViewPager(viewPager) + } + + private fun displayEditForm() { + val bundle = Bundle() + bundle.putString("groupID", this.guild!!.id) + bundle.putString("name", this.guild!!.name) + bundle.putString("description", this.guild!!.description) + bundle.putString("privacy", this.guild!!.privacy) + bundle.putString("leader", this.guild!!.leaderID) + + val intent = Intent(activity, GroupFormActivity::class.java) + intent.putExtras(bundle) + intent.flags = Intent.FLAG_ACTIVITY_REORDER_TO_FRONT + startActivityForResult(intent, GroupFormActivity.GROUP_FORM_ACTIVITY) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + when (requestCode) { + GroupFormActivity.GROUP_FORM_ACTIVITY -> { + if (resultCode == Activity.RESULT_OK) { + val bundle = data!!.extras + this.socialRepository.updateGroup(this.guild, + bundle!!.getString("name"), + bundle.getString("description"), + bundle.getString("leader"), + bundle.getString("privacy")) + .subscribe(Consumer { }, RxErrorHandler.handleEmptyError()) + } + } + } + } + + private fun setGroup(group: Group?) { + if (group != null) { + guildInformationFragment?.setGroup(group) + + this.chatListFragment?.seenGroupId = group.id + + this.guild = group + } + this.activity?.supportInvalidateOptionsMenu() + } + + override fun customTitle(): String { + return if (!isAdded) { + "" + } else getString(R.string.guild) + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GuildsOverviewFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GuildsOverviewFragment.kt index 814fd48b8..1c54b92bb 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GuildsOverviewFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/GuildsOverviewFragment.kt @@ -107,8 +107,9 @@ class GuildsOverviewFragment : BaseMainFragment(), View.OnClickListener, SwipeRe fragment = publicGuildsFragment } else { val guildIndex = (v.parent as ViewGroup).indexOfChild(v) + val guildId = this.guilds?.get(guildIndex)?.id ?: return val guildFragment = GuildFragment() - guildFragment.setGuildId(this.guilds?.get(guildIndex)?.id) + guildFragment.setGuildId(guildId) guildFragment.isMember = true fragment = guildFragment } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/InboxMessageListFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/InboxMessageListFragment.kt index b3dfe08c1..db6e894db 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/InboxMessageListFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/InboxMessageListFragment.kt @@ -61,7 +61,9 @@ class InboxMessageListFragment : BaseMainFragment(), SwipeRefreshLayout.OnRefres recyclerView.adapter = chatAdapter recyclerView.itemAnimator = SafeDefaultItemAnimator() chatAdapter.notNull { - compositeSubscription.add(it.getUserLabelClickFlowable().subscribe(Consumer { FullProfileActivity.open(context, it) }, RxErrorHandler.handleEmptyError())) + compositeSubscription.add(it.getUserLabelClickFlowable().subscribe(Consumer { + context.notNull { context -> FullProfileActivity.open(context, it) } + }, RxErrorHandler.handleEmptyError())) compositeSubscription.add(it.getDeleteMessageFlowable().subscribe(Consumer { this.showDeleteConfirmationDialog(it) }, RxErrorHandler.handleEmptyError())) compositeSubscription.add(it.getFlagMessageClickFlowable().subscribe(Consumer { this.showFlagConfirmationDialog(it) }, RxErrorHandler.handleEmptyError())) compositeSubscription.add(it.getCopyMessageAsTodoFlowable().subscribe(Consumer { this.copyMessageAsTodo(it) }, RxErrorHandler.handleEmptyError())) @@ -140,7 +142,7 @@ class InboxMessageListFragment : BaseMainFragment(), SwipeRefreshLayout.OnRefres clipMan.primaryClip = messageText val activity = getActivity() as MainActivity? if (activity != null) { - showSnackbar(activity.getFloatingMenuWrapper(), getString(R.string.chat_message_copied), HabiticaSnackbar.SnackbarDisplayType.NORMAL) + showSnackbar(activity.floatingMenuWrapper, getString(R.string.chat_message_copied), HabiticaSnackbar.SnackbarDisplayType.NORMAL) } } @@ -151,7 +153,9 @@ class InboxMessageListFragment : BaseMainFragment(), SwipeRefreshLayout.OnRefres socialRepository.flagMessage(chatMessage) .subscribe(Consumer { val activity = getActivity() as MainActivity? - showSnackbar(activity!!.getFloatingMenuWrapper(), "Flagged message by " + chatMessage.user, HabiticaSnackbar.SnackbarDisplayType.NORMAL) + activity?.floatingMenuWrapper.notNull { + showSnackbar(it, "Flagged message by " + chatMessage.user, HabiticaSnackbar.SnackbarDisplayType.NORMAL) + } }, RxErrorHandler.handleEmptyError()) } .setNegativeButton(R.string.action_cancel) { dialog, id -> } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/challenges/ChallengeDetailDialogHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/challenges/ChallengeDetailDialogHolder.kt index 42ed3d389..5839f2dc3 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/challenges/ChallengeDetailDialogHolder.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/challenges/ChallengeDetailDialogHolder.kt @@ -236,7 +236,7 @@ class ChallengeDetailDialogHolder private constructor(view: View, private val co } private fun openLeaderProfile() { - FullProfileActivity.open(context, challenge?.leaderId) + FullProfileActivity.open(context, challenge?.leaderId ?: "") } private fun openChallengeActivity() { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/challenges/ChallengesOverviewFragment.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/challenges/ChallengesOverviewFragment.java deleted file mode 100644 index 29cb1b759..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/challenges/ChallengesOverviewFragment.java +++ /dev/null @@ -1,134 +0,0 @@ -package com.habitrpg.android.habitica.ui.fragments.social.challenges; - -import android.content.Intent; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentStatePagerAdapter; -import android.support.v4.view.ViewPager; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.components.AppComponent; -import com.habitrpg.android.habitica.data.ChallengeRepository; -import com.habitrpg.android.habitica.events.commands.ShowChallengeDetailActivityCommand; -import com.habitrpg.android.habitica.events.commands.ShowChallengeDetailDialogCommand; -import com.habitrpg.android.habitica.helpers.RxErrorHandler; -import com.habitrpg.android.habitica.ui.activities.ChallengeDetailActivity; -import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment; - -import org.greenrobot.eventbus.Subscribe; - -import javax.inject.Inject; - -public class ChallengesOverviewFragment extends BaseMainFragment { - - @Inject - ChallengeRepository challengeRepository; - - public ViewPager viewPager; - public FragmentStatePagerAdapter statePagerAdapter; - private ChallengeListFragment userChallengesFragment; - private ChallengeListFragment availableChallengesFragment; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - this.usesTabLayout = true; - super.onCreateView(inflater, container, savedInstanceState); - View v = inflater.inflate(R.layout.fragment_viewpager, container, false); - - viewPager = (ViewPager) v.findViewById(R.id.viewPager); - - setViewPagerAdapter(); - - userChallengesFragment = new ChallengeListFragment(); - userChallengesFragment.setUser(this.user); - userChallengesFragment.setViewUserChallengesOnly(true); - - availableChallengesFragment = new ChallengeListFragment(); - availableChallengesFragment.setUser(this.user); - availableChallengesFragment.setViewUserChallengesOnly(false); - return v; - } - - @Override - public void onDestroy() { - challengeRepository.close(); - super.onDestroy(); - } - - @Override - public void injectFragment(AppComponent component) { - component.inject(this); - } - - public void setViewPagerAdapter() { - android.support.v4.app.FragmentManager fragmentManager = getChildFragmentManager(); - - statePagerAdapter = new FragmentStatePagerAdapter(fragmentManager) { - - @Override - public Fragment getItem(int position) { - Fragment fragment = new Fragment(); - - switch (position) { - case 0: - return userChallengesFragment; - case 1: - return availableChallengesFragment; - } - - return fragment; - } - - @Override - public int getCount() { - return 2; - } - - @Override - public CharSequence getPageTitle(int position) { - switch (position) { - case 0: - return getString(R.string.my_challenges); - case 1: - return getString(R.string.public_challenges); - } - return ""; - } - }; - viewPager.setAdapter(statePagerAdapter); - - if (tabLayout != null && viewPager != null) { - tabLayout.setupWithViewPager(viewPager); - } - } - - @Subscribe - public void onEvent(ShowChallengeDetailDialogCommand cmd) { - challengeRepository.getChallenge(cmd.challengeId).firstElement().subscribe(challenge -> ChallengeDetailDialogHolder.Companion.showDialog(getActivity(), challengeRepository, challenge, - challenge1 -> { - // challenge left - }), RxErrorHandler.handleEmptyError()); - } - - @Subscribe - public void onEvent(ShowChallengeDetailActivityCommand cmd) { - Bundle bundle = new Bundle(); - bundle.putString(ChallengeDetailActivity.CHALLENGE_ID, cmd.challengeId); - - Intent intent = new Intent(getActivity(), ChallengeDetailActivity.class); - intent.putExtras(bundle); - getActivity().startActivity(intent); - } - - @Override - public String customTitle() { - if (!isAdded()) { - return ""; - } - return getString(R.string.challenges); - } -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/challenges/ChallengesOverviewFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/challenges/ChallengesOverviewFragment.kt new file mode 100644 index 000000000..4ea46cad7 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/challenges/ChallengesOverviewFragment.kt @@ -0,0 +1,130 @@ +package com.habitrpg.android.habitica.ui.fragments.social.challenges + +import android.content.Intent +import android.os.Bundle +import android.support.v4.app.Fragment +import android.support.v4.app.FragmentStatePagerAdapter +import android.support.v4.view.ViewPager +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup + +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.components.AppComponent +import com.habitrpg.android.habitica.data.ChallengeRepository +import com.habitrpg.android.habitica.events.commands.ShowChallengeDetailActivityCommand +import com.habitrpg.android.habitica.events.commands.ShowChallengeDetailDialogCommand +import com.habitrpg.android.habitica.helpers.RxErrorHandler +import com.habitrpg.android.habitica.models.social.Challenge +import com.habitrpg.android.habitica.ui.activities.ChallengeDetailActivity +import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment +import com.habitrpg.android.habitica.ui.helpers.bindView +import com.habitrpg.android.habitica.utils.Action1 +import io.reactivex.functions.Consumer + +import org.greenrobot.eventbus.Subscribe + +import javax.inject.Inject + +class ChallengesOverviewFragment : BaseMainFragment() { + + @Inject + internal lateinit var challengeRepository: ChallengeRepository + + private val viewPager: ViewPager? by bindView(R.id.viewPager) + var statePagerAdapter: FragmentStatePagerAdapter? = null + private var userChallengesFragment: ChallengeListFragment? = null + private var availableChallengesFragment: ChallengeListFragment? = null + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + this.usesTabLayout = true + super.onCreateView(inflater, container, savedInstanceState) + val v = inflater.inflate(R.layout.fragment_viewpager, container, false) + + return v + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + setViewPagerAdapter() + + userChallengesFragment = ChallengeListFragment() + userChallengesFragment?.user = this.user + userChallengesFragment?.setViewUserChallengesOnly(true) + + availableChallengesFragment = ChallengeListFragment() + availableChallengesFragment?.user = this.user + availableChallengesFragment?.setViewUserChallengesOnly(false) + } + + override fun onDestroy() { + challengeRepository.close() + super.onDestroy() + } + + override fun injectFragment(component: AppComponent) { + component.inject(this) + } + + private fun setViewPagerAdapter() { + val fragmentManager = childFragmentManager + + statePagerAdapter = object : FragmentStatePagerAdapter(fragmentManager) { + + override fun getItem(position: Int): Fragment? { + val fragment = Fragment() + + when (position) { + 0 -> return userChallengesFragment + 1 -> return availableChallengesFragment + } + + return fragment + } + + override fun getCount(): Int { + return 2 + } + + override fun getPageTitle(position: Int): CharSequence? { + when (position) { + 0 -> return getString(R.string.my_challenges) + 1 -> return getString(R.string.public_challenges) + } + return "" + } + } + viewPager?.adapter = statePagerAdapter + tabLayout?.setupWithViewPager(viewPager) + } + + @Subscribe + fun onEvent(cmd: ShowChallengeDetailDialogCommand) { + challengeRepository.getChallenge(cmd.challengeId).firstElement().subscribe(Consumer { challenge -> + ChallengeDetailDialogHolder.showDialog(activity, challengeRepository, challenge, + object: Action1 { + override fun call(t: Challenge) { + + } + }) + }, RxErrorHandler.handleEmptyError()) + } + + @Subscribe + fun onEvent(cmd: ShowChallengeDetailActivityCommand) { + val bundle = Bundle() + bundle.putString(ChallengeDetailActivity.CHALLENGE_ID, cmd.challengeId) + + val intent = Intent(activity, ChallengeDetailActivity::class.java) + intent.putExtras(bundle) + activity?.startActivity(intent) + } + + override fun customTitle(): String { + return if (!isAdded) { + "" + } else getString(R.string.challenges) + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyFragment.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyFragment.java deleted file mode 100644 index 35983f1a6..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyFragment.java +++ /dev/null @@ -1,362 +0,0 @@ -package com.habitrpg.android.habitica.ui.fragments.social.party; - -import android.app.Activity; -import android.app.AlertDialog; -import android.content.Intent; -import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentPagerAdapter; -import android.support.v4.view.ViewPager; -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 com.habitrpg.android.habitica.R; -import com.habitrpg.android.habitica.components.AppComponent; -import com.habitrpg.android.habitica.data.InventoryRepository; -import com.habitrpg.android.habitica.data.SocialRepository; -import com.habitrpg.android.habitica.helpers.RxErrorHandler; -import com.habitrpg.android.habitica.models.social.Group; -import com.habitrpg.android.habitica.models.social.UserParty; -import com.habitrpg.android.habitica.ui.activities.GroupFormActivity; -import com.habitrpg.android.habitica.ui.activities.PartyInviteActivity; -import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment; -import com.habitrpg.android.habitica.ui.fragments.social.ChatListFragment; -import com.habitrpg.android.habitica.ui.fragments.social.GroupInformationFragment; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -import io.reactivex.android.schedulers.AndroidSchedulers; - -public class PartyFragment extends BaseMainFragment { - - @Inject - SocialRepository socialRepository; - - public ViewPager viewPager; - @Inject - InventoryRepository inventoryRepository; - @Nullable - private Group group; - private PartyMemberListFragment partyMemberListFragment; - private ChatListFragment chatListFragment; - private FragmentPagerAdapter viewPagerAdapter; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - this.usesTabLayout = true; - hideToolbar(); - disableToolbarScrolling(); - super.onCreateView(inflater, container, savedInstanceState); - View v = inflater.inflate(R.layout.fragment_viewpager, container, false); - - viewPager = (ViewPager) v.findViewById(R.id.viewPager); - - viewPager.setCurrentItem(0); - - // Get the full group data - if (userHasParty()) { - if (user != null) { - getCompositeSubscription().add(socialRepository.getGroup(user.getParty().getId()) - .firstElement() - //delay, so that realm can save party first - .delay(500, TimeUnit.MILLISECONDS) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(group -> { - PartyFragment.this.group = group; - updateGroupUI(); - }, RxErrorHandler.handleEmptyError())); - } - socialRepository.retrieveGroup("party") - .flatMap(group1 -> socialRepository.retrieveGroupMembers(group1.getId(), true)) - .subscribe(members -> {}, RxErrorHandler.handleEmptyError()); - } - - setViewPagerAdapter(); - this.setTutorialStepIdentifier("party"); - this.setTutorialText(getString(R.string.tutorial_party)); - - return v; - } - - private boolean userHasParty() { - return this.user != null && this.user.getParty() != null && this.user.getParty().id != null; - } - - @Override - public void onDestroyView() { - showToolbar(); - enableToolbarScrolling(); - super.onDestroyView(); - } - - @Override - public void onDestroy() { - socialRepository.close(); - super.onDestroy(); - } - - @Override - public void injectFragment(AppComponent component) { - component.inject(this); - } - - private void updateGroupUI() { - if (viewPagerAdapter != null) { - viewPagerAdapter.notifyDataSetChanged(); - } - - if (tabLayout != null) { - if (group == null) { - tabLayout.setVisibility(View.GONE); - return; - } else { - tabLayout.setVisibility(View.VISIBLE); - } - } - - if (partyMemberListFragment != null && group != null) { - partyMemberListFragment.setPartyId(group.getId()); - } - - if (chatListFragment != null && group != null) { - chatListFragment.setSeenGroupId(group.getId()); - } - - if (this.activity != null) { - this.activity.supportInvalidateOptionsMenu(); - } - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - if (this.group != null && this.user != null) { - if (this.group.getLeaderID().equals(this.user.getId())) { - inflater.inflate(R.menu.menu_party_admin, menu); - } else { - inflater.inflate(R.menu.menu_party, menu); - } - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - int id = item.getItemId(); - - switch (id) { - case R.id.menu_invite_item: - Intent intent = new Intent(getActivity(), PartyInviteActivity.class); - startActivityForResult(intent, PartyInviteActivity.RESULT_SEND_INVITES); - return true; - case R.id.menu_guild_edit: - this.displayEditForm(); - return true; - case R.id.menu_guild_leave: - new AlertDialog.Builder(viewPager.getContext()) - .setTitle(viewPager.getContext().getString(R.string.leave_party)) - .setMessage(viewPager.getContext().getString(R.string.leave_party_confirmation)) - .setPositiveButton(viewPager.getContext().getString(R.string.yes), (dialog, which) -> { - if (this.group != null){ - this.socialRepository.leaveGroup(this.group.getId()) - .subscribe(group -> getActivity().getSupportFragmentManager().beginTransaction().remove(PartyFragment.this).commit(), RxErrorHandler.handleEmptyError()); - } - }) - .setNegativeButton(viewPager.getContext().getString(R.string.no), (dialog, which) -> dialog.dismiss()) - .show(); - return true; - } - - return super.onOptionsItemSelected(item); - } - - private void displayEditForm() { - Bundle bundle = new Bundle(); - bundle.putString("groupID", this.group != null ? this.group.getId() : null); - bundle.putString("name", this.group.getName()); - bundle.putString("description", this.group.getDescription()); - bundle.putString("leader", this.group.getLeaderID()); - - Intent intent = new Intent(activity, GroupFormActivity.class); - intent.putExtras(bundle); - intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - startActivityForResult(intent, GroupFormActivity.GROUP_FORM_ACTIVITY); - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - switch (requestCode) { - case (GroupFormActivity.GROUP_FORM_ACTIVITY): { - if (resultCode == Activity.RESULT_OK) { - boolean needsSaving = false; - Bundle bundle = data.getExtras(); - if (this.group == null) { - break; - } - this.socialRepository.updateGroup(this.group, bundle.getString("name"), bundle.getString("description"), bundle.getString("leader"), bundle.getString("privacy")) - .subscribe(aVoid -> {}, RxErrorHandler.handleEmptyError()); - } - break; - } - case (PartyInviteActivity.RESULT_SEND_INVITES): { - if (resultCode == Activity.RESULT_OK) { - Map inviteData = new HashMap<>(); - inviteData.put("inviter", this.user != null ? this.user.getProfile().getName() : null); - if (data.getBooleanExtra(PartyInviteActivity.IS_EMAIL_KEY, false)) { - String[] emails = data.getStringArrayExtra(PartyInviteActivity.EMAILS_KEY); - List> invites = new ArrayList<>(); - for (String email : emails) { - HashMap invite = new HashMap<>(); - invite.put("name", ""); - invite.put("email", email); - invites.add(invite); - } - inviteData.put("emails", invites); - } else { - String[] userIDs = data.getStringArrayExtra(PartyInviteActivity.USER_IDS_KEY); - List invites = new ArrayList<>(); - Collections.addAll(invites, userIDs); - inviteData.put("uuids", invites); - } - if (this.group != null) { - this.socialRepository.inviteToGroup(this.group.getId(), inviteData) - .subscribe(aVoid -> {}, RxErrorHandler.handleEmptyError()); - } - } - } - } - } - - public void setViewPagerAdapter() { - android.support.v4.app.FragmentManager fragmentManager = getChildFragmentManager(); - if (this.user == null) { - return; - } - - UserParty party = this.user.getParty(); - - if (party == null) { - return; - } - - viewPagerAdapter = new FragmentPagerAdapter(fragmentManager) { - - @Override - public Fragment getItem(int position) { - - Fragment fragment; - - switch (position) { - case 0: { - if (user.hasParty()) { - PartyDetailFragment detailFragment = new PartyDetailFragment(); - detailFragment.setPartyId(user.getParty().id); - fragment = detailFragment; - } else { - fragment = GroupInformationFragment.Companion.newInstance(null, user); - } - break; - } - case 1: { - if (chatListFragment == null) { - chatListFragment = new ChatListFragment(); - if (user.hasParty()) { - chatListFragment.configure(user.getParty().id, user, false); - } - } - fragment = chatListFragment; - break; - } - case 2: { - if (partyMemberListFragment == null) { - partyMemberListFragment = new PartyMemberListFragment(); - if (user.hasParty()) { - partyMemberListFragment.setPartyId(user.getParty().id); - } - } - fragment = partyMemberListFragment; - break; - } - default: - fragment = new Fragment(); - } - - return fragment; - } - - @Override - public int getCount() { - if (group == null) { - return 1; - } else { - return 3; - } - } - - @Override - public CharSequence getPageTitle(int position) { - switch (position) { - case 0: - return getContext().getString(R.string.party); - case 1: - return getContext().getString(R.string.chat); - case 2: - return getContext().getString(R.string.members); - } - return ""; - } - }; - this.viewPager.setAdapter(viewPagerAdapter); - - viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - if (position == 1 && group != null) { - if (chatListFragment != null) { - chatListFragment.setNavigatedToFragment(group.getId()); - - } - } - } - - @Override - public void onPageSelected(int position) { - if (position == 1 && group != null) { - if (chatListFragment != null) { - chatListFragment.setNavigatedToFragment(group.getId()); - } - } - } - - @Override - public void onPageScrollStateChanged(int state) { - - } - }); - if (tabLayout != null) { - tabLayout.setupWithViewPager(viewPager); - } - } - - - @Override - public String customTitle() { - if (!isAdded()) { - return ""; - } - return getString(R.string.sidebar_party); - } - -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyFragment.kt new file mode 100644 index 000000000..badd92ea2 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyFragment.kt @@ -0,0 +1,325 @@ +package com.habitrpg.android.habitica.ui.fragments.social.party + +import android.app.Activity +import android.app.AlertDialog +import android.content.Intent +import android.os.Bundle +import android.support.v4.app.Fragment +import android.support.v4.app.FragmentPagerAdapter +import android.support.v4.view.ViewPager +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 com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.components.AppComponent +import com.habitrpg.android.habitica.data.InventoryRepository +import com.habitrpg.android.habitica.data.SocialRepository +import com.habitrpg.android.habitica.helpers.RxErrorHandler +import com.habitrpg.android.habitica.models.social.Group +import com.habitrpg.android.habitica.models.social.UserParty +import com.habitrpg.android.habitica.ui.activities.GroupFormActivity +import com.habitrpg.android.habitica.ui.activities.PartyInviteActivity +import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment +import com.habitrpg.android.habitica.ui.fragments.social.ChatListFragment +import com.habitrpg.android.habitica.ui.fragments.social.GroupInformationFragment +import com.habitrpg.android.habitica.ui.helpers.bindView + +import java.util.ArrayList +import java.util.Collections +import java.util.HashMap +import java.util.concurrent.TimeUnit + +import javax.inject.Inject + +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.functions.Consumer + +class PartyFragment : BaseMainFragment() { + + @Inject + internal lateinit var socialRepository: SocialRepository + @Inject + internal lateinit var inventoryRepository: InventoryRepository + + private val viewPager: ViewPager? by bindView(R.id.viewpager) + private var group: Group? = null + private var partyMemberListFragment: PartyMemberListFragment? = null + private var chatListFragment: ChatListFragment? = null + private var viewPagerAdapter: FragmentPagerAdapter? = null + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + this.usesTabLayout = true + hideToolbar() + disableToolbarScrolling() + super.onCreateView(inflater, container, savedInstanceState) + return inflater.inflate(R.layout.fragment_viewpager, container, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + + viewPager?.currentItem = 0 + + // Get the full group data + if (userHasParty()) { + if (user != null) { + compositeSubscription.add(socialRepository.getGroup(user?.party?.id) + .firstElement() + //delay, so that realm can save party first + .delay(500, TimeUnit.MILLISECONDS) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(Consumer { group -> + this@PartyFragment.group = group + updateGroupUI() + }, RxErrorHandler.handleEmptyError())) + } + socialRepository.retrieveGroup("party") + .flatMap { group1 -> socialRepository.retrieveGroupMembers(group1.id, true) } + .subscribe(Consumer { }, RxErrorHandler.handleEmptyError()) + } + + setViewPagerAdapter() + this.tutorialStepIdentifier = "party" + this.tutorialText = getString(R.string.tutorial_party) + } + + private fun userHasParty(): Boolean { + return user?.party?.id != null + } + + override fun onDestroyView() { + showToolbar() + enableToolbarScrolling() + super.onDestroyView() + } + + override fun onDestroy() { + socialRepository.close() + inventoryRepository.close() + super.onDestroy() + } + + override fun injectFragment(component: AppComponent) { + component.inject(this) + } + + private fun updateGroupUI() { + if (viewPagerAdapter != null) { + viewPagerAdapter!!.notifyDataSetChanged() + } + + if (tabLayout != null) { + if (group == null) { + tabLayout?.visibility = View.GONE + return + } else { + tabLayout?.visibility = View.VISIBLE + } + } + + partyMemberListFragment?.setPartyId(group!!.id) + + chatListFragment?.seenGroupId = group!!.id + + this.activity?.supportInvalidateOptionsMenu() + } + + override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { + if (this.group != null && this.user != null) { + if (this.group!!.leaderID == this.user!!.id) { + inflater!!.inflate(R.menu.menu_party_admin, menu) + } else { + inflater!!.inflate(R.menu.menu_party, menu) + } + } + } + + override fun onOptionsItemSelected(item: MenuItem?): Boolean { + val id = item!!.itemId + + when (id) { + R.id.menu_invite_item -> { + val intent = Intent(activity, PartyInviteActivity::class.java) + startActivityForResult(intent, PartyInviteActivity.RESULT_SEND_INVITES) + return true + } + R.id.menu_guild_edit -> { + this.displayEditForm() + return true + } + R.id.menu_guild_leave -> { + AlertDialog.Builder(context) + .setTitle(context?.getString(R.string.leave_party)) + .setMessage(context?.getString(R.string.leave_party_confirmation)) + .setPositiveButton(context?.getString(R.string.yes)) { _, _ -> + if (this.group != null) { + this.socialRepository.leaveGroup(this.group!!.id) + .subscribe(Consumer { activity?.supportFragmentManager?.beginTransaction()?.remove(this@PartyFragment)?.commit() }, RxErrorHandler.handleEmptyError()) + } + } + .setNegativeButton(context?.getString(R.string.no)) { dialog, _ -> dialog.dismiss() } + .show() + return true + } + } + + return super.onOptionsItemSelected(item) + } + + private fun displayEditForm() { + val bundle = Bundle() + bundle.putString("groupID", if (this.group != null) this.group!!.id else null) + bundle.putString("name", this.group!!.name) + bundle.putString("description", this.group!!.description) + bundle.putString("leader", this.group!!.leaderID) + + val intent = Intent(activity, GroupFormActivity::class.java) + intent.putExtras(bundle) + intent.flags = Intent.FLAG_ACTIVITY_REORDER_TO_FRONT + startActivityForResult(intent, GroupFormActivity.GROUP_FORM_ACTIVITY) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + when (requestCode) { + GroupFormActivity.GROUP_FORM_ACTIVITY -> { + if (resultCode == Activity.RESULT_OK) { + val needsSaving = false + val bundle = data!!.extras + if (this.group == null) { + return + } + this.socialRepository.updateGroup(this.group, bundle!!.getString("name"), bundle.getString("description"), bundle.getString("leader"), bundle.getString("privacy")) + .subscribe(Consumer { }, RxErrorHandler.handleEmptyError()) + } + } + PartyInviteActivity.RESULT_SEND_INVITES -> { + if (resultCode == Activity.RESULT_OK) { + val inviteData = HashMap() + inviteData["inviter"] = user?.profile?.name ?: "" + if (data!!.getBooleanExtra(PartyInviteActivity.IS_EMAIL_KEY, false)) { + val emails = data.getStringArrayExtra(PartyInviteActivity.EMAILS_KEY) + val invites = ArrayList>() + for (email in emails) { + val invite = HashMap() + invite["name"] = "" + invite["email"] = email + invites.add(invite) + } + inviteData["emails"] = invites + } else { + val userIDs = data.getStringArrayExtra(PartyInviteActivity.USER_IDS_KEY) + val invites = ArrayList() + Collections.addAll(invites, *userIDs) + inviteData["uuids"] = invites + } + if (this.group != null) { + this.socialRepository.inviteToGroup(this.group!!.id, inviteData) + .subscribe(Consumer { }, RxErrorHandler.handleEmptyError()) + } + } + } + } + } + + private fun setViewPagerAdapter() { + val fragmentManager = childFragmentManager + if (this.user == null) { + return + } + + val party = this.user!!.party ?: return + + viewPagerAdapter = object : FragmentPagerAdapter(fragmentManager) { + + override fun getItem(position: Int): Fragment? { + + val fragment: Fragment? + + when (position) { + 0 -> { + if (user!!.hasParty()) { + val detailFragment = PartyDetailFragment() + detailFragment.partyId = user!!.party.id + fragment = detailFragment + } else { + fragment = GroupInformationFragment.newInstance(null, user) + } + } + 1 -> { + if (chatListFragment == null) { + chatListFragment = ChatListFragment() + if (user!!.hasParty()) { + chatListFragment!!.configure(user!!.party.id, user, false) + } + } + fragment = chatListFragment + } + 2 -> { + if (partyMemberListFragment == null) { + partyMemberListFragment = PartyMemberListFragment() + if (user!!.hasParty()) { + partyMemberListFragment!!.setPartyId(user!!.party.id) + } + } + fragment = partyMemberListFragment + } + else -> fragment = Fragment() + } + + return fragment + } + + override fun getCount(): Int { + return if (group == null) { + 1 + } else { + 3 + } + } + + override fun getPageTitle(position: Int): CharSequence? { + when (position) { + 0 -> return context!!.getString(R.string.party) + 1 -> return context!!.getString(R.string.chat) + 2 -> return context!!.getString(R.string.members) + } + return "" + } + } + this.viewPager?.adapter = viewPagerAdapter + + viewPager?.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { + override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { + if (position == 1 && group != null) { + chatListFragment?.setNavigatedToFragment(group!!.id) + } + } + + override fun onPageSelected(position: Int) { + if (position == 1 && group != null) { + chatListFragment?.setNavigatedToFragment(group!!.id) + } + } + + override fun onPageScrollStateChanged(state: Int) { + + } + }) + tabLayout?.setupWithViewPager(viewPager) + } + + + override fun customTitle(): String { + return if (!isAdded) { + "" + } else getString(R.string.sidebar_party) + } + +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyMemberListFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyMemberListFragment.kt index cca6d4d42..09c7402da 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyMemberListFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyMemberListFragment.kt @@ -48,7 +48,9 @@ class PartyMemberListFragment : BaseFragment() { recyclerView?.layoutManager = LinearLayoutManager(context) adapter = PartyMemberRecyclerViewAdapter(null, true) - adapter?.getUserClickedEvents()?.subscribe(Consumer { userId -> FullProfileActivity.open(context, userId) }, RxErrorHandler.handleEmptyError()).notNull { compositeSubscription.add(it) } + context.notNull { + adapter?.getUserClickedEvents()?.subscribe(Consumer { userId -> FullProfileActivity.open(it, userId) }, RxErrorHandler.handleEmptyError()).notNull { compositeSubscription.add(it) } + } recyclerView?.adapter = adapter recyclerView?.itemAnimator = SafeDefaultItemAnimator() @@ -59,7 +61,7 @@ class PartyMemberListFragment : BaseFragment() { private fun refreshMembers() { setRefreshing(true) - socialRepository.retrieveGroupMembers(partyId!!, true).doOnComplete { setRefreshing(false) }.subscribe(Consumer { users -> }, RxErrorHandler.handleEmptyError()) + socialRepository.retrieveGroupMembers(partyId!!, true).doOnComplete { setRefreshing(false) }.subscribe(Consumer { }, RxErrorHandler.handleEmptyError()) } private fun setRefreshing(isRefreshing: Boolean) { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TasksFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TasksFragment.kt index 751afd62f..2119f7e25 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TasksFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TasksFragment.kt @@ -135,7 +135,7 @@ class TasksFragment : BaseMainFragment() { private fun showFilterDialog() { context.notNull { - val dialog = TaskFilterDialog(it, HabiticaBaseApplication.getComponent()) + val dialog = TaskFilterDialog(it, HabiticaBaseApplication.component) if (user != null) { dialog.setTags(user?.tags?.createSnapshot() ?: emptyList()) } @@ -378,7 +378,7 @@ class TasksFragment : BaseMainFragment() { override val displayedClassName: String? get() = null - override fun customTitle(): String? = null + override fun customTitle(): String = "" override fun addToBackStack(): Boolean = false diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/Kotterknife.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/Kotterknife.kt index 5103cbba5..b501daf01 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/Kotterknife.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/helpers/Kotterknife.kt @@ -13,12 +13,6 @@ import java.util.* import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KProperty -object ButterKnife { - fun reset(target: Any) { - LazyRegistry.reset(target) - } -} - fun View.bindView(@IdRes id: Int) : ReadOnlyProperty = required(id, viewFinder) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/ShopItemViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/ShopItemViewHolder.kt index 41e12623a..41ca49056 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/ShopItemViewHolder.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/ShopItemViewHolder.kt @@ -102,7 +102,7 @@ class ShopItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), Vi override fun onClick(view: View) { val item = item if (item != null) { - val dialog = PurchaseDialog(context, HabiticaBaseApplication.getComponent(), item) + val dialog = PurchaseDialog(context, HabiticaBaseApplication.component, item) dialog.shopIdentifier = shopIdentifier dialog.isPinned = isPinned dialog.show() diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/BaseTaskViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/BaseTaskViewHolder.kt index eb1fa8930..d69b814b0 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/BaseTaskViewHolder.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/BaseTaskViewHolder.kt @@ -22,7 +22,7 @@ import io.reactivex.schedulers.Schedulers import net.pherth.android.emoji_library.EmojiTextView import org.greenrobot.eventbus.EventBus -abstract class BaseTaskViewHolder @JvmOverloads constructor(itemView: View, useButterKnife: Boolean = true) : RecyclerView.ViewHolder(itemView), View.OnClickListener { +abstract class BaseTaskViewHolder constructor(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener { var task: Task? = null @@ -65,7 +65,7 @@ abstract class BaseTaskViewHolder @JvmOverloads constructor(itemView: View, useB init { - itemView.setOnClickListener(this) + itemView.setOnClickListener { onClick(it) } itemView.isClickable = true //Re enable when we find a way to only react when a link is tapped. diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialog.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialog.kt index 497130e8d..62470ca4c 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialog.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/shops/PurchaseDialog.kt @@ -40,7 +40,7 @@ import org.greenrobot.eventbus.EventBus import java.util.* import javax.inject.Inject -class PurchaseDialog(context: Context, component: AppComponent, val item: ShopItem) : AlertDialog(context) { +class PurchaseDialog(context: Context, component: AppComponent?, val item: ShopItem) : AlertDialog(context) { @Inject lateinit var userRepository: UserRepository @@ -133,7 +133,7 @@ class PurchaseDialog(context: Context, component: AppComponent, val item: ShopIt } } init { - component.inject(this) + component?.inject(this) setView(customView) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/social/ChatBarView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/social/ChatBarView.kt index 385177316..73ec5cf63 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/social/ChatBarView.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/social/ChatBarView.kt @@ -14,8 +14,8 @@ import android.widget.ImageButton import android.widget.LinearLayout import android.widget.TextView import com.habitrpg.android.habitica.R -import com.habitrpg.android.habitica.extensions.bindView import com.habitrpg.android.habitica.ui.helpers.NavbarUtils +import com.habitrpg.android.habitica.ui.helpers.bindView class ChatBarView : FrameLayout { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/stats/BulkAllocateStatsDialog.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/stats/BulkAllocateStatsDialog.kt index 4995de327..25679f451 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/stats/BulkAllocateStatsDialog.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/stats/BulkAllocateStatsDialog.kt @@ -16,7 +16,7 @@ import io.reactivex.functions.Consumer import kotlinx.android.synthetic.main.dialog_bulk_allocate.* import javax.inject.Inject -class BulkAllocateStatsDialog(context: Context, component: AppComponent) : AlertDialog(context) { +class BulkAllocateStatsDialog(context: Context, component: AppComponent?) : AlertDialog(context) { @Inject lateinit var userRepository: UserRepository @@ -57,7 +57,7 @@ class BulkAllocateStatsDialog(context: Context, component: AppComponent) : Alert } init { - component.inject(this) + component?.inject(this) val inflater = LayoutInflater.from(context) val view = inflater.inflate(R.layout.dialog_bulk_allocate, null) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/subscriptions/SubscriptionOptionView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/subscriptions/SubscriptionOptionView.kt index e6bbe622a..114225583 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/subscriptions/SubscriptionOptionView.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/subscriptions/SubscriptionOptionView.kt @@ -1,6 +1,7 @@ package com.habitrpg.android.habitica.ui.views.subscriptions import android.content.Context +import android.support.v4.content.ContextCompat import android.util.AttributeSet import android.view.View import android.widget.FrameLayout @@ -57,19 +58,19 @@ class SubscriptionOptionView(context: Context, attrs: AttributeSet) : FrameLayou subscriptionSelectedView.setBackgroundResource(R.drawable.subscription_selected) subscriptionSelectedFrameView.setBackgroundResource(R.color.brand_300) gemCapTextView.setBackgroundResource(R.drawable.pill_bg_green) - gemCapTextView.setTextColor(context.resources.getColor(R.color.white)) + gemCapTextView.setTextColor(ContextCompat.getColor(context, R.color.white)) gemCapTextView.setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding) hourGlassTextView.setBackgroundResource(R.drawable.pill_bg_green) - hourGlassTextView.setTextColor(context.resources.getColor(R.color.white)) + hourGlassTextView.setTextColor(ContextCompat.getColor(context, R.color.white)) hourGlassTextView.setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding) } else { subscriptionSelectedView.setBackgroundResource(R.drawable.subscription_unselected) subscriptionSelectedFrameView.setBackgroundResource(R.color.brand_700) gemCapTextView.setBackgroundResource(R.drawable.pill_bg) - gemCapTextView.setTextColor(context.resources.getColor(R.color.text_light)) + gemCapTextView.setTextColor(ContextCompat.getColor(context, R.color.text_light)) gemCapTextView.setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding) hourGlassTextView.setBackgroundResource(R.drawable.pill_bg) - hourGlassTextView.setTextColor(context.resources.getColor(R.color.text_light)) + hourGlassTextView.setTextColor(ContextCompat.getColor(context, R.color.text_light)) hourGlassTextView.setPadding(horizontalPadding, verticalPadding, horizontalPadding, verticalPadding) } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/TaskFilterDialog.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/TaskFilterDialog.kt index 4f73a42db..5688cf71c 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/TaskFilterDialog.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/tasks/TaskFilterDialog.kt @@ -29,7 +29,7 @@ import io.reactivex.functions.Consumer import java.util.* import javax.inject.Inject -class TaskFilterDialog(context: Context, component: AppComponent) : AlertDialog(context), RadioGroup.OnCheckedChangeListener { +class TaskFilterDialog(context: Context, component: AppComponent?) : AlertDialog(context), RadioGroup.OnCheckedChangeListener { @Inject lateinit var repository: TagRepository @@ -55,7 +55,7 @@ class TaskFilterDialog(context: Context, component: AppComponent) : AlertDialog( private var isEditing: Boolean = false init { - component.inject(this) + component?.inject(this) addIcon = ContextCompat.getDrawable(context, R.drawable.ic_add_purple_300_36dp) val inflater = LayoutInflater.from(context) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/AvatarStatsWidgetProvider.java b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/AvatarStatsWidgetProvider.java index 4a6844f91..63c758013 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/AvatarStatsWidgetProvider.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/AvatarStatsWidgetProvider.java @@ -22,6 +22,8 @@ import com.habitrpg.android.habitica.ui.AvatarView; import com.habitrpg.android.habitica.ui.activities.MainActivity; import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper; +import java.util.Objects; + import javax.inject.Inject; import javax.inject.Named; @@ -42,7 +44,7 @@ public class AvatarStatsWidgetProvider extends BaseWidgetProvider { private void setUp() { if (userRepository == null) { - HabiticaBaseApplication.getComponent().inject(this); + Objects.requireNonNull(HabiticaBaseApplication.Companion.getComponent()).inject(this); } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/BaseWidgetProvider.java b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/BaseWidgetProvider.java index 3d4aa5497..2ce5f8024 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/BaseWidgetProvider.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/BaseWidgetProvider.java @@ -17,6 +17,8 @@ import com.habitrpg.android.habitica.interactors.NotifyUserUseCase; import com.habitrpg.android.habitica.models.responses.TaskScoringResult; import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar; +import java.util.Objects; + import javax.inject.Inject; @@ -74,7 +76,7 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider { protected void showToastForTaskDirection(Context context, TaskScoringResult data, String userID) { if (userRepository == null) { - HabiticaApplication.getComponent().inject(this); + Objects.requireNonNull(HabiticaApplication.Companion.getComponent()).inject(this); } if (data != null) { Pair pair = NotifyUserUseCase.Companion.getNotificationAndAddStatsToUserAsText(context, data.getExperienceDelta(), data.getHealthDelta(), data.getGoldDelta(), data.getManaDelta()); diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/DailiesWidgetProvider.java b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/DailiesWidgetProvider.java index b3d008f52..b292f6327 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/DailiesWidgetProvider.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/DailiesWidgetProvider.java @@ -8,6 +8,8 @@ import com.habitrpg.android.habitica.R; import com.habitrpg.android.habitica.api.HostConfig; import com.habitrpg.android.habitica.data.ApiClient; +import java.util.Objects; + import javax.inject.Inject; public class DailiesWidgetProvider extends TaskListWidgetProvider { @@ -22,8 +24,7 @@ public class DailiesWidgetProvider extends TaskListWidgetProvider { private void setUp(Context context) { if (apiClient == null) { - HabiticaBaseApplication application = HabiticaApplication.getInstance(context); - application.getComponent().inject(this); + Objects.requireNonNull(HabiticaBaseApplication.Companion.getComponent()).inject(this); } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetProvider.java b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetProvider.java index 4d3fe0a87..5db1b3a71 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetProvider.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetProvider.java @@ -15,6 +15,8 @@ import com.habitrpg.android.habitica.helpers.RxErrorHandler; import com.habitrpg.android.habitica.models.responses.TaskDirection; import com.habitrpg.android.habitica.modules.AppModule; +import java.util.Objects; + import javax.inject.Inject; import javax.inject.Named; @@ -31,7 +33,7 @@ public class HabitButtonWidgetProvider extends BaseWidgetProvider { private void setUp() { if (taskRepository == null) { - HabiticaBaseApplication.getComponent().inject(this); + Objects.requireNonNull(HabiticaBaseApplication.Companion.getComponent()).inject(this); } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetService.java b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetService.java index ab3db0b3c..3efc30bfa 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetService.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetService.java @@ -28,6 +28,7 @@ import net.pherth.android.emoji_library.EmojiHandler; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import javax.inject.Inject; import javax.inject.Named; @@ -51,7 +52,7 @@ public class HabitButtonWidgetService extends Service { @Override public int onStartCommand(final Intent intent, int flags, int startId) { - HabiticaBaseApplication.getComponent().inject(this); + Objects.requireNonNull(HabiticaBaseApplication.Companion.getComponent()).inject(this); this.appWidgetManager = AppWidgetManager.getInstance(this); ComponentName thisWidget = new ComponentName(this, HabitButtonWidgetProvider.class); allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget); diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListFactory.java b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListFactory.java index 0fd16a7e0..7eac5bfdb 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListFactory.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListFactory.java @@ -22,6 +22,7 @@ import net.pherth.android.emoji_library.EmojiHandler; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import javax.inject.Inject; import javax.inject.Named; @@ -54,7 +55,7 @@ public abstract class TaskListFactory implements RemoteViewsService.RemoteViewsF this.taskType = taskType; if (userID == null) { - HabiticaApplication.getComponent().inject(this); + Objects.requireNonNull(HabiticaApplication.Companion.getComponent()).inject(this); } this.loadData(); } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListWidgetProvider.java b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListWidgetProvider.java index cf69004fe..f6131542d 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListWidgetProvider.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListWidgetProvider.java @@ -18,6 +18,8 @@ import com.habitrpg.android.habitica.helpers.RxErrorHandler; import com.habitrpg.android.habitica.modules.AppModule; import com.habitrpg.android.habitica.ui.activities.MainActivity; +import java.util.Objects; + import javax.inject.Inject; import javax.inject.Named; @@ -35,7 +37,7 @@ public abstract class TaskListWidgetProvider extends BaseWidgetProvider { private void setUp(Context context) { if (apiClient == null) { - HabiticaBaseApplication.getComponent().inject(this); + Objects.requireNonNull(HabiticaBaseApplication.Companion.getComponent()).inject(this); } }