diff --git a/Habitica/AndroidManifest.xml b/Habitica/AndroidManifest.xml index c195eccc7..effee908c 100644 --- a/Habitica/AndroidManifest.xml +++ b/Habitica/AndroidManifest.xml @@ -152,6 +152,11 @@ + + + + + @@ -185,7 +190,7 @@ android:resource="@xml/avatar_widget_info" /> + android:label="@string/widget_add_task"> @@ -193,17 +198,26 @@ android:resource="@xml/add_task_widget_info" /> + android:label="@string/widget_dailies"> + + + + + + + diff --git a/Habitica/res/drawable/widget_background.xml b/Habitica/res/drawable/widget_background.xml index ec645c0a6..d20a9e9e3 100644 --- a/Habitica/res/drawable/widget_background.xml +++ b/Habitica/res/drawable/widget_background.xml @@ -10,7 +10,7 @@ + android:radius="@dimen/widget_rounding" > \ No newline at end of file diff --git a/Habitica/res/layout/widget_configure_habit_button.xml b/Habitica/res/layout/widget_configure_habit_button.xml new file mode 100644 index 000000000..52a5f6d8e --- /dev/null +++ b/Habitica/res/layout/widget_configure_habit_button.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/Habitica/res/layout/widget_habit_button.xml b/Habitica/res/layout/widget_habit_button.xml new file mode 100644 index 000000000..9cfedc0f5 --- /dev/null +++ b/Habitica/res/layout/widget_habit_button.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Habitica/res/values/dimens.xml b/Habitica/res/values/dimens.xml index fa1c3e7d4..ab1a042a3 100644 --- a/Habitica/res/values/dimens.xml +++ b/Habitica/res/values/dimens.xml @@ -83,4 +83,5 @@ 7dp 40dp 13dp + 5dp \ No newline at end of file diff --git a/Habitica/res/values/strings.xml b/Habitica/res/values/strings.xml index 31f0a8177..406b3d6b5 100644 --- a/Habitica/res/values/strings.xml +++ b/Habitica/res/values/strings.xml @@ -404,5 +404,8 @@ To start, which parts of your life do you want to improve? Add To-Do Add Reward You completed all your dailies. Well done! + Habitica Do Habit + Habitica Dailies + Habitica Add Task diff --git a/Habitica/res/xml/habit_button_widget_info.xml b/Habitica/res/xml/habit_button_widget_info.xml new file mode 100644 index 000000000..2b4a832e2 --- /dev/null +++ b/Habitica/res/xml/habit_button_widget_info.xml @@ -0,0 +1,10 @@ + + + + diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/components/AppComponent.java b/Habitica/src/main/java/com/habitrpg/android/habitica/components/AppComponent.java index 2802a5da2..69feea14e 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/components/AppComponent.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/components/AppComponent.java @@ -9,6 +9,7 @@ import com.habitrpg.android.habitica.ui.activities.AboutActivity; import com.habitrpg.android.habitica.ui.activities.ClassSelectionActivity; import com.habitrpg.android.habitica.ui.activities.FullProfileActivity; import com.habitrpg.android.habitica.ui.activities.GroupFormActivity; +import com.habitrpg.android.habitica.ui.activities.HabitButtonWidgetActivity; import com.habitrpg.android.habitica.ui.activities.IntroActivity; import com.habitrpg.android.habitica.ui.activities.LoginActivity; import com.habitrpg.android.habitica.ui.activities.MainActivity; @@ -55,6 +56,7 @@ import com.habitrpg.android.habitica.ui.fragments.tasks.TaskRecyclerViewFragment import com.habitrpg.android.habitica.ui.fragments.tasks.TasksFragment; import com.habitrpg.android.habitica.widget.AvatarStatsWidgetService; import com.habitrpg.android.habitica.widget.DailiesWidgetProvider; +import com.habitrpg.android.habitica.widget.HabitButtonWidgetService; import javax.inject.Singleton; @@ -168,4 +170,8 @@ public interface AppComponent { void inject(FullProfileActivity fullProfileActivity); void inject(DailiesWidgetProvider dailiesWidgetProvider); + + void inject(HabitButtonWidgetService habitButtonWidgetService); + + void inject(HabitButtonWidgetActivity habitButtonWidgetActivity); } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/modules/AppModule.java b/Habitica/src/main/java/com/habitrpg/android/habitica/modules/AppModule.java index d4cdf28f3..ce5bda5e2 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/modules/AppModule.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/modules/AppModule.java @@ -7,6 +7,7 @@ import com.habitrpg.android.habitica.helpers.TagsHelper; import android.content.Context; import android.content.SharedPreferences; +import android.content.res.Resources; import android.support.v7.preference.PreferenceManager; import javax.inject.Named; @@ -47,4 +48,9 @@ public class AppModule { public TagsHelper providesTagsHelper() { return new TagsHelper(); } + + @Provides + public Resources providesResources(Context context) { + return context.getResources(); + } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/HabitButtonWidgetActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/HabitButtonWidgetActivity.java new file mode 100644 index 000000000..aa9e15aa2 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/HabitButtonWidgetActivity.java @@ -0,0 +1,89 @@ +package com.habitrpg.android.habitica.ui.activities; + +import android.app.Activity; +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.ui.adapter.SkillTasksRecyclerViewAdapter; +import com.habitrpg.android.habitica.widget.AddTaskWidgetProvider; +import com.habitrpg.android.habitica.widget.HabitButtonWidgetProvider; +import com.magicmicky.habitrpgwrapper.lib.models.tasks.Task; + +import butterknife.BindView; +import butterknife.ButterKnife; + +public class HabitButtonWidgetActivity extends BaseActivity implements TaskClickActivity { + + @BindView(R.id.recyclerView) + RecyclerView recyclerView; + private int widgetId; + + @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); + 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(); + } + + LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager(); + + if (layoutManager == null) { + layoutManager = new LinearLayoutManager(this); + + recyclerView.setLayoutManager(layoutManager); + } + + recyclerView.setAdapter(new SkillTasksRecyclerViewAdapter(Task.TYPE_HABIT, this)); + } + + @Override + public void taskSelected(String taskId) { + finishWithSelection(taskId); + } + + private void finishWithSelection(String selectedTaskId) { + storeSelectedTaskId(selectedTaskId); + + Intent resultValue = new Intent(); + resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId); + setResult(RESULT_OK, resultValue); + finish(); + + Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE, null, this, HabitButtonWidgetProvider.class); + intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {widgetId}); + sendBroadcast(intent); + } + + private void storeSelectedTaskId(String selectedTaskId) { + SharedPreferences.Editor preferences = PreferenceManager.getDefaultSharedPreferences(this).edit(); + preferences.putString("habit_button_widget_" + widgetId, selectedTaskId); + preferences.apply(); + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillTasksActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillTasksActivity.java index 7b7d14cca..0ce5604e2 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillTasksActivity.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/SkillTasksActivity.java @@ -20,7 +20,7 @@ import java.util.Map; import butterknife.BindView; -public class SkillTasksActivity extends BaseActivity { +public class SkillTasksActivity extends BaseActivity implements TaskClickActivity { @BindView(R.id.viewpager) public ViewPager viewPager; @@ -100,3 +100,4 @@ public class SkillTasksActivity extends BaseActivity { finish(); } } + diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskClickActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskClickActivity.java new file mode 100644 index 000000000..59fdd2f44 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskClickActivity.java @@ -0,0 +1,5 @@ +package com.habitrpg.android.habitica.ui.activities; + +public interface TaskClickActivity { + void taskSelected(String taskId); +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/SkillTasksRecyclerViewAdapter.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/SkillTasksRecyclerViewAdapter.java index e0b028346..3ad69abf5 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/SkillTasksRecyclerViewAdapter.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/SkillTasksRecyclerViewAdapter.java @@ -3,6 +3,7 @@ package com.habitrpg.android.habitica.ui.adapter; import com.habitrpg.android.habitica.R; import com.habitrpg.android.habitica.databinding.SkillTaskItemCardBinding; import com.habitrpg.android.habitica.ui.activities.SkillTasksActivity; +import com.habitrpg.android.habitica.ui.activities.TaskClickActivity; import com.magicmicky.habitrpgwrapper.lib.models.tasks.Task; import com.raizlabs.android.dbflow.sql.builder.Condition; import com.raizlabs.android.dbflow.sql.language.OrderBy; @@ -28,11 +29,11 @@ public class SkillTasksRecyclerViewAdapter extends RecyclerView.Adapter observableContent; private RecyclerView.Adapter parentAdapter; - public SkillTasksRecyclerViewAdapter(String taskType, SkillTasksActivity activity) { + public SkillTasksRecyclerViewAdapter(String taskType, TaskClickActivity activity) { this.setHasStableIds(true); this.taskType = taskType; this.activity = activity; @@ -129,7 +130,7 @@ public class SkillTasksRecyclerViewAdapter extends RecyclerView.Adapter= 16) { + for (int widgetId : allWidgetIds) { + Bundle options = appWidgetManager.getAppWidgetOptions(widgetId); + appWidgetManager.partiallyUpdateAppWidget(widgetId, + sizeRemoteViews(context, options, widgetId)); + } + } + + // Build the intent to call the service + Intent intent = new Intent(context.getApplicationContext(), + HabitButtonWidgetService.class); + intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, allWidgetIds); + + context.startService(intent); + } + + @Override + public RemoteViews configureRemoteViews(RemoteViews remoteViews, int widgetId, int columns, int rows) { + return remoteViews; + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetService.java b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetService.java new file mode 100644 index 000000000..abed585ed --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/HabitButtonWidgetService.java @@ -0,0 +1,117 @@ +package com.habitrpg.android.habitica.widget; + +import android.app.Service; +import android.appwidget.AppWidgetManager; +import android.content.ComponentName; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.res.Resources; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.Drawable; +import android.os.IBinder; +import android.preference.PreferenceManager; +import android.view.View; +import android.widget.RemoteViews; + +import com.habitrpg.android.habitica.HabiticaApplication; +import com.habitrpg.android.habitica.HostConfig; +import com.habitrpg.android.habitica.R; +import com.magicmicky.habitrpgwrapper.lib.models.tasks.Task; +import com.raizlabs.android.dbflow.runtime.transaction.BaseTransaction; +import com.raizlabs.android.dbflow.runtime.transaction.TransactionListener; +import com.raizlabs.android.dbflow.sql.builder.Condition; +import com.raizlabs.android.dbflow.sql.language.Select; + +import java.util.HashMap; +import java.util.Map; + +import javax.inject.Inject; + +public class HabitButtonWidgetService extends Service { + @Inject + public HostConfig hostConfig; + @Inject + public SharedPreferences sharedPreferences; + @Inject + public Resources resources; + private AppWidgetManager appWidgetManager; + + private Map taskMapping; + + @Override + public int onStartCommand(final Intent intent, int flags, int startId) { + HabiticaApplication application = (HabiticaApplication) getApplication(); + application.getComponent().inject(this); + this.appWidgetManager = AppWidgetManager.getInstance(this); + + makeTaskMapping(); + + for (String taskid : this.taskMapping.keySet()) { + new Select().from(Task.class).where(Condition.column("id").eq(taskid)).async().querySingle(userTransactionListener); + } + + stopSelf(); + + return START_STICKY; + } + + private TransactionListener userTransactionListener = new TransactionListener() { + @Override + public void onResultReceived(Task task) { + updateData(task); + } + + @Override + public boolean onReady(BaseTransaction task) { + return true; + } + + @Override + public boolean hasResult(BaseTransaction baseTransaction, Task task) { + return true; + } + }; + + private void updateData(Task task) { + RemoteViews remoteViews = new RemoteViews(this.getPackageName(), R.layout.widget_habit_button); + remoteViews.setTextViewText(R.id.habit_title, task.text); + + if (!task.getUp()) { + remoteViews.setViewVisibility(R.id.btnPlusWrapper, View.GONE); + } else { + remoteViews.setViewVisibility(R.id.btnPlusWrapper, View.VISIBLE); + + remoteViews.setInt(R.id.btnPlus, "setBackgroundColor", resources.getColor(task.getLightTaskColor())); + } + if (!task.getDown()) { + remoteViews.setViewVisibility(R.id.btnMinusWrapper, View.GONE); + } else { + remoteViews.setViewVisibility(R.id.btnMinusWrapper, View.VISIBLE); + remoteViews.setInt(R.id.btnMinus, "setBackgroundColor", resources.getColor(task.getMediumTaskColor())); + } + + appWidgetManager.partiallyUpdateAppWidget(taskMapping.get(task.getId()), remoteViews); + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + private void makeTaskMapping() { + ComponentName thisWidget = new ComponentName(this, HabitButtonWidgetProvider.class); + int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget); + this.taskMapping = new HashMap<>(); + for (int widgetId : allWidgetIds) { + String taskId = getTaskId(widgetId); + if (!taskId.equals("")) { + this.taskMapping.put(taskId, widgetId); + } + } + } + + private String getTaskId(int widgetId) { + return sharedPreferences.getString("habit_button_widget_"+widgetId, ""); + } +}