Implemented stackwidget style for checkmark widgets

pull/346/head
Victor Yu 8 years ago
parent b8f7d4fad2
commit 82972d6e47

@ -103,6 +103,20 @@
android:name="android.appwidget.provider"
android:resource="@xml/widget_checkmark_info"/>
</receiver>
<receiver
android:name=".widgets.StackWidgetProvider"
android:label="@string/stacked_checkmark">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_checkmark_stack_info"/>
</receiver>
<service android:name=".widgets.StackWidgetService"
android:permission="android.permission.BIND_REMOTEVIEWS"
android:exported="false" />
<receiver
android:name=".widgets.HistoryWidgetProvider"
android:label="@string/history">

@ -156,7 +156,7 @@ public abstract class BaseWidget
}
@NonNull
private RemoteViews getRemoteViews(int width, int height)
protected RemoteViews getRemoteViews(int width, int height)
{
View view = buildView();
measureView(view, width, height);

@ -19,15 +19,21 @@
package org.isoron.uhabits.widgets;
import android.appwidget.*;
import android.content.*;
import android.os.*;
import android.support.annotation.*;
import android.widget.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.core.models.*;
import org.isoron.uhabits.core.preferences.*;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.os.Bundle;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.widget.RemoteViews;
import org.isoron.uhabits.HabitsApplication;
import org.isoron.uhabits.R;
import org.isoron.uhabits.core.models.Habit;
import org.isoron.uhabits.core.models.HabitList;
import org.isoron.uhabits.core.models.HabitNotFoundException;
import org.isoron.uhabits.core.preferences.WidgetPreferences;
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT;
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH;
@ -77,7 +83,7 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
if (context == null) throw new RuntimeException("context is null");
if (manager == null) throw new RuntimeException("manager is null");
if (options == null) throw new RuntimeException("options is null");
context.setTheme(R.style.TransparentWidgetTheme);
context.setTheme(R.style.OpaqueWidgetTheme);
updateDependencies(context);
@ -123,7 +129,7 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider
if (context == null) throw new RuntimeException("context is null");
if (manager == null) throw new RuntimeException("manager is null");
if (widgetIds == null) throw new RuntimeException("widgetIds is null");
context.setTheme(R.style.TransparentWidgetTheme);
context.setTheme(R.style.OpaqueWidgetTheme);
updateDependencies(context);

@ -0,0 +1,57 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.widgets
import android.appwidget.AppWidgetManager
import android.content.Context
import android.content.Intent
import android.view.View
import android.widget.RemoteViews
import org.isoron.uhabits.R
import org.isoron.uhabits.widgets.views.CheckmarkWidgetView
class StackGroupWidget(
context: Context,
widgetId: Int
) : BaseWidget(context, widgetId) {
override fun getOnClickPendingIntent(context: Context) = null
override fun refreshData(v: View) {
// unused
}
override fun getRemoteViews(width: Int, height: Int): RemoteViews {
val remoteViews = RemoteViews(context.packageName, R.layout.widget_stackview)
val serviceIntent = Intent(context, StackWidgetService::class.java)
serviceIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id)
// TODO this commented line is taken from official examples, but adding it seems to make the widget not update immediately when completing the habit
// serviceIntent.data = Uri.parse(serviceIntent.toUri(Intent.URI_INTENT_SCHEME)) // embed extras so they don't get ignored
remoteViews.setRemoteAdapter(R.id.stackWidgetView, serviceIntent)
AppWidgetManager.getInstance(context).notifyAppWidgetViewDataChanged(id, R.id.stackWidgetView)
// TODO what should the empty view look like?
remoteViews.setEmptyView(R.id.stackWidgetView, R.id.stackWidgetEmptyView)
return remoteViews
}
override fun buildView() = null // unused
override fun getDefaultHeight() = 0 // unused
override fun getDefaultWidth() = 0 // unused
}

@ -0,0 +1,17 @@
package org.isoron.uhabits.widgets
import android.appwidget.AppWidgetManager
import android.content.Context
import android.content.Intent
import android.util.Log
import org.isoron.uhabits.R
/**
* Created by victoryu on 9/30/17.
*/
class StackWidgetProvider : BaseWidgetProvider() {
override fun getWidgetFromId(context: Context, id: Int): StackGroupWidget {
return StackGroupWidget(context, id)
}
}

@ -0,0 +1,110 @@
package org.isoron.uhabits.widgets;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.widget.RemoteViews;
import android.widget.RemoteViewsService;
import org.isoron.uhabits.HabitsApplication;
import org.isoron.uhabits.core.models.Habit;
import java.util.ArrayList;
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT;
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH;
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT;
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH;
import static org.isoron.androidbase.utils.InterfaceUtils.dpToPixels;
/**
* Created by victoryu on 9/30/17.
*/
public class StackWidgetService extends RemoteViewsService {
@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
return new StackRemoteViewsFactory(this.getApplicationContext(), intent);
}
}
class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
private Context mContext;
private int mAppWidgetId;
private ArrayList<Habit> mHabitList;
public StackRemoteViewsFactory(Context context, Intent intent) {
mContext = context;
mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
}
public void onCreate() {
}
public void onDestroy() {
mHabitList.clear();
}
public int getCount() {
return mHabitList.size();
}
@NonNull
public WidgetDimensions getDimensionsFromOptions(@NonNull Context ctx,
@NonNull Bundle options) {
int maxWidth =
(int) dpToPixels(ctx, options.getInt(OPTION_APPWIDGET_MAX_WIDTH));
int maxHeight =
(int) dpToPixels(ctx, options.getInt(OPTION_APPWIDGET_MAX_HEIGHT));
int minWidth =
(int) dpToPixels(ctx, options.getInt(OPTION_APPWIDGET_MIN_WIDTH));
int minHeight =
(int) dpToPixels(ctx, options.getInt(OPTION_APPWIDGET_MIN_HEIGHT));
return new WidgetDimensions(minWidth, maxHeight, maxWidth, minHeight);
}
public RemoteViews getViewAt(int position) {
RemoteViews rv = null;
if (position < getCount()) {
Habit habit = mHabitList.get(position);
CheckmarkWidget checkmarkWidget = new CheckmarkWidget(mContext, mAppWidgetId, habit);
Bundle options = AppWidgetManager.getInstance(mContext).getAppWidgetOptions(mAppWidgetId);
checkmarkWidget.setDimensions(getDimensionsFromOptions(mContext, options));
RemoteViews landscape = checkmarkWidget.getLandscapeRemoteViews();
RemoteViews portrait = checkmarkWidget.getPortraitRemoteViews();
rv = new RemoteViews(landscape, portrait);
}
return rv;
}
public RemoteViews getLoadingView() {
return null;
}
public int getViewTypeCount() {
return 1;
}
public long getItemId(int position) {
return position;
}
public boolean hasStableIds() {
return false;
}
public void onDataSetChanged() {
mHabitList = new ArrayList<>();
HabitsApplication app = (HabitsApplication) mContext.getApplicationContext();
for (Habit h : app.getComponent().getHabitList()) {
mHabitList.add(h);
}
}
}

@ -61,6 +61,7 @@ class WidgetUpdater
fun updateWidgets() {
taskRunner.execute {
updateWidgets(CheckmarkWidgetProvider::class.java)
updateWidgets(StackWidgetProvider::class.java)
updateWidgets(HistoryWidgetProvider::class.java)
updateWidgets(ScoreWidgetProvider::class.java)
updateWidgets(StreakWidgetProvider::class.java)

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<StackView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/stackWidgetView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:loopViews="true" />
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/stackWidgetEmptyView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/stacked_checkmark"
android:gravity="center"
android:textColor="#ffffff"
android:textStyle="bold"
android:textSize="16sp" />
</FrameLayout>

@ -124,6 +124,7 @@
<string name="version_n">Version %s</string>
<string name="frequency">Frequency</string>
<string name="checkmark">Checkmark</string>
<string name="stacked_checkmark">Stacked Checkmark</string>
<string name="strength">Strength</string>
<string name="best_streaks">Best streaks</string>

@ -154,6 +154,10 @@
<item name="widgetBackgroundAlpha">0.25</item>
</style>
<style name="OpaqueWidgetTheme" parent="TransparentWidgetTheme">
<item name="widgetBackgroundAlpha">1</item>
</style>
<style name="day_of_week_label_condensed"/>
<style name="CardList">

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
~
~ This file is part of Loop Habit Tracker.
~
~ Loop Habit Tracker is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by the
~ Free Software Foundation, either version 3 of the License, or (at your
~ option) any later version.
~
~ Loop Habit Tracker is distributed in the hope that it will be useful, but
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
~ more details.
~
~ You should have received a copy of the GNU General Public License along
~ with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<!-- TODO need to change preview image here -->
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minHeight="40dp"
android:minWidth="40dp"
android:initialLayout="@layout/widget_wrapper"
android:previewImage="@drawable/widget_preview_checkmark"
android:resizeMode="none"
android:updatePeriodMillis="3600000"
android:configure="org.isoron.uhabits.widgets.HabitPickerDialog"
android:widgetCategory="home_screen">
</appwidget-provider>
Loading…
Cancel
Save