Start working on habit button widget

This commit is contained in:
Phillip Thelen 2016-09-22 16:02:01 +02:00
parent 3ed0be06d0
commit 44b6de04be
17 changed files with 372 additions and 10 deletions

View file

@ -152,6 +152,11 @@
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
</intent-filter>
</activity>
<activity android:name=".ui.activities.HabitButtonWidgetActivity">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
</intent-filter>
</activity>
<service
android:name=".helpers.notifications.HabiticaFirebaseMessagingService">
@ -185,7 +190,7 @@
android:resource="@xml/avatar_widget_info" />
</receiver>
<receiver android:name=".widget.AddTaskWidgetProvider"
android:label="Habitica Add Task">
android:label="@string/widget_add_task">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
@ -193,17 +198,26 @@
android:resource="@xml/add_task_widget_info" />
</receiver>
<receiver android:name=".widget.DailiesWidgetProvider"
android:label="Habitica Dailies">
android:label="@string/widget_dailies">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/dailies_task_widget_info" />
</receiver>
<receiver android:name=".widget.HabitButtonWidgetProvider"
android:label="@string/widget_habit_button">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/habit_button_widget_info" />
</receiver>
<service android:name=".widget.AvatarStatsWidgetService"/>
<service
android:name=".widget.DailiesWidgetService"
android:permission="android.permission.BIND_REMOTEVIEWS" />
<service android:name=".widget.HabitButtonWidgetService"/>
</application>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

View file

@ -10,7 +10,7 @@
<!-- Here is the corner radius -->
<corners
android:radius="2dp" >
android:radius="@dimen/widget_rounding" >
</corners>
</shape>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recyclerView"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>

View file

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/brand_100"
android:clipChildren="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<FrameLayout
android:id="@+id/btnPlusWrapper"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1">
<ImageView
android:id="@+id/btnPlus"
android:src="@drawable/plus"
android:scaleType="center"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/selection_highlight"
/>
</FrameLayout>
<FrameLayout
android:id="@+id/btnMinusWrapper"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1">
<ImageView
android:id="@+id/btnMinus"
android:src="@drawable/minus"
android:scaleType="center"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/selection_highlight" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/selection_highlight"
/>
</FrameLayout>
</LinearLayout>
<TextView
android:id="@+id/habit_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:layout_marginTop="6dp"
android:layout_marginBottom="6dp" />
</LinearLayout>

View file

@ -83,4 +83,5 @@
<dimen name="header_bar_spacing">7dp</dimen>
<dimen name="habit_circle_background_size">40dp</dimen>
<dimen name="bar_size_slim">13dp</dimen>
<dimen name="widget_rounding">5dp</dimen>
</resources>

View file

@ -404,5 +404,8 @@ To start, which parts of your life do you want to improve?</string>
<string name="add_todo">Add To-Do</string>
<string name="add_reward">Add Reward</string>
<string name="all_dailies_completed">You completed all your dailies. Well done!</string>
<string name="widget_habit_button">Habitica Do Habit</string>
<string name="widget_dailies">Habitica Dailies</string>
<string name="widget_add_task">Habitica Add Task</string>
</resources>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/widget_habit_button"
android:minHeight="40dp"
android:minWidth="180dp"
android:updatePeriodMillis="1800000"
android:resizeMode="horizontal|vertical"
android:configure="com.habitrpg.android.habitica.ui.activities.HabitButtonWidgetActivity">
</appwidget-provider>

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,5 @@
package com.habitrpg.android.habitica.ui.activities;
public interface TaskClickActivity {
void taskSelected(String taskId);
}

View file

@ -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<SkillTas
private static final int TYPE_CELL = 1;
String taskType;
SkillTasksActivity activity;
TaskClickActivity activity;
private ObservableArrayList<Task> observableContent;
private RecyclerView.Adapter<ViewHolder> 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<SkillTas
resources = itemView.getResources();
}
public void bindHolder(Task habitItem, int position) {
void bindHolder(Task habitItem, int position) {
task = habitItem;
if (habitItem.notes == null || habitItem.notes.length() == 0) {
notesTextView.setHeight(0);

View file

@ -10,7 +10,7 @@ import butterknife.BindView;
public class DailyViewHolder extends ChecklistedViewHolder {
public final int dailyResetOffset;
private final int dailyResetOffset;
@BindView(R.id.streakTextView)
TextView streakTextView;

View file

@ -87,9 +87,8 @@ public class DailiesListProvider implements RemoteViewsService.RemoteViewsFactor
@Override
public RemoteViews getLoadingView() {
final RemoteViews remoteView = new RemoteViews(
return new RemoteViews(
context.getPackageName(), R.layout.widget_dailies_list_row);
return remoteView;
}
@Override

View file

@ -0,0 +1,48 @@
package com.habitrpg.android.habitica.widget;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.widget.RemoteViews;
import com.habitrpg.android.habitica.R;
public class HabitButtonWidgetProvider extends BaseWidgetProvider {
public static String TASK_ID = "TASK_ID";
@Override
public int layoutResourceId() {
return R.layout.widget_habit_button;
}
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// Get all ids
ComponentName thisWidget = new ComponentName(context,
HabitButtonWidgetProvider.class);
int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
if (Build.VERSION.SDK_INT >= 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;
}
}

View file

@ -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<String, Integer> 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<Task> userTransactionListener = new TransactionListener<Task>() {
@Override
public void onResultReceived(Task task) {
updateData(task);
}
@Override
public boolean onReady(BaseTransaction<Task> task) {
return true;
}
@Override
public boolean hasResult(BaseTransaction<Task> 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, "");
}
}