Refactor activities and remove Butterknife

This commit is contained in:
Phillip Thelen 2018-07-11 17:52:24 +02:00
parent b52b4563e9
commit b366686764
102 changed files with 7291 additions and 8266 deletions

View file

@ -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'

View file

@ -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;
}
}

View file

@ -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)
}
}
}

View file

@ -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

View file

@ -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;

View file

@ -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 {

View file

@ -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
}

View file

@ -17,7 +17,7 @@ class SoundManager {
private val loadedSoundFiles: MutableMap<String, SoundFile> = HashMap()
init {
HabiticaBaseApplication.getComponent().inject(this)
HabiticaBaseApplication.component?.inject(this)
}
fun preloadAllFiles(): Maybe<List<SoundFile>> {

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -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();

View file

@ -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);

View file

@ -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();
}

View file

@ -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)

View file

@ -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 {

View file

@ -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();
}
}

View file

@ -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()
}
}

View file

@ -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();
}
}

View file

@ -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()
}
}

View file

@ -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<Task> 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<Task> resultList = new ArrayList<>();
ArrayList<Task> todos = new ArrayList<>();
ArrayList<Task> habits = new ArrayList<>();
ArrayList<Task> dailies = new ArrayList<>();
ArrayList<Task> rewards = new ArrayList<>();
for (Map.Entry<String, Task> 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<Task> 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<String> 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());
}
}

View file

@ -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<Task>()
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<Task>()
val todos = ArrayList<Task>()
val habits = ArrayList<Task>()
val dailies = ArrayList<Task>()
val rewards = ArrayList<Task>()
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<Task>) {
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<String> {
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<String>) {
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<Challenge> {
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"
}
}

View file

@ -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<User> {
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();
}
}
}

View file

@ -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<User> {
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()
}
}
}

View file

@ -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<String, Task> addedTasks = new HashMap<>();
private HashMap<String, Task> updatedTasks = new HashMap<>();
private HashMap<String, Task> 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<Challenge> 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<String> 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<Task> 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<Challenge> createChallenge() {
Challenge c = getChallengeData();
List<Task> taskList = challengeTasks.getTaskList();
taskList.remove(addHabit);
taskList.remove(addDaily);
taskList.remove(addTodo);
taskList.remove(addReward);
return challengeRepository.createChallenge(c, taskList);
}
private Flowable<Challenge> updateChallenge() {
Challenge c = getChallengeData();
List<Task> 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<Group> {
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;
}
}
}

View file

@ -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<String, Task>()
private val updatedTasks = HashMap<String, Task>()
private val removedTasks = HashMap<String, Task>()
// 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<Challenge> = 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<String>()
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<Task>()
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<Challenge> {
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<Challenge> {
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<Group>(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
}
}
}

View file

@ -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<TableRow> 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<ImageInfo>() {
@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<Object> 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<Object> targetList) {
// Order by ID first
ArrayList<Achievement> 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<ImageInfo>() {
@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<RealmResults<Equipment>> loadItemDataByOutfit(Outfit outfit) {
ArrayList<String> 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<Equipment> equipmentList, Member user) {
UserStatComputer userStatComputer = new UserStatComputer();
List<UserStatComputer.StatsRow> 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<Equipment> 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
}

View file

@ -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<TableRow>()
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<EmojiEditText>(R.id.edit_new_message_text)
val newMessageTitle = newMessageView.findViewById<TextView>(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<ImageInfo>() {
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<RealmResults<Equipment>> { 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<AchievementResult> { this.fillAchievements(it) }, RxErrorHandler.handleEmptyError())
}
// endregion
// region Attributes
private fun fillAchievements(achievements: AchievementResult?) {
if (achievements == null) {
return
}
val items = ArrayList<Any>()
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<Any>) {
// 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<SimpleDraweeView>(R.id.gear_drawee)
draweeView.controller = Fresco.newDraweeControllerBuilder()
.setUri(AvatarView.IMAGE_URI_ROOT + "shop_" + gearKey + ".png")
.setControllerListener(object : BaseControllerListener<ImageInfo>() {
override fun onFailure(id: String?, throwable: Throwable?) {
draweeView.visibility = View.GONE
}
})
.build()
val keyTextView = gearRow.findViewById<TextView>(R.id.tableRowTextView1)
keyTextView.text = text
val valueTextView = gearRow.findViewById<TextView>(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<RealmResults<Equipment>> {
val outfitList = ArrayList<String>()
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<Equipment>, 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<Equipment>) {
// 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<TextView>(R.id.tv_attribute_type)
keyTextView.text = label
val strTextView = tableRow.findViewById<TextView>(R.id.tv_attribute_str)
strTextView.text = getFloorValueString(strVal, roundDown)
val intTextView = tableRow.findViewById<TextView>(R.id.tv_attribute_int)
intTextView.text = getFloorValueString(intVal, roundDown)
val conTextView = tableRow.findViewById<TextView>(R.id.tv_attribute_con)
conTextView.text = getFloorValueString(conVal, roundDown)
val perTextView = tableRow.findViewById<TextView>(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
}

View file

@ -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<CheckoutFragment> 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<Purchase>() {
@Override
public void onSuccess(@NonNull Purchase purchase) {
if (PurchaseTypes.allGemTypes.contains(purchase.sku)) {
billingRequests.consume(purchase.token, new RequestListener<Object>() {
@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<Purchases>() {
@Override
public void onSuccess(@NonNull Purchases purchases) {
for (Purchase purchase : purchases.list) {
if (PurchaseTypes.allGemTypes.contains(purchase.sku)) {
billingRequests.consume(purchase.token, new RequestListener<Object>() {
@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);
}
}

View file

@ -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<CheckoutFragment> = 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<Toolbar>(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<Purchase> {
override fun onSuccess(purchase: Purchase) {
if (PurchaseTypes.allGemTypes.contains(purchase.sku)) {
billingRequests?.consume(purchase.token, object : RequestListener<Any> {
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<Purchases> {
override fun onSuccess(purchases: Purchases) {
for (purchase in purchases.list) {
if (PurchaseTypes.allGemTypes.contains(purchase.sku)) {
billingRequests?.consume(purchase.token, object : RequestListener<Any> {
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?)
}
}

View file

@ -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);
}
}
}
}

View file

@ -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
}
}

View file

@ -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();
}
}

View file

@ -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()
}
}

View file

@ -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;
}
}
}

View file

@ -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
}
}
}

View file

@ -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<UserAuthResponse> {
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<String, Object> 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<LoginResult>() {
@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(); // Dont 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();
}
}

View file

@ -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<UserAuthResponse> {
@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<String, Any>()
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<LoginResult> {
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() // Dont 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<String> {
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<View>(newGameButton, View.ALPHA, 0.toFloat())
val showLoginAlphaAnimation = ObjectAnimator.ofFloat<View>(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<View>(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, 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<View>(newGameButton, View.ALPHA, 1.toFloat()).setDuration(700)
val showLoginAlphaAnimation = ObjectAnimator.ofFloat<View>(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<View>(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<View>(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
}
}
}

View file

@ -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<BaseMainFragment>? = 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<DrawerLayout>(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<BaseMainFragment>(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<View>(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<View>(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<View>(R.id.hpBar) as ValueBar
hpBarView.setLightBackground(true)
hpBarView.setIcon(HabiticaIconsHelper.imageOfHeartLightBg())
val dialogAvatarView = customView.findViewById<View>(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<String, Any>()
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<String>, 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<String, Any>()
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<String, Any>()
updateData[path] = true
userRepository.updateUser(user, updateData)
.subscribe(Consumer { }, RxErrorHandler.handleEmptyError())
this.overlayLayout.removeView(this.activeTutorialView)
this.removeActiveTutorialView()
val additionalData = HashMap<String, Any>()
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<TaskScoringResult> { this.displayTaskScoringResponse(it) }, RxErrorHandler.handleEmptyError())
}
Task.TYPE_TODO -> {
todoCheckUseCase.observable(TodoCheckUseCase.RequestValues(user, event.Task, !event.Task.completed))
.subscribe(Consumer<TaskScoringResult> { 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<TaskScoringResult> { 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<View>(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<View>(R.id.you_earned_message) as TextView
youEarnedTexView.text = youEarnedMessage
val nextUnlockTextView = view.findViewById<View>(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
}
}

View file

@ -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)));
}
}
}

View file

@ -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")))
}
}
}

View file

@ -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<PartyInviteFragment> 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<String, Object> inviteData = new HashMap<>();
List<String> invites = new ArrayList<>();
invites.add(userIdToInvite);
inviteData.put("uuids", invites);
this.socialRepository.inviteToGroup(user.getParty().getId(), inviteData)
.subscribe(aVoid -> {}, RxErrorHandler.handleEmptyError());
}
}

View file

@ -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<PartyInviteFragment> = 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<User> { 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<String, Any>()
val invites = ArrayList<String>()
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"
}
}

View file

@ -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 {

View file

@ -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<String, Object> 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<Task> 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<String, Object> 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;
}
}

View file

@ -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<String, Any>()
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<User> { 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<String, Any>()
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
}
}
}

View file

@ -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?) {

View file

@ -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<SkillTasksRecyclerViewFragment> 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();
}
}

View file

@ -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<SkillTasksRecyclerViewFragment>()
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()
}
}

View file

@ -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<Task> callback;
private Task newTask;
public AddItemViewHolder(View itemView, PublishSubject<Task> callback) {
super(itemView, false);
AddItemViewHolder(View itemView, PublishSubject<Task> 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());
}
}

View file

@ -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<VH : BaseTaskViewHolder>(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)

View file

@ -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() {

View file

@ -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;
}
}

View file

@ -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
}
}

View file

@ -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
}

View file

@ -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) {

View file

@ -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()
}
}

View file

@ -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
}

View file

@ -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<String>()
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

View file

@ -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);
}
}

View file

@ -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)
}
}

View file

@ -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()
}

View file

@ -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);
}
}

View file

@ -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"
}
}

View file

@ -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);
}
}

View file

@ -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)
}
}

View file

@ -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)

View file

@ -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()

View file

@ -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" -> {

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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,

View file

@ -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<ChatMessage> { 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)
}
}

View file

@ -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)
}
}

View file

@ -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);
}
}

View file

@ -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<Group> { 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)
}
}

View file

@ -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
}

View file

@ -61,7 +61,9 @@ class InboxMessageListFragment : BaseMainFragment(), SwipeRefreshLayout.OnRefres
recyclerView.adapter = chatAdapter
recyclerView.itemAnimator = SafeDefaultItemAnimator()
chatAdapter.notNull {
compositeSubscription.add(it.getUserLabelClickFlowable().subscribe(Consumer<String> { FullProfileActivity.open(context, it) }, RxErrorHandler.handleEmptyError()))
compositeSubscription.add(it.getUserLabelClickFlowable().subscribe(Consumer<String> {
context.notNull { context -> FullProfileActivity.open(context, it) }
}, RxErrorHandler.handleEmptyError()))
compositeSubscription.add(it.getDeleteMessageFlowable().subscribe(Consumer<ChatMessage> { this.showDeleteConfirmationDialog(it) }, RxErrorHandler.handleEmptyError()))
compositeSubscription.add(it.getFlagMessageClickFlowable().subscribe(Consumer<ChatMessage> { this.showFlagConfirmationDialog(it) }, RxErrorHandler.handleEmptyError()))
compositeSubscription.add(it.getCopyMessageAsTodoFlowable().subscribe(Consumer<ChatMessage> { 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 -> }

View file

@ -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() {

View file

@ -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);
}
}

View file

@ -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<Challenge> {
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)
}
}

View file

@ -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<String, Object> 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<HashMap<String, String>> invites = new ArrayList<>();
for (String email : emails) {
HashMap<String, String> 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<String> 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);
}
}

View file

@ -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<String, Any>()
inviteData["inviter"] = user?.profile?.name ?: ""
if (data!!.getBooleanExtra(PartyInviteActivity.IS_EMAIL_KEY, false)) {
val emails = data.getStringArrayExtra(PartyInviteActivity.EMAILS_KEY)
val invites = ArrayList<HashMap<String, String>>()
for (email in emails) {
val invite = HashMap<String, String>()
invite["name"] = ""
invite["email"] = email
invites.add(invite)
}
inviteData["emails"] = invites
} else {
val userIDs = data.getStringArrayExtra(PartyInviteActivity.USER_IDS_KEY)
val invites = ArrayList<String>()
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)
}
}

View file

@ -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) {

View file

@ -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

View file

@ -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 <V : View> View.bindView(@IdRes id: Int)
: ReadOnlyProperty<View, V> = required(id, viewFinder)

View file

@ -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()

View file

@ -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.

View file

@ -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)

View file

@ -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 {

View file

@ -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)

View file

@ -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)
}
}

View file

@ -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)

View file

@ -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);
}
}

View file

@ -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<SpannableStringBuilder, HabiticaSnackbar.SnackbarDisplayType> pair = NotifyUserUseCase.Companion.getNotificationAndAddStatsToUserAsText(context, data.getExperienceDelta(), data.getHealthDelta(), data.getGoldDelta(), data.getManaDelta());

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);

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